Is it possible to use AVAudioRecorder without silencing playback? - iphone

i'm using AVAudioRecorder to detect blows into the mic of the iPhone. But when i "record", the iPhone silences background music. Is there a way to avoid this?
I've tried to set the audio session category to ambient:
[session setCategory:AVAudioSessionCategoryAmbient error:&error];
But then recording isn't allowed anymore.
Also the mixwithothers property is not allowed to be set if you use the record category
UInt32 allowMixing = true;
propertySetError = AudioSessionSetProperty(
kAudioSessionProperty_OverrideCategoryMixWithOthers,
sizeof(allowMixing),
&allowMixing
);

Related

AVQueuePlayer and audio session issue

I am going to try to give a detailed account of my issue.
I have an app that is in the store that uses in app sound. Currently I am using AVQueuePlayer because some of the sound will overlap and allow it to play in order. A lot of this sound is being played while I am playing embedded videos using AVPlayer which may not matter at all. The problem is that I am having reports of the sound stopping across the entire app. I am unable to reproduce this myself but we have a lot of active users and it is reported by some. Whenever it is reported and we determine its not just the silent switch or the sound volume down restarting the app always solves the problem. Occasionally we've heard of the sound magically returning with no changes. I have also had a couple of reports that it happens when using airplay and bluetooth but that may just be an complication of the problem or coincidence.
Below is the code that I am using and maybe I'm just using a setting wrong or not using a setting that I should be but this code works 99.9% of the time.
I use ducking for all sounds I play to lower the volume of the user's iPod music.
Here is my initialization in appDidFinishLaunchingWithOptions (Maybe its not needed at all in the start and sorry for the mixing of conventions):
AudioSessionInitialize (NULL, NULL, NULL, NULL);
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
UInt32 sessionCategory = kAudioSessionCategory_AmbientSound;
AudioSessionSetProperty (kAudioSessionProperty_AudioCategory, sizeof (sessionCategory), &sessionCategory);
[[AVAudioSession sharedInstance] setActive:YES withFlags:AVAudioSessionSetActiveFlags_NotifyOthersOnDeactivation error:nil];
When I play a sound:
-(void)playSound: (NSString *)soundString
{
OSStatus propertySetError = 0;
UInt32 allowMixing = true;
propertySetError |= AudioSessionSetProperty(kAudioSessionProperty_OtherMixableAudioShouldDuck, sizeof(allowMixing), &allowMixing);
[[AVAudioSession sharedInstance] setActive:YES withFlags:AVAudioSessionSetActiveFlags_NotifyOthersOnDeactivation error:nil];
NSURL *thisUrl = [NSURL fileURLWithPath:[NSString stringWithFormat:#"%#/%#.caf", [[NSBundle mainBundle] resourcePath], soundString]];
AVPlayerItem *item = [[AVPlayerItem alloc] initWithURL:thisUrl];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(reachedEndOfItem:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:item];
if(_audioPlayerQueue == nil)
{
_audioPlayerQueue = [[AVQueuePlayer alloc] initWithItems:[NSArray arrayWithObject:item]];
}
else
{
if([_audioPlayerQueue canInsertItem:item afterItem:nil])
{
[_audioPlayerQueue insertItem:item afterItem:nil];
}
}
if(_audioPlayerQueue == nil)
{
NSLog(#"error");
}
else
{
[_audioPlayerQueue play];
}
return;
}
When the sound finishes playing:
- (void)reachedEndOfItem: (AVPlayerItem*)item
{
[self performSelector:#selector(turnOffDucking) withObject:nil afterDelay:0.5f];
}
- (void)turnOffDucking
{
NSLog(#"reached end");
[[AVAudioSession sharedInstance] setActive:NO withFlags:AVAudioSessionSetActiveFlags_NotifyOthersOnDeactivation error:nil];
OSStatus propertySetError = 0;
UInt32 allowMixing = false;
propertySetError |= AudioSessionSetProperty(kAudioSessionProperty_OtherMixableAudioShouldDuck, sizeof(allowMixing), &allowMixing);
}
Any insight on what I am doing wrong, what settings I should be using for the audio session or known bugs/problems would be very helpful. I would be willing to look into using a different audio engine as this can have some slight performance issues when playing a video and having iPod music playing in tandem but I'd rather stick with this method of playing audio.
Thank you for any help you can provide.
-Ryan
I had a similar issue in past and found out that concurrent thread access was the reason.
Specifically, I think calling performSelectorAfterDelay could be the reason if another thread tries to modify the audio session at the same time the delay ends, as then we'll have two different threads trying to access the audio session.
So, I suggest to check your code again and make sure all calls to playSound are made from the main thread. Also, it may be better to use performSelectorOnMainThread instead of performSelectorAfterDelay as the docs say:
Invocations of blocks, key-value observers, or notification handlers are not guaranteed to be made on any particular thread or queue. Instead, AV Foundation invokes these handlers on threads or queues on which it performs its internal tasks.

How to play audio over speakers rather than the much weaker ear speakers?

I'm learning core audio. For some reason the sound of the processing graph only plays through the weak "ear speakers" (when you hold device to your ear) but not over the regular speakers of the iPhone.
This is the code which sets up the audio session but I can't see where it configures the audio route:
- (void) setupAudioSession {
AVAudioSession *mySession = [AVAudioSession sharedInstance];
// Specify that this object is the delegate of the audio session, so that
// this object's endInterruption method will be invoked when needed.
[mySession setDelegate: self];
// Assign the Playback category to the audio session.
NSError *audioSessionError = nil;
[mySession setCategory: AVAudioSessionCategoryPlayAndRecord//AVAudioSessionCategoryPlayback
error: &audioSessionError];
if (audioSessionError != nil) {
NSLog (#"Error setting audio session category.");
return;
}
// Request the desired hardware sample rate.
self.graphSampleRate = 44100.0; // Hertz
[mySession setPreferredHardwareSampleRate: graphSampleRate
error: &audioSessionError];
if (audioSessionError != nil) {
NSLog (#"Error setting preferred hardware sample rate.");
return;
}
// Activate the audio session
[mySession setActive: YES
error: &audioSessionError];
if (audioSessionError != nil) {
NSLog (#"Error activating audio session during initial setup.");
return;
}
// Obtain the actual hardware sample rate and store it for later use in the audio processing graph.
self.graphSampleRate = [mySession currentHardwareSampleRate];
// Register the audio route change listener callback function with the audio session.
AudioSessionAddPropertyListener (
kAudioSessionProperty_AudioRouteChange,
audioRouteChangeListenerCallback,
self
);
}
At which point in core audio do you say "play over speakers" when playing sounds with audio units?
You can use the setCategory withOption:
[mySession setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker error:&audioSessionError];
I had the same problem. Turns out it is something to do with the "play and record" category. Just need to redirect the audio output.
UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_Speaker;
AudioSessionSetProperty (
kAudioSessionProperty_OverrideAudioRoute,
sizeof (audioRouteOverride),
&audioRouteOverride
);
Source:
http://developer.apple.com/library/ios/#documentation/Audio/Conceptual/AudioSessionProgrammingGuide/Cookbook/Cookbook.html#//apple_ref/doc/uid/TP40007875-CH6-SW35

AVAssetWriter / AVAudioPlayer Conflict?

Weeks ago, I posted this thread regarding problems I was having with AVAssetWriter: AVAssetWriter Woes
Further research seems to lead to a conflict using AVAssetWriter while playing audio with AVAudioPlayer or, really, any audio system. I tried with OpenAL as well.
Here's the background:
Using AVAssetWriter to write frames to a video from an image or set of images works fine UNTIL [AVAudioPlayer play] is called.
This only happens on the device, not the sim.
The error occurs when attempting to create a pixel buffer from CVPixelBufferPoolCreatePixelBuffer.
Once the audio starts playing, the AVAssetWriterInputPixelBufferAdaptor.pixelBufferPool which existed before suddely becomes nil.
You can download the representative project here: http://www.mediafire.com/?5k7kqyvtbfdgdgv
Comment out AVAudioPlayer play and it will work on the device.
Any clues are appreciated.
I've found the solution to this issue.
If you want to have AVAudioPlayer and AVAssetWriter behave correctly together, you must have and audio session category that is 'mixable'.
You can use a category that is mixable like AVAudioSessionCategoryAmbient.
However, I needed to use AVAudioSessionCategoryPlayAndRecord.
You can set any category to be mixable by implementing this:
OSStatus propertySetError = 0;
UInt32 allowMixing = true;
propertySetError = AudioSessionSetProperty (
kAudioSessionProperty_OverrideCategoryMixWithOthers, // 1
sizeof (allowMixing), // 2
&allowMixing // 3
);
This above answer is in complete. It doesn't work. Do this instead
// Setup to be able to record global sounds (preexisting app sounds)
NSError *sessionError = nil;
if ([[AVAudioSession sharedInstance] respondsToSelector:#selector(setCategory:withOptions:error:)])
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDuckOthers error:&sessionError];
else
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:&sessionError];
// Set the audio session to be active
[[AVAudioSession sharedInstance] setActive:YES error:&sessionError];
//then call your asset writer
movieWriter = [[AVAssetWriter alloc] initWithMovieURL:movieURL size:CGSizeMake(480.0, 640.0)];

Playing sound while the app is inactive/in background with respect to mute/ring-switch

I want to play sound while my app is in the background and respect the mute-ring-switch.
I managed to play sound while my app is not in the foreground. I use basically the following code to play my sound:
sound = [[AVAudioPlayer alloc] initWithContentsOfURL:fileUrl error:nil];
sound.numberOfLoops = -1;
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
/* ... */
[sound playAtTime:0];
AVAudioSessionCategoryPlayback causes my application to ignore the silent/ring-switch and play sound in background.
Since I want to have the last behavior without the first, I searched for a solution to query the state of the silent/rind-switch. I tried different approaches but this seemed to work for most of the users:
-(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;
}
I found it here:
How to programmatically sense the iPhone mute switch?
This code isn't working for me. Despite the phone is muted "state" is not empty.
Does anyone have an idea, why this isn't working for me? I am developing and testing for iOS 4.3.
Thx.
Edit I used CFShow to show the content of the kAudioSessionProperty_AudioRoute and it is "Speaker" everytime, nontheless I changed the state of the mute/ring switch.

playing iPod music and avaudioplayer together

I am trying to write an application where the user records a sound and would be listening to the same with a background music from music library. However, when i try to play the recorded file using AVAudioplayer, the background music (iPod Player) is going very low and not audible. Is there any session property i need to set for playing both the AVAudioPlayer and the iPod player at the same level?
I have tried to put allowMixing property but no success..
Here is the code for setting . May be i missed out something.
[audioSession setCategory :AVAudioSessionCategoryPlayAndRecord error:&err];
UInt32 category = 1;
AudioSessionSetProperty( kAudioSessionProperty_OverrideCategoryMixWithOthers , sizeof( category ) , &category );
UInt32 doChangeDefaultRoute = 1;
OSStatus status;
if (status = AudioSessionSetProperty( kAudioSessionProperty_OverrideCategoryDefaultToSpeaker,
sizeof(doChangeDefaultRoute), &doChangeDefaultRoute)) {
NSLog(#"RunSketchAppDelegate: ERROR: couldn't set kAudioSessionProperty_OverrideCategoryDefaultToSpeaker to %i. Error code = %i", doChangeDefaultRoute, status);
} else {
NSLog(#"RunSketchAppDelegate: successfully set kAudioSessionProperty_OverrideCategoryDefaultToSpeaker to %i", doChangeDefaultRoute);
}
Hope that helps.. I am still getting the playback lovering.