AVAudioRecorder not working on iPhone 5S - iphone

So I'm using an AVAudioRecorder to record audio alongside an AVCaptureSession that is recording video (I know this is odd, but for my situation I need to record them seperately).
Everything works fine on every device, except for my iPhone 5S. It records without error, but the file that is saved to disk is corrupted or something. When I access the file system on my mac and try and play the m4a file with VLC or Quicktime, I get a "format of the file cannot be detected" error. Here is how I am initializing my AVAudioRecorder and recording my audio:
// Prepare the audio session
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryRecord error:nil];
[[AVAudioSession sharedInstance] setMode:AVAudioSessionModeVideoRecording error:nil];
// Setup audio recording
NSDictionary *recordSettings = #{AVFormatIDKey: #(kAudioFormatMPEG4AAC),
AVEncoderAudioQualityKey: #(AVAudioQualityLow),
AVEncoderBitRateKey: #16,
AVNumberOfChannelsKey: #1,
AVSampleRateKey: #22050.0f};
NSError *audioRecorderError;
NSURL *audioFileURL = [[self.outputFileURL URLByDeletingPathExtension] URLByAppendingPathExtension:#"m4a"];
self.audioRecorder = [[AVAudioRecorder alloc] initWithURL:audioFileURL
settings:recordSettings
error:&audioRecorderError];
self.audioRecorder.delegate = self;
if (audioRecorderError) {
CCLog(#"Error while initializing the audio recorder... Skipping sound recording!");
}
else {
if (![self.audioRecorder prepareToRecord]) {
CCLog(#"Error preparing to record");
}
if (![self.audioRecorder record]) {
CCLog(#"Error recording");
}
}
Again, this works on all devices aside from the 5S. Anyone know what could be causing this?

Did some more digging and I apparently found the solution. I simply got rid of AVEncoderBitRateKey and everything works fine. So now my recordSettings dictionary looks like this:
// Setup audio recording
NSDictionary *recordSettings = #{AVFormatIDKey: #(kAudioFormatMPEG4AAC),
AVEncoderAudioQualityKey: #(AVAudioQualityLow),
AVNumberOfChannelsKey: #1,
AVSampleRateKey: #22050.0f};
Still not sure why this would be the case only on an iPhone 5S. Again, I've tested on all other devices running iOS6 and iOS7, and the old settings dictionary works fine on everything except for the 5S. Now that I've removed the AVEncoderBitRateKey and value, it also works on the 5S.

It looks like it's actually an interaction between AVNumberOfChannelsKey and AVEncoderBitRateKey. I can't seem to record above 64000Kbps when the # of channels = 1, but when it = 2, I can use 128000...

I had this same problem - and the answer proved to be related to AVAudioSession. I needed to set the audio session category to PlayAndRecord. Other parts of my application were setting it to Ambient. This post https://stackoverflow.com/a/10287793/1009125 gave me the answer. Hope this helps someone, I lost a Sunday to this one.

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.

Is it possible to use AVAudioRecorder without silencing playback?

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
);

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.