Detecting iPhone mute switch in iOS 5 - iphone

I know that Apple does not provide a way to detect the state of the iPhone mute/silence switch in iOS 5. However, I have I tried a technique mentioned elsewhere to detect this by playing an audio file and measuring its runtime. However, even though my iPhone was muted, the audio file still played the entire duration. I'm using [AVAudioPlayer play] and computing the time before audioPlayerDidFinishPlaying is called. Do you know of a trick to play an audio file, which will complete early/immediately if the phone is muted?

UPDATE: Sorry the below code posted works fine for me but I am using iOS4. For iOS5 this answer is what will solve your problem - Detecting the iPhone's Ring / Silent / Mute switch using AVAudioPlayer not working?
This piece of code is what you need. Define a global gAudioSessionInited var. This flag tells you whether your mute switch is on or off.
// "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.");
}

Related

use rear microphone of iphone 5

I have used to following code the stream the i/o of audio from microphone. What I want to do is want to select the rear microphone for recording. I have read that setting kAudioSessionProperty_Mode to kAudioSessionMode_VideoRecording can do the work but I am not sure how to use this with my code. Can any one help me in successfully setting this parameter.
I have these lines for setting the property
status = AudioUnitSetProperty(audioUnit,
kAudioSessionProperty_Mode,
kAudioSessionMode_VideoRecording,
kOutputBus,
&audioFormat,
sizeof(audioFormat));
checkStatus(status);
but its not working.
in apple developer library click here
you can see a specific method
struct AudioChannelLayout {
AudioChannelLayoutTag mChannelLayoutTag;
UInt32 mChannelBitmap;
UInt32 mNumberChannelDescriptions;
AudioChannelDescription mChannelDescriptions[1];
};
typedef struct AudioChannelLayout AudioChannelLayout;
you can change AudioChannelDescription to 2 for using secondary microphone
I did some searching and reading. Finally ended up in the AVCaptureDevice Class Reference. The key command here for you is NSLog(#"%#", [AVCaptureDevice devices]);. I ran this with my iPhone attached and got this:
"<AVCaptureFigVideoDevice: 0x1fd43a50 [Back Camera][com.apple.avfoundation.avcapturedevice.built-in_video:0]>",
"<AVCaptureFigVideoDevice: 0x1fd47230 [Front Camera][com.apple.avfoundation.avcapturedevice.built-in_video:1]>",
"<AVCaptureFigAudioDevice: 0x1fd46730 [Microphone][com.apple.avfoundation.avcapturedevice.built-in_audio:0]>"
Only one microphone ever shows up in the list. So to answer your question, it cannot be done (yet).
Your code:
status = AudioUnitSetProperty(audioUnit,
kAudioSessionProperty_Mode,
kAudioSessionMode_VideoRecording,
kOutputBus,
&audioFormat,
sizeof(audioFormat));
checkStatus(status);
Is not working as the code is not correct. Audio SESSIONS are not properties of Audio UNITS. The Audio Session describes the general behaviour of your app with hardware resources, and how it cooperates with other demands on those same resources by other apps and other parts of the system. It is your best chance of taking control of input and output hardware, but does not give you total control as the iOS frameworks have the overall user experience as the uppermost priority.
Your app has a single audio session, which you can initialise, activate and deactivate, and get and set properties of. Since ios6 most of these properties can be addressed using the AVFoundation singleton AVAudioSession object, but to get full access you will still want to use Core Audio function syntax.
To set the audio session mode to "VideoRecording" using AVFoundation you would do something like this:
- (void) configureAVAudioSession
{
//get your app's audioSession singleton object
AVAudioSession* session = [AVAudioSession sharedInstance];
//error handling
BOOL success;
NSError* error;
//set the audioSession category.
//Needs to be Record or PlayAndRecord to use VideoRecording mode:
success = [session setCategory:AVAudioSessionCategoryPlayAndRecord
error:&error]
if (!success) NSLog(#"AVAudioSession error setting category:%#",error);
//set the audioSession mode
succcess = [session setMode:AVAudioSessionModeVideoRecording error:&error];
if (!success) NSLog(#"AVAudioSession error setting mode:%#",error);
//activate the audio session
success = [session setActive:YES error:&error];
if (!success) NSLog(#"AVAudioSession error activating: %#",error);
else NSLog(#"audioSession active");
}
The same functionality using Core Audio functions (ios5 and below). checkStatus is the error handling function from your code sample.
- (void) configureAudioSession
{
OSStatus status;
//initialise the audio session
status = AudioSessionInitialize ( NULL
//runloop
, kCFRunLoopDefaultMode
//runloopmode
, NULL
//MyInterruptionListener
, (__bridge void *)(self)
//user info
);
//set the audio session category
UInt32 category = kAudioSessionCategory_PlayAndRecord;
status = AudioSessionSetProperty ( kAudioSessionProperty_AudioCategory
, sizeof(category)
, &category);
checkStatus(status);
//set the audio session mode
UInt32 mode = kAudioSessionMode_VideoRecording;
status = AudioSessionSetProperty(kAudioSessionMode_VideoRecording
, sizeof(mode)
, &mode);
checkStatus(status);
//activate the audio session
status = AudioSessionSetActive(true);
checkStatus(status);
}
The reason you have been told to use VideoRecording mode is because it is the only mode that will give you any hope of directly selecting the rear mic. What it does is select the mic nearest to the video camera.
"On devices with more than one built-in microphone, the microphone closest to the video camera is used." (From Apple's AVSession Class Reference)
This suggests that the video camera will need to be active when using the mic, and the choice of camera from front to back is the parameter that the system uses to select the appropriate microphone. It may be that video-free apps using the rear mic (such as your example) are in fact getting a video input stream from the rear camera and not doing anything with it. I am unable to test this as I do not have access to an iPhone 5. I do see that the "Babyscope" app you mentioned has an entirely different app for running on ios5 vs. ios4.
The answer from Kuriakose is misleading: AudioChannelLayout is a description of an audo track, it has no effect on the audio hardware used in capture. The answer from Sangony just shows us that Apple do not really want us to have full control over the hardware. Much of it's audio management on iOS is an attempt to keep us away from direct control in order to accommodate both user expectations (of audio i/o behaviour between apps) and hardware limitations when dealing with live signals.

iOS: Unable to play my sounds when silent mode on

So I've already gone through most of the relevant question on SO that I could find, yet I still can't get my audio playing while silent. I believe that it's probably a small error I'm making.
I'm trying to play a small alert sound. This is the code in my viewDidLoad method :
AudioSessionInitialize (NULL, NULL, NULL, NULL);
AudioSessionSetActive(true);
And here is the method I call when I need to play an audio clip
-(void)playAudioClip:(CFStringRef)clip{
CFBundleRef mainBundle = CFBundleGetMainBundle();
CFURLRef soundFile = CFBundleCopyResourceURL(mainBundle, clip, CFSTR("wav"), NULL);
//Supposed to make sound playable even when silent right?
UInt32 soundType = kAudioSessionCategory_MediaPlayback;
UInt32 soundID;
AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(soundType), &soundType);
AudioServicesCreateSystemSoundID(soundFile, &soundID);
AudioServicesPlaySystmSound(soundID);
}
I got most of this off SO and confirmed it in the Apple programming guides. I have already tried combining the Uint32 for both uses but this didn't do anything either, still no play in locked/silent mode. Any ideas?
System sounds do not play while in silent mode. (As the name would suggest) If you need to play audio while silent you need to use a different method like a media player or other audio method. Look at Apple's examples for playing music.

MPMoviePlayerController background audio issue in iOS5

I have an App that does the pretty standard operation:
It plays audio (streamed or in filesystem) when the app is in 1) Foreground mode, 2) Screen locked state 3)Background mode.
This was working fine in all iOS prior to iOS5.
I have been using MPMoviePlayerController (Because it can play streamed and local file system audio)
I have the following setup:
info.plist has Background Mode set to "Audio"
I have Audiosession setup as shown at http://developer.apple.com/library/ios/#qa/qa1668/_index.html
NSError *activationError = nil;
AVAudioSession *mySession = [AVAudioSession sharedInstance];
[mySession setCategory: AVAudioSessionCategoryPlayback error: &activationError];
if (activationError) { /* handle the error condition */ }
[mySession setActive: YES error: &activationError];
if (activationError) { /* handle the error condition */ }
I have background timer enabled that gets stopped at the end of audio playback
UIBackgroundTaskIdentifier newId = [[UIApplication sharedApplication]
beginBackgroundTaskWithExpirationHandler:NULL];
I have the Moveplayer's useApplicationAudioSession = NO
I have subscribed to the following events to detect and handle various playback state and to start a new audio file at the end of current file.
MPMoviePlayerLoadStateDidChangeNotification
MPMoviePlayerPlaybackDidFinishNotification
MPMoviePlayerPlaybackStateDidChangeNotification
MPMoviePlayerNowPlayingMovieDidChangeNotification
Problem:
With this the audio starts to play and when the application is put to background state or if the phone is locked, the audio continues to play. But, after when I start another audio file,
I start getting PlaybackDidFinishNotification immediately with the state set to Playback ended (But the file was never played)
The same code plays audio files in foreground mode (After the current audio file ends, the next file is started without any problem)
Is there anything new in iOS5 I should be doing to get this to work? I read through the MPMoviePlayerController class reference and I couldn't see anything specific for iOS5.
Thanks in advance.
Finally figured out the issue. This is solved in this post in apple dev forums (needs login to see). That post was applicable to AVPlayer but also fixes the problem with MPMoviePlayerController as well.
Basically, this is an excerpt from that post:
your app must support remote control events! These are the audio
controller interface prex/nex/play/pause on the left of the multitask
switcher taskbar (not sure about the proper name of the thing). You
to this ensuring your view becomes First Controller and then calling
> [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
in viewDidLoad. Once you do this, your Player will no longer return
NO!!
My situation was different and I'm only answering here (and in the other SO question) to help future searchers on this error message. This does not answer the original question.
My app plays a sound OR a song but when I first coded it could play both. And in testing I always tested with a song. I played the song in the usual way:
self.musicQuery = [MPMediaQuery songsQuery];
[_musicQuery addFilterPredicate:[MPMediaPropertyPredicate predicateWithValue:selectedSongID forProperty:MPMediaItemPropertyPersistentID comparisonType:MPMediaPredicateComparisonEqualTo]];
[_musicQuery setGroupingType:MPMediaGroupingTitle];
[_myPlayer setQueueWithQuery:_musicQuery];
[_myPlayer play];
Weeks passed and I started testing with the sound, played with AVAudioPlayer. My app started freezing for 5 seconds and I'd get the MediaPlayer: Message playbackState timed out message in the Console.
It turns out that passing a query that was empty was causing the freeze and the message. Changing my app's logic to only play a song when there was a song to play fixed it.

Any way to play audio on the iPhone when the screen is locked, but not when the silent switch is set?

I've been going through the audio session categories and overrides, and it appears that you can either play audio regardless of whether the silent switch is set or the screen is locked (AVAudioSessionCategoryPlayback), OR you can respect the silent switch and the screen lock settings (AVAudioSessionCategorySoloAmbient).
Any idea how I can play audio that gets muted by the silent switch, but keeps playing when the screen is locked? All I want is to have my cake and eat it too.
To answer the inevitable "why would you ever want to circumvent the system" questions, this is for an app that shows words and plays music which is likely to be used in both a church and home setting. At church it would be bad if the app started playing music, hence I want to respect the mute switch. At home you might set the phone down to listen to music while doing something else, and you wouldn't want it to stop playing when the phone auto-locks.
You can use AVAudioSessionCategoryPlayback and use this code to check if silent switch is on or off:
- (BOOL)deviceIsSilenced
{
#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);
return (CFStringGetLength(state) <= 0);
}

AVAudioSession category not working as documentation dictates

I have an iOS app that has some audio feedback in certain places, but I want any other music the user has playing in the background to be allowed to play over this. In addition, I want the audio in my app to respect the mute switch. According to the developer documentation, this functionality should all be enabled by the AVAudioSession ambient category. This is the code I'm using:
if (!hasInitialisedAudioSession) {
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryAmbient error:NULL];
[session setActive:YES error:NULL];
hasInitialisedAudioSession = YES;
}
The code is executing just fine, and it does indeed let the app sounds play over iPod music. What it doesn't do, however, is respect the mute switch. I've tried swapping this code out for similar C audio calls (stuff like AudioSessionSetProperty) instead of the Objective-C calls, but I get the same result - the ambient session category simply doesn't want to respect the mute switch, despite what the documentation says it should be doing.
Any ideas? Thanks for the help :)
I think I managed to work it out - turns out that it has nothing to do with my app at all, but rather the iPod app. My app obeys the mute switch as it should when the iPod isn't playing, and then allows the iPod to play over it - all behaviour I wanted. However, when the iPod is playing, the app stops responding to the mute switch, so I think it's just something the iPod does to the device audio settings. I could probably work a way around it if I really wanted to spend the time on it, but as long as it obeys the mute switch when the iPod isn't playing that's good enough for me.
EDIT: to work around this, just use this function to determine whether or not the mute switch is on manually, and don't play your sounds if the result is YES. Could be a bit of a pain if you don't have a central audio manager class, though. It would be nice if Apple could publish this behaviour in their documentation.
- (BOOL)deviceIsSilenced
{
#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);
return (CFStringGetLength(state) <= 0);
}