I am stuck a little bit.
Here is my code:
let speaker = AVSpeechSynthesizer()
var playQueue = [AVSpeechUtterance]() // current queue
var backedQueue = [AVSpeechUtterance]() // queue backup
...
func moveBackward(_ currentUtterance:AVSpeechUtterance) {
speaker.stopSpeaking(at: .immediate)
let currentIndex = getCurrentIndexOfText(currentUtterance)
// out of range check was deleted
let previousElement = backedQueue[currentIndex-1]
playQueue.insert(previousElement, at: 0)
for utterance in playQueue {
speaker.speak(utterance) // error here
}
}
According to the docs AVSpeechSynthesizer.stopSpeaking(at:)
:
Stopping the synthesizer cancels any further speech; in constrast with when the synthesizer is paused, speech cannot be resumed where it left off. Any utterances yet to be spoken are removed from the synthesizer’s queue.
I always get the error(AVSpeechUtterance shall not be enqueued twice), when I insert an AVSpeechUtterance in the AVSpeechSynthesizer
queue. But it should stop according to the doc.
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).
Insert backedQueue[1]
at playQueue[0]
(former playQueue[1]
becomes new playQueue[2]
).
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.