I'm having problems with AVAudioSession using the AVAudioRecorder in a cocos2d game that I'm working on.
I'm trying to capture mic input using a simple AVAudioRecorder example to detect when the user makes a sound in the mic (the sound itself doesn't matter, as I'm recording into /dev/null).
Here is my setup code for the microphone:
NSURL *newURL = [[NSURL alloc] initFileURLWithPath:#"/dev/null"];
[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayAndRecord error: nil];
NSDictionary *recordSettings =
[[NSDictionary alloc] initWithObjectsAndKeys:
[NSNumber numberWithFloat: 22050.0], AVSampleRateKey,
[NSNumber numberWithInt: kAudioFormatAppleLossless], AVFormatIDKey,
[NSNumber numberWithInt: 1], AVNumberOfChannelsKey,
[NSNumber numberWithInt: AVAudioQualityLow],
AVEncoderAudioQualityKey,
nil];
micInput = [[AVAudioRecorder alloc] initWithURL:newURL settings:recordSettings error:nil];
[newURL release];
[recordSettings release];
[micInput setMeteringEnabled:YES];
On the iPhone with the above code, the scene starts with all of the audio (sound effects, background music, etc.) playing at a really low level, because it is only playing through the phone speaker instead of the external speaker. When I test this on iPad or iPod Touch, the background audio plays through the external speaker as expected. This is a problem, since the volume of the game lowers drastically when playing on the iPhone version during this particular part of the game.
When I comment out the AVAudioSession setup line, the sounds play through the external speaker, but of course I can't get microphone input anymore. Is there any workaround or solution to this problem? I need to be able to record with AVAudioRecorder but still have audio output from the iPhone's external speaker.
Thanks!
Try something like the following after you setup your audio session:
UInt32 ASRoute = kAudioSessionOverrideAudioRoute_Speaker;
AudioSessionSetProperty (
kAudioSessionProperty_OverrideAudioRoute,
sizeof (ASRoute),
&ASRoute
);
Related
I record some Audio file with my iOS application and I send it to an Android application through my web server.
The Android application successfully gets the file but when I use the MediaPlayer class to try and play it, it reports an error "Unable to to create media player" at the line mediaplayer.setDataSource(androidFilePath);
Just to mention, iOS devices can read the files sent with the app.
After some research I found that it may be an encoding issue. But I tried many recording settings provided here on SO but none has worked. Here is the code I use to record the audio file:
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
[audioSession setCategory:AVAudioSessionCategoryRecord error:nil];
NSMutableDictionary *recordSettings = [[NSMutableDictionary alloc] initWithCapacity:10];
[recordSettings NSNumber numberWithInt: kAudioFormatMPEG4AAC] forKey: AVFormatIDKey];
[recordSettings setObject:[NSNumber numberWithFloat:16000.0] forKey: AVSampleRateKey];
[recordSettings setObject:[NSNumber numberWithInt:1] forKey:AVNumberOfChannelsKey];
[recordSettings setObject:[NSNumber numberWithInt: AVAudioQualityMin] forKey: AVEncoderAudioQualityKey];
}
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *basePath = paths[0];
self.audioURL = [NSURL fileURLWithPath:[NSString stringWithFormat:#"%#/recordTest.caf", basePath]];
NSError *error = nil;
self.audioRecorder = [[ AVAudioRecorder alloc] initWithURL:self.audioURL settings:recordSettings error:&error];
if ([self.audioRecorder recordForDuration:60] == YES){
[self.audioRecorder record];
}
Could you just tell me what change do I have to make so that Android devices can read those audio files?
I'd probably try .aac or .mp4 instead of .caf.
Try removing the AVEncoderAudioQualityKey from the code and check if this solves the problem. Also do check if the android device you are checking with are able to play the other aac files properly or not.
No you can't state's all the formats used Check here.
You would have to convert to AAC or WAV before you can use it
I am continuing this question from my old post. I read much content for audio recording and playing using Audio queues, audio bufferes, Unfortunately due to less time I compromised and planning to use AVFondation frame work for recording and playing of recorder audio.
My idea: creating one instance of AVRecorder and record through micro phone, same audio planning to play through Iphone speaker using AVAudioPlayer (I ll use multiple instances)
Please bare my tons of lines for recording and playing
-(id)startAudioRecorder:(NSUInteger)viewTag {
NSError *setCategoryErr = nil;
NSError *activationErr = nil;
//Set the general audio session category
[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayAndRecord error: &setCategoryErr];
//Make the default sound route for the session be to use the speaker
UInt32 doChangeDefaultRoute = 1;
AudioSessionSetProperty (kAudioSessionProperty_OverrideCategoryDefaultToSpeaker, sizeof (doChangeDefaultRoute), &doChangeDefaultRoute);
//Activate the customized audio session
[[AVAudioSession sharedInstance] setActive: YES error: &activationErr];
self.audioRecorder=nil;
//AVAudioSession *audioSession = [AVAudioSession sharedInstance];
NSError *err = nil;
//[audioSession setCategory :AVAudioSessionCategoryPlayAndRecord error:&err];
NSMutableDictionary* recordSetting = [[NSMutableDictionary alloc] init] ;
[recordSetting setValue :[NSNumber numberWithInt:kAudioFormatMPEG4AAC] forKey:AVFormatIDKey];//kAudioFormatAppleIMA4,kAudioFormatAAC
[recordSetting setValue:[NSNumber numberWithFloat:44100.0] forKey:AVSampleRateKey];
[recordSetting setValue:[NSNumber numberWithInt: 1] forKey:AVNumberOfChannelsKey];
NSString *absolutePath=[self getAbsoluteAudioFilePath:viewTag];
NSError *audioRecorderError=nil;
NSURL *absoluteUrl=[NSURL fileURLWithPath:absolutePath];
self.audioRecorder=[[[AVAudioRecorder alloc] initWithURL:absoluteUrl settings:recordSetting error:&audioRecorderError] autorelease];
NSLog(#"%s audioRecorder created=%#",__func__,self.audioRecorder);
BOOL isDurationAccepted=[self.audioRecorder recordForDuration:1800.0];
BOOL record=[self.audioRecorder record];
[recordSetting release];
recordSetting=nil;
//if(error !=NULL) *error=audioRecorderError;
return nil;
}
-(id)playRecordedAudio:(NSUInteger)viewTag
{
NSURL *absoluteUrl=[NSURL fileURLWithPath:[[self getAbsoluteAudioFilePath:viewTag] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding ]];
NSError *audioPlayerError=nil;
recordingView.audioPlayer=[[[AVAudioPlayer alloc] initWithContentsOfURL: absoluteUrl error:&audioPlayerError] autorelease] ;
recordingView.audioPlayer.delegate=self;
[recordingView.audioPlayer prepareToPlay];
recordingView.audioPlayer.numberOfLoops=-1;
UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_Speaker;
AudioSessionSetProperty (kAudioSessionProperty_OverrideAudioRoute,sizeof (audioRouteOverride),&audioRouteOverride);
[recordingView.audioPlayer play];
return nil;
}
The above code perfectly working as I expected. But when I am looking for recording->playing, again recording(before finishing currently playing audio) this time currently playing audio coming to microphone(ear phones) instead of playing through speakers.
Now the problem is, I am unable to record and play simultaneously. I am expecting record(even audio playing) always through earphones and multiple audios play through phone speaker.
Please suggest me changes in my above code to come out of this situation. All your comments helpful.
Set AVAudioSessionCategoryPlayAndRecord for playing those recording
I was googling this problem for many days but I didn't find a solution for my simple problem.
I'm trying to play a video while recording sound. Recording sound works fine without the video. And the video works fine without the recording.
As soon as I put both together, the recording throws values which do not react on sound and the video is not playing anymore.
By the way this happens only on my device (iPhone 4 / iOS5). In the simulator everything works fine.
This is a reduced version of my code which is in a ViewController.
// Audio Recorder
NSURL *nullUrl = [NSURL fileURLWithPath:#"/dev/null"];
NSDictionary *settings = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithFloat:44100.0], AVSampleRateKey,
[NSNumber numberWithInt:kAudioFormatAppleLossless], AVFormatIDKey,
[NSNumber numberWithInt:1], AVNumberOfChannelsKey,
[NSNumber numberWithInt:AVAudioQualityMax], AVEncoderAudioQualityKey,
nil];
NSError *recorderError;
_recorder = [[AVAudioRecorder alloc] initWithURL:nullUrl settings:settings error:&recorderError];
if (_recorder)
{
[_recorder prepareToRecord];
_recorder.meteringEnabled = YES;
[_recorder record];
_levelTimer = [NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:#selector(levelTimerCallback:) userInfo:nil repeats:YES];
}
// Video Player
NSURL *url = [[NSBundle mainBundle] URLForResource:#"Video"
withExtension:#"mp4"
subdirectory:nil];
_avPlayer = [[AVPlayer playerWithURL:url] retain];
_avPlayerLayer = [[AVPlayerLayer playerLayerWithPlayer:_avPlayer] retain];
_avPlayerLayer.frame = self.view.layer.bounds;
[self.view.layer addSublayer:_avPlayerLayer];
[_avPlayer play];
The recorder simply outputs a value which is read in the levelTimerCallback method.
The video simply plays a short video.
I figured out that I have to set a category for the AVAudioSession.
If I implement this code after app start, the video is playing but the recorder still outputs the same values without reacting to sound.
NSError *error = nil;
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:&error];
[audioSession setActive:YES error:&error];
I could imagine that I might have to do something with AudioSessionSetProperty
UInt32 sessionCategory = kAudioSessionCategory_PlayAndRecord;
AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(sessionCategory), &sessionCategory);
UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_Speaker;
AudioSessionSetProperty (kAudioSessionProperty_OverrideAudioRoute,sizeof (audioRouteOverride),&audioRouteOverride);
But that doesn't change anything.
Do you have an idea what I am missing or how to set up the AudioSession properly?
Thank you for every hint on that!
Cheers,
Raphael ;)
I had the same problem (except I'm using the AVPlayer to play audio and not video though). I suggest you start listening for audio routing changes and you will see the route change multiple times when the AVPlayer and AVAudioRecorder are started at the same time.
AudioSessionAddPropertyListener(kAudioSessionProperty_AudioRouteChange,
audioRouteChangeListenerCallback,
self);
In the callback print out the category and route (kAudioSessionProperty_AudioRoute). The only way I could get the audio playback and record to work simultaneously was if:
The audio session was AVAudioSessionCategoryPlayAndRecord and kAudioSessionProperty_OverrideCategoryMixWithOthers property was true.
I initiated the playback before the recording. After AVAudioRecorder prepareToRecord is called, don't start recording until you get the audio route change. Even though it does not fail if you start recording earlier, the audio route change seems to make the recording fail.
I tried other combinations, but this is the only one that works every time.
I am recording voice on iPhone using AVAudioRecorder and these are my recorder settings:
NSMutableDictionary* recordSetting = [[NSMutableDictionary alloc] init];
[recordSetting setValue :[NSNumber numberWithInt:kAudioFormatAppleIMA4] forKey:AVFormatIDKey];
[recordSetting setValue:[NSNumber numberWithFloat:16000.0] forKey:AVSampleRateKey];
[recordSetting setValue:[NSNumber numberWithInt:1] forKey:AVNumberOfChannelsKey];
The problem is that I can't hear the recorded voice without headphones.
I want to be able to hear the voice without headphones also.
How should I change my code?
You have to set the AVAudiosession category to AVAudioSessionCategoryPlayback before playing the sound
Another thing to consider is that if you do not set up your AVAudioSession for playback, then the silence ringer switch on the side of the phone will make your audio not play over the speaker. In order for you to be able to play sound with a silenced phone make sure you are using something like this before triggering your AVAudioPlayer to play the file:
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryPlayback error:nil];
[session setActive:YES error:nil];
Maybe force-routing the audio output to the speaker will help?
- (void) forceRouteAudioToSpeaker
{
UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_Speaker;
AudioSessionSetProperty(kAudioSessionProperty_OverrideAudioRoute,
sizeof(audioRouteOverride), &audioRouteOverride);
}
Don’t forget to include <AudioToolbox/AudioServices.h>. On second thought, switching the audio category to plain playback should also do the trick, and you say it didn’t…
I have a AVAudioRecorder, which gets initialized like:
_recorder = [AVAudioRecorder alloc];
NSMutableDictionary *recordSettings = [[[NSMutableDictionary alloc] initWithCapacity:2] autorelease];
[recordSettings setObject:[NSNumber numberWithInt:kAudioFormatMPEG4AAC] forKey: AVFormatIDKey];
[recordSettings setObject:[NSNumber numberWithInt:AVAudioQualityLow] forKey: AVEncoderAudioQualityKey];
NSURL *url = [NSURL fileURLWithPath:[self getActualSoundPath]];
[_recorder initWithURL:url settings:recordSettings error:nil];
[_recorder setDelegate:self];
This code works perfectly in my Simulator and on my iPhone 3GS but not on the older iPhone 3G...
What is the problem on that?
Thanks
Markus
The iPhone 3G doesnt have support for hardware AAC encoding like the 3GS/4/4S. You could use linear PCM, but thats not compressed.
See this thread for supported formats: How can I record AMR audio format on the iPhone?
Apple lossless (kAudioFormatAppleLossless) and iLBC (kAudioFormatiLBC) seems to work fine on the iPhone 3G.
kAudioFormatAppleIMA4 is recommended to use, but its not recognized by mobile Safari.
/Marcus