55

I can't seem to find in the SDK how to programatically sense the mute button/switch on the iPhone. When my app plays background music, it responds properly to the volume button without me having any code to follow that but, when I use the mute switch, it just keeps playing away.

How do I test the position of mute?

(NOTE: My program has its own mute switch, but I'd like the physical switch to override that.)

mfaani
  • 33,269
  • 19
  • 164
  • 293
Olie
  • 24,597
  • 18
  • 99
  • 131

8 Answers8

30

Thanks, JPM. Indeed, the link you provide leads to the correct answer (eventually. ;) For completeness (because S.O. should be a source of QUICK answers! )...

// "Ambient" makes it respect the mute switch
// Must call this once to init session
if (!gAudioSessionInited)
{
    AudioSessionInterruptionListener    inInterruptionListener = NULL;
    OSStatus    error;
    if ((error = AudioSessionInitialize (NULL, NULL, inInterruptionListener, NULL)))
    {
        NSLog(@"*** Error *** error in AudioSessionInitialize: %d.", error);
    }
    else
    {
        gAudioSessionInited = YES;
    }
}

SInt32  ambient = kAudioSessionCategory_AmbientSound;
if (AudioSessionSetProperty (kAudioSessionProperty_AudioCategory, sizeof (ambient), &ambient))
{
    NSLog(@"*** Error *** could not set Session property to ambient.");
}
Olie
  • 24,597
  • 18
  • 99
  • 131
  • if I log the value of ambient afterwards, I will always get returned 1634558569 no matter if I'm muted or not. Any idea? – Finn Gaida Nov 14 '13 at 22:01
  • "Does not work" how? What did you do? What did you expect to happen? What happened, instead? If you provide more info, I'll try to update my answer. Thanks! – Olie Jun 13 '15 at 01:30
  • Could you please help me: http://stackoverflow.com/questions/31604629/audio-player-play-in-background-and-should-be-work-with-hardware-mute-switch – Ganee.... Aug 06 '15 at 08:09
11

I answered a similar question here (link). The relevant code:

 -(BOOL)silenced {
     #if TARGET_IPHONE_SIMULATOR
         // return NO in simulator. Code causes crashes for some reason.
         return NO;
     #endif

    CFStringRef state;
    UInt32 propertySize = sizeof(CFStringRef);
    AudioSessionInitialize(NULL, NULL, NULL, NULL);
    AudioSessionGetProperty(kAudioSessionProperty_AudioRoute, &propertySize, &state);
    if(CFStringGetLength(state) > 0)
            return NO;
    else
            return YES;

    }
Community
  • 1
  • 1
Chris Ladd
  • 2,795
  • 1
  • 29
  • 22
  • 1
    This doesn't work when headphones are plugged in. AudioSessionGetProperty for the audio route returns 'Headphone' regardless of mute switch. – yood May 06 '11 at 22:17
  • But does it matter if the mute switch is on when headphones are plugged in? Audio still plays out of the headphones, yes? It seems to me the point of the mute switch is to silence the phone from others, not yourself. Obeying it shouldn't matter much (imo) while headphones are in. That seems to be the stance Apple has taken by returning 'Headphone' – Chris Ladd May 20 '11 at 22:22
  • 7
    No longer works in iOS 5. http://stackoverflow.com/questions/6901363/detecting-the-iphones-ring-silent-mute-switch-using-avaudioplayer-not-worki – morgancodes Nov 05 '11 at 17:07
  • Bummer! Not the only thing that no longer works in iOS5 by a long stretch... ;) – Chris Ladd Nov 09 '11 at 16:59
  • iphone mute switch is not working in ios5.Is there any reason for that. – Yuvaraj.M Nov 28 '12 at 07:19
  • AudioRoute can't detect switch state - Ambient mode is the best solution. – slamor Dec 09 '14 at 18:22
  • AudioSessionInitialize' is unavailable: no longer supported –  Nov 06 '18 at 22:30
7

Some of the code in other answers (including the accepted answer) may not work if you aren't in the ambient mode, where the mute switch is respected.

I wrote the routine below to switch to ambient, read the switch, and then return to the settings I need in my app.

-(BOOL)muteSwitchEnabled {

#if TARGET_IPHONE_SIMULATOR
    // set to NO in simulator. Code causes crashes for some reason.
    return NO;
#endif

// go back to Ambient to detect the switch
AVAudioSession* sharedSession = [AVAudioSession sharedInstance];
[sharedSession setCategory:AVAudioSessionCategoryAmbient error:nil];

CFStringRef state;
UInt32 propertySize = sizeof(CFStringRef);
AudioSessionInitialize(NULL, NULL, NULL, NULL);
AudioSessionGetProperty(kAudioSessionProperty_AudioRoute, &propertySize, &state);

BOOL muteSwitch = (CFStringGetLength(state) <= 0);
NSLog(@"Mute switch: %d",muteSwitch);

// code below here is just restoring my own audio state, YMMV
_hasMicrophone = [sharedSession inputIsAvailable];
NSError* setCategoryError = nil;

if (_hasMicrophone) {

    [sharedSession setCategory: AVAudioSessionCategoryPlayAndRecord error: &setCategoryError];

    // By default PlayAndRecord plays out over the internal speaker.  We want the external speakers, thanks.
    UInt32 ASRoute = kAudioSessionOverrideAudioRoute_Speaker;
    AudioSessionSetProperty (kAudioSessionProperty_OverrideAudioRoute,
                             sizeof (ASRoute),
                             &ASRoute
                             );
}
else
    // Devices with no mike don't support PlayAndRecord - we don't get playback, so use just playback as we don't have a microphone anyway
    [sharedSession setCategory: AVAudioSessionCategoryPlayback error: &setCategoryError];

if (setCategoryError)
    NSLog(@"Error setting audio category! %@", setCategoryError);

return muteSwitch;
}
Praveen-K
  • 3,401
  • 1
  • 23
  • 31
Jane Sales
  • 13,526
  • 3
  • 52
  • 57
5

To find out the state of the mute switch and the volume control I wrote these two functions. These are ideal if you wish to warn the user before they try creating audio output.

-(NSString*)audioRoute
{
    CFStringRef state;
    UInt32 propertySize = sizeof(CFStringRef);
    OSStatus n = AudioSessionGetProperty(kAudioSessionProperty_AudioRoute, &propertySize, &state);
    if( n )
    {
        // TODO: Throw an exception
        NSLog( @"AudioSessionGetProperty: %@", osString( n ) );
    }

    NSString *result = (NSString*)state;
    [result autorelease];
    return result;
}

-(Float32)audioVolume
{
    Float32 state;
    UInt32 propertySize = sizeof(CFStringRef);
    OSStatus n = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareOutputVolume, &propertySize, &state);
    if( n )
    {
        // TODO: Throw an exception
        NSLog( @"AudioSessionGetProperty: %@", osString( n ) );
    }
    return state;
}
Martin Cowie
  • 2,788
  • 7
  • 38
  • 74
5
-(BOOL)isDeviceMuted
{
 CFStringRef state;
 UInt32 propertySize = sizeof(CFStringRef);
 AudioSessionInitialize(NULL, NULL, NULL, NULL);
 AudioSessionGetProperty(kAudioSessionProperty_AudioRoute, &propertySize, &state);
 return (CFStringGetLength(state) > 0 ? NO : YES);
}
gak
  • 32,061
  • 28
  • 119
  • 154
  • 4
    (CFStringGetLength(state) > 0 ? NO : YES) is the same as (CFStringGetLength(state) <= 0) – Daniel Oct 19 '10 at 12:52
  • @simpleBob how to detect the iphone mute button value. (e.x if the iphone mute switch in on position (in mute) how to detect the mute value). – Yuvaraj.M Nov 24 '12 at 13:16
  • @Yuvaraj.M That is what this question is about. Select your favorite answer ;) For example you could just run the method above, which returns YES if the device is muted. – Daniel Nov 26 '12 at 10:21
  • @simpleBob Thanks for reply. ANd Sorry for the disturb. It is working for all ios? It is not working in my ios5?. – Yuvaraj.M Nov 26 '12 at 12:01
4

I followed the general theory here and got this to work http://inforceapps.wordpress.com/2009/07/08/detect-mute-switch-state-on-iphone/

Here is a recap: Play a short silent sound. Time how long it takes to play. If the mute switch is on, the playing of the sound will come back much shorter than the sound itself. I used a 500ms sound and if the sound played in less than this time, then the mute switch was on. I use Audio Services to play the silent sound (which always honors the mute switch). This article says that you can use AVAudioPlayer to play this sound. If you use AVAudioPlayer, I assume you'll need to setup your AVAudioSession's category to honor the mute switch, but I have not tried it`.

Mark24x7
  • 1,447
  • 1
  • 11
  • 9
4

Using Ambient mode for playing a video and PlayAndRecord mode for recording a video on camera screen, resolves the issue in our case.

The code in application:didFinishLaunchingWithOptions:

NSError *error = nil;
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:&error];
[[AVAudioSession sharedInstance] setMode:AVAudioSessionModeVideoRecording error:&error];
[[AVAudioSession sharedInstance] setActive:YES error:&error];

The code in viewWillAppear on cameraController, if you have to use camera or recording in your app

[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];

The code in viewWillDisappear on cameraController

[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:nil];

Using these lines our Application records and plays a video and mute switch works perfectly under both iOS8 and iOS7!!!

slamor
  • 3,355
  • 2
  • 14
  • 13
  • Could you please help me, I have voip call feature in my app, I have to play music/ring tone when user gets a call. I want to play this ringtone even if app is in the background mode. If I use "AVAudioSessionCategoryPlayAndRecord" it is not working with mute switch, if I use "AVAudioSessionCategoryAmbient", it is not playing in the background. Please give me suggestions to achieve both cases. – Ganee.... Jul 30 '15 at 07:31
3

For Swift

Below framework works perfectly in device

https://github.com/akramhussein/Mute

Just install using pod or download from Git

pod 'Mute'

and use like below code

import UIKit
import Mute

class ViewController: UIViewController {

    @IBOutlet weak var label: UILabel! {
        didSet {
            self.label.text = ""
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        // Notify every 2 seconds
        Mute.shared.checkInterval = 2.0

        // Always notify on interval
        Mute.shared.alwaysNotify = true

        // Update label when notification received
        Mute.shared.notify = { m in
            self.label.text = m ? "Muted" : "Not Muted"
        }

        // Stop after 5 seconds
        DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
            Mute.shared.isPaused = true
        }

        // Re-start after 10 seconds
        DispatchQueue.main.asyncAfter(deadline: .now() + 10.0) {
            Mute.shared.isPaused = false
        }
    }

}
Govind Prajapati
  • 957
  • 7
  • 24