Avspeechsynthesizer Errors in iOS 10

AVSpeechSynthesizer is not working After use one time

You need to set the AVAudioSession for performing such tasks. Here is my working code. Hope this helps.

func speakText(voiceOutdata: String ) {
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playAndRecord, mode: .default, options: .defaultToSpeaker)
try AVAudioSession.sharedInstance().setActive(true, options: .notifyOthersOnDeactivation)
} catch {
print("audioSession properties weren't set because of an error.")
}

let utterance = AVSpeechUtterance(string: voiceOutdata)
utterance.voice = AVSpeechSynthesisVoice(language: "en-US")

let synth = AVSpeechSynthesizer()
synth.speak(utterance)

defer {
disableAVSession()
}
}

private func disableAVSession() {
do {
try AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation)
} catch {
print("audioSession properties weren't disable.")
}
}

An issue with AVSpeechSynthesizer, Any workarounds?

quite likely to be a bug, in that the delegate method synthesizer didCancelSpeechUtterance isn't called after the first utterance;

A workaround would be to chain the utterances rather than have them in an array and queue them up at once.

Use the delegate method synthesizer didFinishSpeechUtterance to increment an array pointer and speak the the next text from that array. Then when trying to stop the speech, set a BOOL that is checked in this delegate method before attempting to speak the next text.

For example:

1) implement the protocol in the view controller that is doing the speech synthesis

#import <UIKit/UIKit.h>
@import AVFoundation;
@interface ViewController : UIViewController <AVSpeechSynthesizerDelegate>

@end

2) instantiate the AVSpeechSynthesizer and set its delegate to self

speechSynthesizer   = [AVSpeechSynthesizer new];
speechSynthesizer.delegate = self;

3) use an utterance counter, set to zero at start of speaking

4) use an array of texts to speak

textArray           = @[@"Mary had a little lamb, its fleece",
@"was white as snow",
@"and everywhere that Mary went",
@"that sheep was sure to go"];

5) add delegate method didFinishSpeechUtterance to speak the next utterance from the array
of texts and increment the utterance counter

- (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer didFinishSpeechUtterance:(AVSpeechUtterance *)utterance{
if(utteranceCounter < utterances.count){
AVSpeechUtterance *utterance = utterances[utteranceCounter];
[synthesizer speakUtterance:utterance];
utteranceCounter++;
}
}

5) to stop speaking, set the utterance counter to the count of the texts array and attempt to get the synthesizer to stop

utteranceCounter = utterances.count;

BOOL speechStopped = [speechSynthesizer stopSpeakingAtBoundary:AVSpeechBoundaryImmediate];
if(!speechStopped){
[speechSynthesizer stopSpeakingAtBoundary:AVSpeechBoundaryWord];
}

6) when speaking again, reset the utterance counter to zero

AVSpeechSynthesizer error: An AVSpeechUtterance shall not be enqueued twice

When stopping the player, the utterances are definitely removed from the queue.

However, in your moveBackward function, you insert another AVSpeechUterrance at playQueue[0] whose complete array represents the player queue.

Assuming the stops happens with currentIndex = 2, the following snapshots prove that the same object is injected twice in the queue:

  • Copy backedQueue[1] that is a copy of playQueue[1] (same memory address).
    Sample Image

  • Insert backedQueue[1] at playQueue[0] (former playQueue[1] becomes new playQueue[2]).
    Sample Image

Unfortunately, as the system indicates, AVSpeechUtterance shall not be enqueued twice and that's exactly what you're doing here: objects at playQueue indexes 0 and 2 have the same memory address.

The last loop after inserting the new object at index 0 asks the speech synthesizer to put all the utterances in its all new queue... and two of them are the same.

Instead of copying the playedQueue into the backedQueue (both contain the same memory addresses to their objects) OR appending the same utterance in both arrays, I suggest to create different utterance instances to be put as follows:

    for i in 1...5 {

let stringNb = "number " + String(i) + " of the speech synthesizer."
let utterance = AVSpeechUtterance(string: stringNb)
playQueue.append(utterance)

let utteranceBis = AVSpeechUtterance(string: stringNb)
backedQueue.append(utteranceBis)
}

Following this piece of advice, you shouldn't meet the error AVSpeechUtterance shall not be enqueued twice.

AVSpeechSynthesizer freezes iOS app for a few seconds

Looks like it's connected with the internal AVSpeechSynthesizer queue. You can try to make it manage the thing in background, since there's no mention about AVSpeechSynthesizer is main thread only.
Do so by adding the last line (synthesizer.speak(utterance)) call to the background queue capturing references to the synthesizer and utterance objects like that:

DispatchQueue.global(qos: .background).async { 
synthesizer.speak(utterance)
}

I think the reason why you have ui blocked is because synthesizer instance has to block current thread to speak. So you must wait till synthesizer finish speaking before being deallocated on the exit from your speak() method.

Speech Synthesis on iOS weird errors on loading, and no concurrency

As for #1, it's probably not gonna happen. The speech synthesizer is a system shared resource, so how the system handles scheduling multiple requests is out of our control as clients of the API. (Note that if you reuse the same synthesizer, it queues up extra utterances, but if you create multiple synthesizers, it fails to speak utterances that are requested while another synthesizer is speaking.)

Dunno about #2, sorry. Looks like diagnostic text, not necessarily an error. Probably worth filing a bug about, since they probably don't want to be logging diagnostics when there's no actual problem.

Bonus answer: You can use functional programming to make the selection of voice a bit shorter:

let voice = AVSpeechSynthesisVoice.speechVoices().first(where: { $0.name == "Arthur" })


Related Topics



Leave a reply



Submit