Search code examples
iosswiftavspeechsynthesizer

Interrupt AVSpeechUtterance and Resume Using WillSpeakRange Delegate in Swift


Using AVSpeechSynthesizer, if an utterance is going to be very long, I'd like to prompt user whether to cut it off or hear to end.

I'm tracking the length in the willSpeakRange delegate.

At a certain point, I would like to pause or stop the speech. And then ask the user whether to continue.

If I use the Pause functionality of the synthesizer on the original utterance, it does not seem to allow me to interrupt with "Do you want to hear to the end".

On the other hand, if I use the Stop functionality, it allows me to ask "Do you want to hear to the end" but we lose the place in the original utterance.

How can I pause or stop the original utterance and then either resume or capture the remaining text to start a new utterance that effectively completes the original one.

This is the code I am using:

let count = 0
func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, willSpeakRangeOfSpeechString characterRange: NSRange, utterance: AVSpeechUtterance) {
count = count+1
if (count==limit) {
count = 0
//Pause option does not seem to allow interjection
self.voice.pauseSpeaking(at: AVSpeechBoundary.immediate)
//stop option loses place
self.voice.stopSpeaking(at: .immediate)
//NEED TO CAPTURE UNSPOKEN TEXT IF I USE STOP OPTION
let speechString = utterance.speechString
let newRange = Range(characterRange, in: speechString)
//Store the remaining text to a variable       
remainingText = ???
self.speakSomething(text:",,Should I stop or continue?")
}

Solution

  • If I use the Pause functionality of the synthesizer on the original utterance, it does not seem to allow me to interrupt with "Do you want to hear to the end".

    The pauseSpeaking method doesn't impact the queue to be spoken by the speech synthesis. It just pauses as long as you wish and allows to resume from where you left off but you can't insert any new utterance on the fly at this point following this pattern.

    On the other hand, if I use the Stop functionality, it allows me to ask "Do you want to hear to the end" but we lose the place in the original utterance.

    The stopSpeaking method removes any utterances yet to be spoken from the synthesizer’s queue, that's why you can insert your new utterance but lose everything that was previously scheduled.

    How can I pause or stop the original utterance and then either resume or capture the remaining text to start a new utterance that effectively completes the original one.

    According to what I explained above, you have no choice but stopping the original utterance for asking your question. I suggest this pattern:

    1. Get the current spoken utterance thanks to the AVSpeechSynthesizerDelegate protocol.
    2. Fire the stopSpeaking synthesizer method that will remove from the queue the utterances that haven't been spoken yet.
    3. Create the utterance to ask the choice question and make the synthesizer read it out.
    4. If the choice is to resume, create a new utterance from the location found at 1. until the end of your text and then resume.

    If you don't know how to capture the unspoken text, take a look at this example (ObjC, Swift) that shows up the spoken word in bold text.

    Add the following code snippet in the delegate method of this example to catch your remaining text for instance:

    func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer,
                           willSpeakRangeOfSpeechString characterRange: NSRange,
                           utterance: AVSpeechUtterance) {
    
        let remainingText = utterance.speechString.dropFirst(characterRange.location)
        //...
    }
    

    Following these steps, you could Interrupt AVSpeechUtterance and Resume Using WillSpeakRange Delegate in Swift as mentioned in your post title.