Search code examples
iphoneios4iphone-sdk-3.0avaudioplayeravaudiosession

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.


Solution

  • I went through this VSSilentSwitch library.
    Didn't work for me (doesn't work when you start actually using audio).
    I was thinking on how he did it, and then realised that the audio completion call is being called almost as soon as the sound begins playing when we're silent.
    To be a bit more specific:
    System sounds being played using AudioServicesPlaySystemSound will complete playback as soon as it started.
    Of course, this will only work on audio categories that respect the silent switch (the default AVAudioSessionCategoryAmbient respects it).
    So the trick is to create a system sound, preferably of a silent sound, and keep playing it over and over again, while checking the time it took from playback to completion (install a completion procedure using AudioServicesAddSystemSoundCompletion).
    If the completion proc is called very soon (allow some threshold) - it means the silent switch is on.
    This trick has many caveats, the biggest one being the fact that it won't work on all audio categories.
    If your app plays audio in the background - make sure you stop this test while in the background or your app will run forever in the background (and will be rejected by apple, too).