iOS avaudiosession not starting on certain iOS hardware - ios

I have a strange situation with an AVAudioRecorder used to record audio in my iOS app. I have found some rare iPad2 devices where the audio session will not start recording. The iOS app code does record fine for all other types of iPod Touches, iPhones, and iPads as well as in the simulator. It only seems to not be able to record in a very few cases. Does anyone know why this might be happening?
Here is the coding information.
In my .h file I have the AVAudioRecorder set up...
RVC.h
#interface RVC : UIViewController <AVAudioRecorderDelegate, AVAudioPlayerDelegate>{
AVAudioRecorder *audioRecorder;
}
#property (nonatomic, retain) AVAudioRecorder *audioRecorder;
RVC.m
#implementation RVC
#synthesize audioRecorder;
// Set up the file path and name, where the path is to the temporary directory
soundFile = [NSURL fileURLWithPath:[tempDir stringByAppendingFormat:#"theSoundFile.m4a"]];
// Set up the settings for the recording
soundSetting = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithFloat: 44100.0],AVSampleRateKey,
[NSNumber numberWithInt: kAudioFormatMPEG4AAC], AVFormatIDKey,
[NSNumber numberWithInt: 2], AVNumberOfChannelsKey,
[NSNumber numberWithInt: AVAudioQualityHigh], AVEncoderAudioQualityKey, nil];
// Make the AVAudioRecorder with the filename and the sound settings.
self.audioRecorder = [[AVAudioRecorder alloc] initWithURL:soundFile settings:soundSetting error:&error];
// Check to make sure the iOS device has audio hardware that can make a recording
BOOL audioHWAvailable = [[AVAudioSession sharedInstance] inputIsAvailable];
if (! audioHWAvailable) {
NSLog(#"No audio hardware is available.");
return;
}
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
// Make sure any output sound is played through the speakers.
UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_Speaker;
AudioSessionSetProperty (kAudioSessionProperty_OverrideAudioRoute,sizeof (audioRouteOverride),&audioRouteOverride);
[[AVAudioSession sharedInstance] setActive:YES error:nil];
// If the audiorecorder is properly set up, then start recording.
if (audioRecorder) {
[audioRecorder prepareToRecord];
// Start the recording
isRecording = [audioRecorder record];
}
else {
isRecording = FALSE;
NSLog(#"The audio recorder was not properly set up.");
}
if (isRecording) {
// The AVAudioSession is recording.
}
else {
// The AVAudioSession did not start so make some kind of log file.
NSLog(#"The recording was not started properly.");
// This is where THE PROBLEM SHOWS UP where it is having trouble with some versions of the iPad2, which I can not replicate in the simulators.
return;
}

You are passing in an error in the init call:
self.audioRecorder = [[AVAudioRecorder alloc] initWithURL:soundFile settings:soundSetting error:&error];
but you are never reading it. I would bet that on the failed cases, you are getting an error here, causing the audioRecorder to be nil.

Related

iOS AVAudioRecorder- how to record silence while user is on a phone call

This is the code I have right now for pausing a recording when a user receives a phone call and resuming it after they hang up.
- (void)handleAudioSessionInterruption:(NSNotification *)notification
{
NSNumber *interruptionType = [[notification userInfo] objectForKey:AVAudioSessionInterruptionTypeKey];
switch (interruptionType.unsignedIntegerValue) {
case AVAudioSessionInterruptionTypeBegan:{
_currentAudioSessionMode = EXAVAudioSessionModeInactive;
if (_audioRecorder.recording) {
[_audioRecorder pause];
[self demoteAudioSessionIfPossible];
}
} break;
case AVAudioSessionInterruptionTypeEnded:{
_currentAudioSessionMode = EXAVAudioSessionModeActive;
if (_audioRecorder && !_audioRecorder.recording) {
if (_allowsAudioRecording) {
[self promoteAudioSessionIfNecessary];
_audioRecorderShouldBeginRecording = true;
[_audioRecorder record];
}
_audioRecorderShouldBeginRecording = false;
}
} break;
default:
break;
}
What I would like to achieve is to keep recording, but capture pure silence while a user is on a phone call. This is how it works on Android and we care about the correct duration of the recording. Any idea how to do it?
#import <AVFoundation/AVFoundation.h>
Set up the audio session to use the AVAudioSessionCategoryPlayAndRecord category and activate it:
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
[audioSession setActive:YES error:nil];
Set up the audio recorder using the AVAudioRecorder class, specifying the URL where the audio file will be saved and the audio settings for the recording:
NSURL *url = [NSURL fileURLWithPath:#"/path/to/audio/file.m4a"];
NSDictionary *settings = #{
AVFormatIDKey: #(kAudioFormatMPEG4AAC),
AVSampleRateKey: #44100.0f,
AVNumberOfChannelsKey: #1,
};
AVAudioRecorder *recorder = [[AVAudioRecorder alloc] initWithURL:url settings:settings error:nil];
Start the recording by calling the record method on the audio recorder:
[recorder record];
To stop the recording, call the stop method on the audio recorder:
[recorder stop];
To record silence while the user is on a phone call, you can use the audioRecorderDidFinishRecording:successfully: delegate method to detect when the recording has stopped due to the phone call and start a new recording.
- (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag {
// Check if the recording was stopped due to a phone call
if (!flag) {
// Start a new recording
[recorder record];
}
}

AVAudioRecorder not saving recording

I am making an iOS game. One of the things I need to do is to allow the user to make a quick little audio recording. This all works, but the recording is only temporarily saved. So when the user closes the app and reopens it, the recording should be able to play again, but it doesn't, it gets deleted when I close the app. I don't understand what I am doing wrong. Below is my code:
I setup the AVAudioRecorder in the ViewDidLoad method like this:
// Setup audio recorder to save file.
NSArray *pathComponents = [NSArray arrayWithObjects:[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject], #"MyAudioMemo.m4a", nil];
NSURL *outputFileURL = [NSURL fileURLWithPathComponents:pathComponents];
// Setup audio session.
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
NSMutableDictionary *recordSetting = [[NSMutableDictionary alloc] init];
[recordSetting setValue:[NSNumber numberWithInt:kAudioFormatMPEG4AAC] forKey:AVFormatIDKey];
[recordSetting setValue:[NSNumber numberWithFloat:44100.0] forKey:AVSampleRateKey];
[recordSetting setValue:[NSNumber numberWithInt:2] forKey:AVNumberOfChannelsKey];
audio_recorder = [[AVAudioRecorder alloc] initWithURL:outputFileURL settings:recordSetting error:nil];
audio_recorder.delegate = self;
audio_recorder.meteringEnabled = YES;
[audio_recorder prepareToRecord];
I have got the AVAudio delegate methods, too:
-(void)audio_playerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
NSLog(#"Did finish playing: %d", flag);
}
-(void)audio_playerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError *)error {
NSLog(#"Decode Error occurred");
}
-(void)audio_recorderDidFinishRecording:(AVAudioPlayer *)recorder successfully:(BOOL)flag {
NSLog(#"Did finish recording: %d", flag);
}
-(void)audio_recorderEncodeErrorDidOccur:(AVAudioPlayer *)recorder error:(NSError *)error {
NSLog(#"Encode Error occurred");
}
When I want to play, record or stop the audio, I have made the following IBActions which are linked to UIButtons:
-(IBAction)play_audio {
NSLog(#"Play");
if (!audio_recorder.recording){
audio_player = [[AVAudioPlayer alloc] initWithContentsOfURL:audio_recorder.url error:nil];
[audio_player setDelegate:self];
[audio_player play];
}
}
-(IBAction)record_voice {
NSLog(#"Record");
if (!audio_recorder.recording) {
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setActive:YES error:nil];
// Start recording.
[audio_recorder record];
}
else {
// Pause recording.
[audio_recorder pause];
}
}
-(IBAction)stop_audio {
NSLog(#"Stop");
[audio_recorder stop];
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
[audioSession setActive:NO error:nil];
}
If you try my code you will see that it works, but it only seems to save the audio file temporarily.
What am I doing wrong? I thought I had used all the correct AVAudioRecorder methods?
To make a working recorder and save the recorded files, you need to:
Create a new audio session
Make sure microphone is connected/working
Start recording
Stop recording
Save the recorded audio file
Play the saved voice file
You're missing step 5 in your code, so the file that's just recorded is still available for you to play, but once you close the app, as it's not saved into an actual file somewhere in the app's directories, you lose it. You should add a method to save the recorded audio into a file so that you can access it any time later:
-(void) saveAudioFileNamed:(NSString *)filename {
destinationString = [[self documentsPath] stringByAppendingPathComponent:filename];
NSLog(#"%#", destinationString);
NSURL *destinationURL = [NSURL fileURLWithPath: destinationString];
NSDictionary *settings = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithFloat: 44100.0], AVSampleRateKey,
[NSNumber numberWithInt: kAudioFormatAppleLossless], AVFormatIDKey,
[NSNumber numberWithInt: 1], AVNumberOfChannelsKey,
[NSNumber numberWithInt: AVAudioQualityMax], AVEncoderAudioQualityKey,
nil];
NSError *error;
audio_recorder = [[AVAudioRecorder alloc] initWithURL:destinationURL settings:settings error:&error];
audio_recorder.delegate = self;
}
Unrelated to this problem, but a general thing to mention is that you must follow Apple's (Objective-C's) naming conventions when defining variables, etc. audio_recording in no way follows these guidelines. You could use something like audioRecording instead.
Ok so thanks for #Neeku so much for answering my question, certainly something to take into account but its still not solving the problem. I was searching around and I found this example which works perfectly and more to the point shows me that I was approaching this entire functionality the wrong way. I think another one of my problems is that my app would delete the previously saved audio file in the ViewDidload method. And as well as that I don't think I have used the AVAudioSession instances correctly.
Anyway the example I found is very useful and solves my problem perfectly, you can find it here: Record audio and save permanently in iOS
Hope that helps anyone else who is having a similar problem to me.

AVAudioRecorder blocks AVSpeechSynthesizer

I'm using Cordova 3.2 for text to speech and speech to text. Under iOS 7, AVSpeechSynthesizer is available and works very well. Here is the critical bit of the plugin:
self.synthesizer = [[AVSpeechSynthesizer alloc] init];
self.synthesizer.delegate = self;
NSString * toBeSpoken=[command.arguments objectAtIndex:0];
NSNumber * rate=[command.arguments objectAtIndex:1];
NSString * voice=[command.arguments objectAtIndex:2];
NSNumber * volume=[command.arguments objectAtIndex:3];
NSNumber * pitchMult=[command.arguments objectAtIndex:4];
AVSpeechUtterance *utt =[[AVSpeechUtterance alloc] initWithString:toBeSpoken];
utt.rate= [rate floatValue]/4;// this is to slow down the speech rate
utt.volume=[volume floatValue];
utt.pitchMultiplier=[pitchMult floatValue];
utt.voice=[AVSpeechSynthesisVoice voiceWithLanguage:voice];
[self.synthesizer speakUtterance:utt];
The problem occurs after the text is spoken. Using the Cordova Media call to record (AVAudioRecorder) the response for voice to text conversion does something to disrupt the synthesizer output.
Some things that I've noticed during my attempts to figure this out:
Running in the simulator, there is no problem. In fact, I have to be careful to wait for the speech to end before recording, otherwise the record will pick the output through the microphone.
On the iPad 3 w/ iOS 7+, starting the recording will pause the synthesizer output until the recorded file is played back. The media reference to the file is released after the successful recording.
After recording, the synthesizer delegate receives responses:
TTSPlugin did start speaking
TTSPlugin will speak in range of speech string.
TTSPlugin will speak in range of speech string.
TTSPlugin will speak in range of speech string.
TTSPlugin did cancel speaking
Canceling the speech synthesize clears the utterance queue.
My goal is to be able to have a conversation with the app. I'm not able to find where the interference is. Help?
EDIT
I solved the problem. The culprit was AVAudioSession, which the Media plugin in Cordova was managing. I hadn't done multiple audio sources before so this was a stumper.
I added these to my TTS plugin to manage AVAudioSession and activate it as needed. Everything is fine now.
// returns whether or not audioSession is available - creates it if necessary
- (BOOL)hasAudioSession
{
BOOL bSession = YES;
if (!self.avSession) {
NSError* error = nil;
self.avSession = [AVAudioSession sharedInstance];
if (error) {
// is not fatal if can't get AVAudioSession , just log the error
NSLog(#"error creating audio session: %#", [[error userInfo] description]);
self.avSession = nil;
bSession = NO;
}
}
return bSession;
}
- (void)setAudioSession
{
if ([self hasAudioSession]) {
NSError* __autoreleasing err = nil;
NSNumber* playAudioWhenScreenIsLocked = 0;
BOOL bPlayAudioWhenScreenIsLocked = YES;
if (playAudioWhenScreenIsLocked != nil) {
bPlayAudioWhenScreenIsLocked = [playAudioWhenScreenIsLocked boolValue];
}
NSString* sessionCategory = bPlayAudioWhenScreenIsLocked ? AVAudioSessionCategoryPlayback : AVAudioSessionCategorySoloAmbient;
[self.avSession setCategory:sessionCategory error:&err];
if (![self.avSession setActive:YES error:&err]) {
// other audio with higher priority that does not allow mixing could cause this to fail
NSLog(#"Unable to play audio: %#", [err localizedFailureReason]);
}
}
}
startRecordingAudio method in Media(CDVSound.m) set AVAudioSession as "AVAudioSessionCategoryRecord".
In my case, I just I added following 1 line, it works for me.
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
[self.synthesizer speakUtterance:utt];

iOS - Unable to play a sound in background

I'm currently trying to implement a system like the alarm one to alert the phone when an event occurred (in my case a bluetooth event). I want this alert to occur even if the phone is in silent and in background.
I create a local notification but i can't get sound played if the phone is in silent mode (which seems to be normal since we put the phone in silent).
So i tried to manage the sound by myself and i'm struggling with playing sound in background. So far i implement the "App plays audio or streams audio/video using AirPlay" key in my plist and i'm using this code.
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
NSError *error = nil;
BOOL result = [audioSession setActive:YES error:&error];
if ( ! result && error) {
NSLog(#"Error For AudioSession Activation: %#", error);
}
error = nil;
result = [audioSession setCategory:AVAudioSessionCategoryPlayback error:&error];
if ( ! result && error) {
NSLog(#"Error For AudioSession Category: %#", error);
}
if (player == nil) {
NSString *path = [[NSBundle mainBundle] pathForResource:#"bell" ofType:#"wav"];
NSURL *url = [NSURL fileURLWithPath:path];
NSError *err = nil;
player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&err];
if(err)
{
NSLog(#"Error Player == %#", err);
}
else {
NSLog(#"Play ready");
}
}
[player prepareToPlay];
[player setVolume:1.0];
if([player play])
{
NSLog(#"YAY sound");
}
else {
NSLog(#"Error sound");
}
The sound works great in foreground, even in silent mode, but i got no sound at all in background. Any ideas ?
Thanks
EDIT:
Finally i got it working with the above code. The only missing point is that i was trying to play the sound in somewhat appeared to be a different thread, when i play it right in my bluetooth event and not my function call it's working.
An app that plays audio continuously (even while the app is running in the background) can register as a background audio app by including the UIBackgroundModes key (with the value audio) in its Info.plist file. Apps that include this key must play audible content to the user while in the background.
Apple reference "Playing Background Audio"
Ensuring That Audio Continues When the Screen Locks
Found here
I'm not sure if your problem is that you have not configure correctly the audio session output. I had similar problem while playing .wav in my app. I only listened them with earphones. This method helped me:
- (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 audioRouteOverride:
success = [session setCategory:AVAudioSessionCategoryPlayAndRecord
error:&error];
if (!success) NSLog(#"AVAudioSession error setting category:%#",error);
//set the audioSession override
success = [session overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker
error:&error];
if (!success) NSLog(#"AVAudioSession error overrideOutputAudioPort:%#",error);
//activate the audio session
success = [session setActive:YES error:&error];
if (!success) NSLog(#"AVAudioSession error activating: %#",error);
else NSLog(#"audioSession active"); }
Try it out!
You need to make couple of changes in plist file.
for enabling sound when the app enter's Background.
1) Set Required background mode to App plays audio
2) set Application does not run in background to YES.
NSError *setCategoryErr = nil;
NSError *activationErr = nil;
[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayback error:&setCategoryErr];
[[AVAudioSession sharedInstance] setActive:YES error:&activationErr];
Then, you need to write these much code in AppDelegate
Now, you can easily run audio while phone screen locks or in background.
It works fine for me. :)
for more please refer to Playing Audio in background mode and Audio Session Programming Guide

iOS play audio success on simulator but fail on device

I use audio recorder to record and create file in the document directory, then I play it using audio player. I am able to record it on both simulator and device, but I can't play it on device only.
Notice that
1. I don't play sound from files, I play from NSData (I store music into database, and retrieve them to play)
2. It works fine on simulator but failed on device.
Here is my code for playing audio
- (void)playAudioWithData:(NSData *)data {
[self stopPlayingAudio];
[self stopRecordingAudio];
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
[audioSession setActive:YES error:nil];
self.audioPlayer = [[AVAudioPlayer alloc] initWithData:data error:nil];
[self.audioPlayer play];
}
this is the setting for recording:
NSDictionary *audioSettings = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithFloat:44100],AVSampleRateKey,
[NSNumber numberWithInt: kAudioFormatLinearPCM],AVFormatIDKey,
[NSNumber numberWithInt: 1],AVNumberOfChannelsKey,
[NSNumber numberWithInt:AVAudioQualityMedium],AVEncoderAudioQualityKey,nil];
Instead of using AVAudioSessionCategoryPlayAndRecord try setting the audio session to AVAudioSessionCategoryRecord when recording and then to AVAudioSessionCategoryPlayback when you want to play back what you just recorded.
I experienced the same thing in my app when recording and playing back audio and this resolved the issue.

Resources