如何在 iOS 上获取音频音量级别和音量更改通知?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3651252/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to get audio volume level, and volume changed notifications on iOS?
提问by MacLemon
I'm writing a very simple application that plays a sound when pressing a button. Since that button does not make a lot of sense when the device is set to silence I want to disable it when the device's audio volume is zero. (And subsequently reenable it when the volume is cranked up again.)
我正在编写一个非常简单的应用程序,它在按下按钮时播放声音。由于该按钮在设备设置为静音时没有多大意义,因此我想在设备的音量为零时禁用它。(然后在音量再次调高时重新启用它。)
I am seeking a working (and AppStore safe) way to detect the current volume settingand get a notification/callback when the volume level changes. I do not want to alter the volumesetting.
我正在寻找一种有效的(和 AppStore 安全的)方法来检测当前的音量设置并在音量级别发生变化时获得通知/回调。我不想改变音量设置。
All this is implemented in my ViewController
where said button is used. I've tested this with an iPhone 4 running iOS 4.0.1 and 4.0.2 as well as an iPhone 3G running 4.0.1. Built with iOS SDK 4.0.2 with llvm 1.5. (Using gcc or llvm-gcc doesn't improve anything.) There are no issues during build implementing either way, neither errors nor warnings. Static analyzer is happy as well.
所有这些都是在我ViewController
使用所述按钮的地方实现的。我已经用运行 iOS 4.0.1 和 4.0.2 的 iPhone 4 以及运行 4.0.1 的 iPhone 3G 对此进行了测试。使用 iOS SDK 4.0.2 和 llvm 1.5 构建。(使用 gcc 或 llvm-gcc 不会改进任何东西。)在构建实施过程中没有任何问题,既没有错误也没有警告。静态分析器也很高兴。
Here is what I've tried so far, all without any success.
这是我迄今为止尝试过的方法,但都没有成功。
Following Apple's audio services documentation I should register an AudioSessionAddPropertyListener
for kAudioSessionProperty_CurrentHardwareOutputVolume
which should work like this:
继苹果公司的音频服务的文件,我应该在注册AudioSessionAddPropertyListener
的kAudioSessionProperty_CurrentHardwareOutputVolume
应该像这样工作:
// Registering for Volume Change notifications
AudioSessionInitialize(NULL, NULL, NULL, NULL);
returnvalue = AudioSessionAddPropertyListener (
kAudioSessionProperty_CurrentHardwareOutputVolume ,
audioVolumeChangeListenerCallback,
self
);
returnvalue
is 0
, which means that registering the callback worked.
returnvalue
is 0
,这意味着注册回调有效。
Sadly, I never get a callback to my function audioVolumeChangeListenerCallback
when I press the volume buttons on my device, the headset clicker or flip the ringer-silent switch.
遗憾的是,audioVolumeChangeListenerCallback
当我按下设备上的音量按钮、耳机答题器或拨动铃声静音开关时,我从来没有收到对我的功能的回调。
When using the exact same code for registering for kAudioSessionProperty_AudioRouteChange
(which is used as an analogous sample project in WWDC videos, Developer documentation and on numerous sites on the interwebs) I actually doget a callback when changing the audio route (by plugging in/out a headset or docking the device).
当使用完全相同的代码进行注册时kAudioSessionProperty_AudioRouteChange
(在 WWDC 视频、开发人员文档和互联网上的众多站点中用作类似的示例项目)时,我实际上确实在更改音频路由时收到了回调(通过插入/输出一个耳机或对接设备)。
A user named Dougopened a thread titled iPhone volume changed event for volume already maxwhere he claimed that he is sucessfully using this way (unless the volume would not actually change because it is already set to maximum). Still, it doesn't work for me.
一位名叫Doug的用户打开了一个名为iPhone 音量已更改事件的线程,他声称他成功地使用了这种方式(除非音量实际上不会改变,因为它已经设置为最大)。尽管如此,它对我不起作用。
Another way I have tried is to register at NSNotificationCenter
like this.
我尝试过的另一种方法是NSNotificationCenter
像这样注册。
// sharedAVSystemController
AudioSessionInitialize(NULL, NULL, NULL, NULL);
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter addObserver:self
selector:@selector(volumeChanged:)
name:@"AVSystemController_SystemVolumeDidChangeNotification"
object:nil];
This should notify my method volumeChanged
of any SystemVolume
changes but it doesn't actually do so.
这应该volumeChanged
将任何SystemVolume
更改通知我的方法,但实际上并没有这样做。
Since common belief tells me that if one is working too hard to achieve something with Cocoa one is doing something fundamentally wrong I'm expecting to miss something here. It's hard to believe that there is no simple wayto getthe current volume level, yet I haven't been able to find one using Apple's documentation, sample code, Google, Apple Developer Forums or by watching WWDC 2010 videos.
因为普遍的信念告诉我,如果一个人太努力地用 Cocoa 来实现某事,那他就是在做一些根本错误的事情,我希望在这里错过一些东西。很难相信,有没有简单的方法来获得当前的音量,但使用苹果的文档,示例代码,谷歌,苹果开发者论坛我一直没能找到一个或观看WWDC 2010条的视频。
回答by Sandy
Any chance you did your signature wrong for the volumeChanged: method? This worked for me, dumped in my appdelegate:
您是否有可能为 volumeChanged: 方法签名错误?这对我有用,倾倒在我的 appdelegate 中:
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(volumeChanged:)
name:@"AVSystemController_SystemVolumeDidChangeNotification"
object:nil];
}
- (void)volumeChanged:(NSNotification *)notification
{
float volume =
[[[notification userInfo]
objectForKey:@"AVSystemController_AudioVolumeNotificationParameter"]
floatValue];
// Do stuff with volume
}
My volumeChanged: method gets hit every time the button is pressed, even if the volume does not change as a result (because it's already at max/min).
每次按下按钮时,我的 volumeChanged: 方法都会被点击,即使音量没有改变(因为它已经达到了最大/最小)。
回答by Stuart
The AudioSession
API used by some answers here has been deprecated as of iOS 7. It was replaced by AVAudioSession
, which exposes an outputVolume
property for the system wide output volume. This can be observed using KVO to receive notifications when the volume changes, as pointed out in the documentation:
从AudioSession
iOS 7 开始,这里的一些答案使用的API 已被弃用。它被替换为AVAudioSession
,它公开outputVolume
了系统范围输出量的属性。这可以通过使用 KVO 在音量变化时接收通知来观察,如文档中所指出的:
A value in the range 0.0 to 1.0, with 0.0 representing the minimum volume and 1.0 representing the maximum volume.
The system wide output volume can be set directly only by the user; to provide volume control in your app, use the MPVolumeView class.
You can observe changes to the value of this property by using key-value observing.
0.0 到 1.0 范围内的值,0.0 表示最小音量,1.0 表示最大音量。
系统范围的输出音量只能由用户直接设置;要在您的应用中提供音量控制,请使用 MPVolumeView 类。
您可以使用键值观察来观察此属性的值的变化。
You need to ensure your app's audio session is active for this to work:
您需要确保您的应用的音频会话处于活动状态才能使其正常工作:
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setActive(true)
startObservingVolumeChanges()
} catch {
print(“Failed to activate audio session")
}
So if all you need is to query the current system volume:
因此,如果您只需要查询当前系统卷:
let volume = audioSession.outputVolume
Or we can be notified of changes like so:
或者我们可以收到这样的更改通知:
private struct Observation {
static let VolumeKey = "outputVolume"
static var Context = 0
}
func startObservingVolumeChanges() {
audioSession.addObserver(self, forKeyPath: Observation.VolumeKey, options: [.Initial, .New], context: &Observation.Context)
}
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
if context == &Observation.Context {
if keyPath == Observation.VolumeKey, let volume = (change?[NSKeyValueChangeNewKey] as? NSNumber)?.floatValue {
// `volume` contains the new system output volume...
print("Volume: \(volume)")
}
} else {
super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
}
}
Don't forgetto stop observing before being deallocated:
在被释放之前不要忘记停止观察:
func stopObservingVolumeChanges() {
audioSession.removeObserver(self, forKeyPath: Observation.VolumeKey, context: &Observation.Context)
}
回答by Mike
-(float) getVolumeLevel
{
MPVolumeView *slide = [MPVolumeView new];
UISlider *volumeViewSlider;
for (UIView *view in [slide subviews]){
if ([[[view class] description] isEqualToString:@"MPVolumeSlider"]) {
volumeViewSlider = (UISlider *) view;
}
}
float val = [volumeViewSlider value];
[slide release];
return val;
}
That should get you the current volume level. 1 is max volume, 0 is no volume. Note: no UI elements need to be displayed for this to work. Also note current volume level is relative to headphones or speakers (meaning, the two volume levels are different, and this gets you whichever the device is currently using. This doesn't answer your question regarding receiving notifications of when volume changes.
这应该可以让您获得当前的音量级别。1 是最大音量,0 是无音量。注意:不需要显示任何 UI 元素才能使其工作。另请注意,当前音量级别与耳机或扬声器有关(意思是,两个音量级别不同,无论设备当前使用的是哪个级别,这都适用。这并不能回答您关于接收音量何时更改通知的问题。
回答by Karsten
did you start the audio session with AudioSessionSetActive
您是否使用 AudioSessionSetActive 启动了音频会话
回答by Joshua C. Lerner
Swift 3 version of Stuart's excellent answer:
Swift 3 版本的 Stuart 的优秀回答:
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setActive(true)
startObservingVolumeChanges()
}
catch {
print("Failed to activate audio session")
}
let volume = audioSession.outputVolume
private struct Observation {
static let VolumeKey = "outputVolume"
static var Context = 0
}
func startObservingVolumeChanges() {
audioSession.addObserver(self, forKeyPath: Observation.VolumeKey, options: [.Initial, .New], context: &Observation.Context)
}
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
if context == &Observation.Context {
if keyPath == Observation.VolumeKey, let volume = (change?[NSKeyValueChangeNewKey] as? NSNumber)?.floatValue {
// `volume` contains the new system output volume...
print("Volume: \(volume)")
}
} else {
super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
}
}
回答by Reimond Hill
Swift 4
斯威夫特 4
func startObservingVolumeChanges() {
avAudioSession.addObserver(self, forKeyPath: Observation.VolumeKey, options: [.initial, .new], context: &Observation.Context)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if context == &Observation.Context {
if keyPath == Observation.VolumeKey, let volume = (change?[NSKeyValueChangeKey.newKey] as? NSNumber)?.floatValue {
print("\(logClassName): Volume: \(volume)")
}
} else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
}
}
func stopObservingVolumeChanges() {
avAudioSession.removeObserver(self, forKeyPath: Observation.VolumeKey, context: &Observation.Context)
}
and then you call
然后你打电话
var avAudioSession = AVAudioSession.sharedInstance()
try? avAudioSession.setActive(true)
startObservingVolumeChanges()
回答by Abundance
Adding on to Stuart's answer using AVAudioSession to account for some changes in Swift 3. I hope the code will make it clear as to where each component goes.
使用 AVAudioSession 添加到 Stuart 的答案中以说明 Swift 3 中的一些更改。我希望代码能够清楚地说明每个组件的去向。
override func viewWillAppear(_ animated: Bool) {
listenVolumeButton()
}
func listenVolumeButton(){
let audioSession = AVAudioSession.sharedInstance()
do{
try audioSession.setActive(true)
let vol = audioSession.outputVolume
print(vol.description) //gets initial volume
}
catch{
print("Error info: \(error)")
}
audioSession.addObserver(self, forKeyPath: "outputVolume", options:
NSKeyValueObservingOptions.new, context: nil)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "outputVolume"{
let volume = (change?[NSKeyValueChangeKey.newKey] as
NSNumber)?.floatValue
print("volume " + volume!.description)
}
}
override func viewWillDisappear(_ animated: Bool) {
audioSession.removeObserver(self, forKeyPath: "outputVolume")
}
回答by Vanya
I think it depends on other implementation. If you for instance use the slider for controlling the volume of sound you can make a checking action by UIControlEventValueChanged
and if you get a 0 value you can set the button hidden or disabled.
我认为这取决于其他实现。例如,如果您使用滑块来控制音量,您可以进行检查操作UIControlEventValueChanged
,如果您获得 0 值,您可以将按钮设置为隐藏或禁用。
Something like:
就像是:
[MusicsliderCtl addTarget:self action:@selector(checkZeroVolume:)forControlEvents:UIControlEventValueChanged];
where void checkZeroVolume
could do the comparing of the actual volume since it is triggered after any volume change.
voidcheckZeroVolume
可以比较实际交易量,因为它是在任何交易量变化后触发的。
回答by fantaxy
Go into settings->sounds and check 'Change with Buttons'. If it's off the system volume won't change when pressing the volume buttons. Maybe that's the reason why you didn't get notified.
进入设置->声音并选中“使用按钮更改”。如果它关闭,按下音量按钮时系统音量不会改变。也许这就是你没有得到通知的原因。