How to get sound volume level, and volume changed notifications on iOS?

I am writing a very simple application that plays sound when a button is pressed. Since this button does not make much sense when the device is set to silent, I want to turn it off when the device’s sound volume is zero. (And subsequently, it can be reused when the volume is twisted again.)

I am looking for a working (and safe for AppStore) way to determine the current volume level and receive a notification / callback when the volume level changes. I do not want to change the volume setting.

All this is implemented in my ViewController , where this button is used. I tested this with iPhone 4 with iOS 4.0.1 and 4.0.2, as well as with iPhone 3G 4.0.1. Built with iOS SDK 4.0.2 with llvm 1.5. (Using gcc or llvm-gcc does not improve anything.) There are no problems, no errors, or warnings during the implementation of the build. The static analyzer is also happy.

Here is what I have tried so far, all without success.

Following Apple's audio documentation, I have to register an AudioSessionAddPropertyListener for kAudioSessionProperty_CurrentHardwareOutputVolume , which should work as follows:

 // Registering for Volume Change notifications AudioSessionInitialize(NULL, NULL, NULL, NULL); returnvalue = AudioSessionAddPropertyListener ( kAudioSessionProperty_CurrentHardwareOutputVolume , audioVolumeChangeListenerCallback, self ); 

returnvalue 0 , which means that callback registration has been completed.

Unfortunately, I never get a callback to my audioVolumeChangeListenerCallback function when I press the volume buttons on my device, the headset clicks or flips the switch without ringing.

When using the same registration code for kAudioSessionProperty_AudioRouteChange (which is used as a similar sample project in WWDC video, documentation for developers and on numerous sites on networks), I really get a callback when changing the audio track (by connecting / disconnecting the headset or docking the device )

A user named Doug opened a stream called changed iPhone volume to volumes already max , where he claimed that he was using this method successfully (unless the volume changed, because it was already set to maximum). However, this does not work for me.

Another way I tried is to register at NSNotificationCenter like this.

 // 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 volumeChanged method of any changes to SystemVolume , but in fact it is not.

Since a common opinion tells me that if someone works too hard to achieve something with Cocoa, then someone is doing something fundamentally wrong. I expect to miss something. It's hard to believe that there is no easy way to get the current volume level, but I could not find it using the Apple documentation, sample code, Google, Apple Developer forums or watching WWDC 2010 videos.

+48
ios iphone ios4 audio volume
Sep 06 '10 at
source share
8 answers

Is there a chance your signature was incorrect for the volumeChanged: method? This worked for me, reset in my 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: the method gets hit every time the button is pressed, even if the volume does not change as a result (because it is already with max / min).

+63
Jun 24 '11 at 18:25
source share

The AudioSession API used by some of the answers here is deprecated from iOS 7. It has been replaced by AVAudioSession , which provides the outputVolume property for the entire system’s volume. This can be observed using KVO to receive notifications when the volume changes, as indicated in the documentation:

Values ​​range from 0.0 to 1.0, with 0.0 representing the minimum volume and 1.0 representing the maximum volume.

The volume level of the system can be set directly by the user; in perform volume management in your application, use the MPVolumeView class.

You can watch for changes to this property using the monitoring of key values.

You need to make sure that your audio application session is active for this:

 let audioSession = AVAudioSession.sharedInstance() do { try audioSession.setActive(true) startObservingVolumeChanges() } catch { print("Failed to activate audio session") } 

So, if you just need to query the current system volume:

 let volume = audioSession.outputVolume 

Or we can be notified of changes as follows:

 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) } } 

Remember to stop watching until released:

 func stopObservingVolumeChanges() { audioSession.removeObserver(self, forKeyPath: Observation.VolumeKey, context: &Observation.Context) } 
+38
Mar 20 '15 at 16:03
source share
 -(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; } 

This will give you the current volume level. 1 - maximum volume, 0 - not volume. Note: you do not need to display interface elements for this. Also pay attention to the current volume level relative to the headphones or speakers (which means that the two volume levels are different, and this gives you what is currently being used by the device. This does not answer your question regarding receiving notifications about when the volume changes.

+6
Feb 04 2018-12-12T00:
source share

You started an audio session with AudioSessionSetActive

+3
Sep 06 '10 at 13:05
source share

I think it depends on another implementation. If, for example, you use the slider to control the sound volume, you can check for UIControlEventValueChanged , and if you get the value 0, you can set the hidden or disabled button.

Something like:

 [MusicsliderCtl addTarget:self action:@selector(checkZeroVolume:)forControlEvents:UIControlEventValueChanged]; 

where void checkZeroVolume can perform a comparison of the actual volume, since it starts after the volume changes.

+1
Sep 06 '10 at 13:23
source share

Go to settings-> sounds and check the box "Change using buttons". If this happens, the system volume will not change when you press the volume buttons. Perhaps the reason you did not receive the notification.

0
Apr 17 '14 at 6:43
source share

Adding a response to Stuart using AVAudioSession to account for some of the changes in Swift 3. I hope the code makes it clear where each component is located.

 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") } 
0
Mar 26 '17 at 18:16
source share

Swift 3 versions of Stuart's excellent answer:

 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) } } 
0
Aug 15 '17 at 12:39 on
source share



All Articles