Problem:
When using Text-To-Speech, I want background audio to dim (or 'duck'), speak an utterance and then un-duck the background audio. It mostly works, however when trying to un-duck, it stays ducked without an error thrown in the deactivation.
Context & Code:
The method that speaks an utterance:
// Create speech utterance
AVSpeechUtterance *speechUtterance = [[AVSpeechUtterance alloc]initWithString:textToSpeak];
speechUtterance.rate = instance.speechRate;
speechUtterance.pitchMultiplier = instance.speechPitch;
speechUtterance.volume = instance.speechVolume;
speechUtterance.postUtteranceDelay = 0.005;
AVSpeechSynthesisVoice *voice = [AVSpeechSynthesisVoice voiceWithLanguage:instance.voiceLanguageCode];
speechUtterance.voice = voice;
if (instance.speechSynthesizer.isSpeaking) {
[instance.speechSynthesizer stopSpeakingAtBoundary:AVSpeechBoundaryImmediate];
}
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
NSError *activationError = nil;
[audioSession setActive:YES error:&activationError];
if (activationError) {
NSLog(@"Error activating: %@", activationError);
}
[instance.speechSynthesizer speakUtterance:speechUtterance];
Then deactivating it when speechUtterance
is finished speaking:
- (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer didFinishSpeechUtterance:(AVSpeechUtterance *)utterance
{
dispatch_queue_t myQueue = dispatch_queue_create("com.company.appname", nil);
dispatch_async(myQueue, ^{
NSError *error = nil;
if (![[AVAudioSession sharedInstance] setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:&error]) {
NSLog(@"Error deactivating: %@", error);
}
});
}
Setting the app's audio category in the App Delegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
NSError *setCategoryError = nil;
[audioSession setCategory:AVAudioSessionCategoryPlayback
withOptions:AVAudioSessionCategoryOptionDuckOthers error:&setCategoryError];
}
What I have tried:
The ducking/unducking works when I deactivate the AVAudioSession
after a delay:
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 0.2 * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_queue_create("com.company.appname", nil), ^(void){
NSError *error = nil;
if (![[AVAudioSession sharedInstance] setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:&error]) {
NSLog(@"Error deactivating: %@", error);
}
});
However, the delay is noticeable and I get an error in the console:
[avas] AVAudioSession.mm:1074:-[AVAudioSession setActive:withOptions:error:]: Deactivating an audio session that has running I/O. All I/O should be stopped or paused prior to deactivating the audio session.
Question:
How can I combine AVSpeechSynthesizer
with ducking of background audio properly?
EDIT: Apparently the issue stems from using postUtteranceDelay
on AVSpeechUtterance
, that causes the music to keep being dimmed. Removing that property fixes the issue. However, I need postUtteranceDelay
for some of my utterances, so I have updated the title.
the ducking worked (started and stopped) without any issue/error using your code while listening to Spotify. i used a iPhone 6S on iOS 9.1 so it is possible that this is an iOS 10 issue.
i would recommend removing the dispatch wrap entirely as it shouldn't be necessary. this may resolve the issue for you.
working code sample is below, all i did was create a new project ("Single View Application") and changed my AppDelegate.m to look like this:
the only output from the console when running on a physical device is:
2016-12-21 09:42:08.484 DimOtherAudio[19017:3751445] Building MacinTalk voice for asset: (null)
UPDATE
setting the
postUtteranceDelay
property created the same issue for me.the documentation for
postUtteranceDelay
states this:it is pretty clear from the documentation that this value is only designed to be used when another utterance will be added. i confirmed that adding a second utterance which hasn't set
postUtteranceDelay
unducks the audio.