Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detecting the iPhone's Ring / Silent / Mute switch using AVAudioPlayer not working?

I've tried using these methods in an attempt to detect that the Ring/Silent switch is active or not:

How to programmatically sense the iPhone mute switch?

AVAudioSession category not working as documentation dictates

But on my iPhone 4, the "state" value is always "Speaker" (and the length value returned by CFStringGetLength(state) is always 7). Has anyone used this method successfully? If so, on what kind of device and SDK version?

I'm calling it like so:

 - (BOOL)deviceIsSilenced {     CFStringRef state;     UInt32 propertySize = sizeof(CFStringRef);     OSStatus audioStatus = AudioSessionGetProperty(kAudioSessionProperty_AudioRoute, &propertySize, &state);     if (audioStatus == kAudioSessionNoError) {         NSLog(@"audio route: %@", state) // "Speaker" regardless of silent switch setting, but "Headphone" when my headphones are plugged in         return (CFStringGetLength(state) <= 0);     }     return NO; }  -(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {      AVAudioSession *audioSession = [AVAudioSession sharedInstance];     audioSession.delegate = self;     [audioSession setCategory:AVAudioSessionCategoryAmbient error:nil];     [audioSession setActive:YES error:nil];     NSLog(@"muted? %i", [self deviceIsSilenced]);     ... } 

I was thinking maybe some other (more accurate) kAudioSessionProperty event is fired when the physical switch on the phone is ... switched. Anyone have any ideas?

By the way, I'm using the AVAudioSessionCategoryAmbient category with my [AVAudioSession sharedInstance].

Update: I've also tried using different audio categories, and a handful of other audio session properties, none seem to fire when muting/unmuting the switch. :(

Jan. 1, 2014 Update: It's a bit of a hack, and I encountered a crash while multitasking w/ it on my iPhone 5S, but the SoundSwitch library linked in the new accepted answer is the way to go if you want to detect the silent switch. It even works in iOS 7.

like image 594
taber Avatar asked Aug 01 '11 16:08

taber


People also ask

Why is my silent switch not working on my iPhone?

If your iPhone's silent switch is not working, tap on the Assistive Touch option and go to the Device features. From here, you can tap on the “Mute” button to put your device in silent mode. You can later follow the same process and tap on the icon to un-mute your device (to put the phone off the silent mode).

Why is my silent button on my phone not working?

The first thing to try is a simple restart. In most cases, a restart will fix the problem and the iPhone silent button will start working again. You can try putting your iPhone 8 plus in silent mode by just toggling the switch.

How does the iPhone mute switch work?

The Ring/Silent switch is on the left side of your iPhone. You can use it to control which sounds play through your iPhone speaker.


2 Answers

Well I found the answer thanks to someone from the Developer Forums, and you guys aren't gonna like it!

I've had a response from Apple on this.

They've said they don't and never have provided a method for detecting hardware mute switch and don't intend to do so.

:(

IMO there is definitely value in detecting the silent switch and notifying the user in case they've forgotten it was on... I've had people complain that they don't have any sound and the silent switch was the reason! Oh well.

PS: If you would like Apple to add this feature (and of course you do!) please file a new "Enhancement" bug report for "iPhone SDK" at http://bugreport.apple.com/

Update: While there is still no official way to check the status of the mute switch, there's a workaround/library called "SoundSwitch" that seems to do the trick. Check out the new accepted answer for the link.

like image 145
taber Avatar answered Sep 30 '22 10:09

taber


"However, if someone could show us how to use this AudioSessionProperty_AudioRouteDescription, the bounty is rightfully his."

Well, just NSLog() the result and you get

routes: {     "RouteDetailedDescription_Inputs" =     (     );     "RouteDetailedDescription_Outputs" =     (                 {             "RouteDetailedDescription_PortType" = Speaker;         }     ); } 

Unfortunately, I get that identical result on an iPad2/OS 5.0 both muted and unmuted. So it appears to be functionally equivalent to kAudioSessionProperty_AudioRoute, no joy there.

Looking on the developer boards finds that this is a frequently encountered problem, summed up best with

"I reported this issue with rdar://9781189 back in July and the issue is still present in the GM."

So yeah ... sure looks like you're SOL with this in 5.0.

ADDENDUM:

"But how about that CFDictionary that you are logging. How can I access the "RouteDetailedDescription_PortType" key?"

Toll free bridging is your friend.

  CFDictionaryRef asCFType = nil;   UInt32 dataSize = sizeof(asCFType);   AudioSessionGetProperty(kAudioSessionProperty_AudioRouteDescription, &dataSize, &asCFType);   NSDictionary *easyPeasy = (NSDictionary *)asCFType;   NSDictionary *firstOutput = (NSDictionary *)[[easyPeasy valueForKey:@"RouteDetailedDescription_Outputs"] objectAtIndex:0];   NSString *portType = (NSString *)[firstOutput valueForKey:@"RouteDetailedDescription_PortType"];   NSLog(@"first output port type is: %@!", portType); 

produces

first output port type is: Speaker!

Many common CFTypes are bridged to more convenient types.

http://developer.apple.com/library/ios/#documentation/CoreFoundation/Conceptual/CFDesignConcepts/Articles/tollFreeBridgedTypes.html

Now, it takes a little bit of practice to guess correctly what magic incantation casts will get something useful out of a dictionary as above. A class dump helper along these lines will help you get up to speed with that:

  - (void)whatsInThis:(CFDictionaryRef)thingy   {      NSDictionary *dict = (NSDictionary *)thingy;      for (NSString *key in dict.allKeys)      {         id value = [dict valueForKey:key];         NSLog(@"key: %@ value type %@", key, [value class]);         if ([value isKindOfClass:[NSArray class]])         {            NSArray *array = (NSArray *)value;            for (id item in array)            {               NSLog(@" -- object type %@", [item class]);               if ([item isKindOfClass:[NSDictionary class]])                  [self whatsInThis:item];            }         }      }   } 

which for our dictionary at hand produces (adding indentation for clarity)

key: RouteDetailedDescription_Inputs value type __NSCFArray key: RouteDetailedDescription_Outputs value type __NSCFArray   -- object type __NSCFDictionary     key: RouteDetailedDescription_PortType value type __NSCFString 

which lets you know for sure what you could have deduced from the original log, knowing that NSLog displays arrays within ( ) and dictionaries within { } so the correct casts were eminently guessable. But some CFType structures are rather harder to parse than that.

like image 45
Alex Curylo Avatar answered Sep 30 '22 11:09

Alex Curylo