How to stop AVAudioSession sequentially after AVSpeechUtterance

What I want to do is allow my application to make a statement using AVSpeechSynthesizer , while background audio applications produce sound. While my application is talking, I would like the sound of the background applications to "fade" and then return to the original volume after my application has finished speaking.

In my AudioFeedback class AudioFeedback I initialize the AVAudioSessions setting as follows:

  self.session = [AVAudioSession sharedInstance]; NSError *error; [self.session setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionDuckOthers error:&error]; 

When I want to make a new statement, I do the following. I followed the suggestion Problem with AVSpeechSynthesizer, any workarounds? to create a new AVSpeechSynthesizer each time to β€œmake sure” that the cancellation is always accepted (this seems to work, I'm not sure why).

 - (AVSpeechUtterance *) utteranceWithString: (NSString *) string { AVSpeechUtterance *utterance = [AVSpeechUtterance speechUtteranceWithString:string]; utterance.voice = [AVSpeechSynthesisVoice voiceWithLanguage:@"en-ES"]; [utterance setRate:(AVSpeechUtteranceDefaultSpeechRate+AVSpeechUtteranceMinimumSpeechRate)/2.0]; return utterance; } - (void) sayString: (NSString *) string cancelPrevious: (BOOL) cancelPrevious { [self.session setActive:enabled error:nil]; if (cancelPrevious) { AVSpeechSynthesizer *oldSynthesizer = self.voice; self.voice = nil; [oldSynthesizer stopSpeakingAtBoundary:AVSpeechBoundaryImmediate]; self.voice = [[AVSpeechSynthesizer alloc] init]; self.voice.delegate = self; } // Keep track of the final utterance, we'll use this to determine whether or not we should stop the audio session self.finalUtterance = [self utteranceWithString:string]; [self.voice speakUtterance:self.finalUtterance]; } 

In my AVSpeechSynthesizer delegation method, I check if I should end the audio session in order to return the background sound to the normal volume if the current AVSpeechSynthesizer and the current AVSpeechUtterance match the last known synthesizer and pronunciation.

 -(void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer didFinishSpeechUtterance:(AVSpeechUtterance *)utterance { NSError *error; // Only stop the audio session if this is the last created synthesizer if (synthesizer == self.voice && self.finalUtterance == utterance) { if ([self.session setActive:enabled error:&error]]) { NSLog(@"Stopped the audio session: Speech synthesizer still speaking %d", synthesizer.speaking); } else { NSLog(@"ERROR failed to stop the audio session: %@. Speech synthesizer still speaking %d", error, synthesizer.speaking); } } } 

The problem I am facing is that sometimes the audio session stops without problems, and in other cases the audio session does not stop with the following error:

Domain Error = NSOSStatusErrorDomain Code = 2003329396 "Operation could not be completed (OSStatus error 2003329396.)"

I am not sure how to guarantee that I can stop AVAudioSession. I tried to keep calling [[AVAudioSession sharedInstance] setActive:NO error:&error] until I can stop the audio session, but that just doesn't work. Any help would be greatly appreciated. Thanks!

+6
source share
2 answers

I found that if I try again in a couple of seconds, it will work. I am using this code.

 -(void)configureAVAudioSession:(bool)active { AVAudioSession *audioSession = [AVAudioSession sharedInstance]; if (deactivationAttempt < 3) { // This doesn't always work first time, but don't want to keep trying forever if it decides not to work ever. NSError *activationError = nil; success = [audioSession setActive:active error:&activationError]; if (!success) { NSLog(@"(de)activation ERROR"); ++deactivationAttempt; [self performSelector:@selector(configureAVAudioSession:) withObject:false afterDelay:2.0]; } else { // Success! deactivationAttempt = 0; } } else { // Failed, but reset the counter in case we have more luck next time deactivationAttempt = 0; } } 

From the docs:

Session deactivation will fail if any related audio objects (such as queues, converters, players or recorders) are currently running.

I think it takes time to free a statement to free itself from the line.

+1
source

I had the same problem and I believe that I found the reason for this. For me, if the phrase I say is too short, it gives an error. If it is β€œlong enough,” no error occurs.

I tested

 NSString *phraseToSay = [NSString stringWithFormat:@"One Two Three Four Five"]; // Gives the error NSString *phraseToSay = [NSString stringWithFormat:@"One Two Three Four Five Six"]; // No error 

The only solution I found was to make termToSay longer.

0
source

All Articles