Search code examples
iosswiftavspeechsynthesizer

How can I make speech occur immediately for each iteration of a while loop in Swift?


Here is my playground code:

import AVFoundation


var speechsynth: AVSpeechSynthesizer = AVSpeechSynthesizer()
let wordsToSpeak = ["word one","word two","word three","word four"]

let endTime = NSDate().dateByAddingTimeInterval(10)

while endTime.timeIntervalSinceNow > 0 { 

    //workaround for iOS8 Bug

    var beforeSpeechString : String = " "
    var beforeSpeech:AVSpeechUtterance = AVSpeechUtterance(string: beforeSpeechString)
    speechsynth.speakUtterance(beforeSpeech)

    //realstring to speak
    var speechString: String = wordsToSpeak[0]

    var nextSpeech:AVSpeechUtterance = AVSpeechUtterance(string: speechString)
    nextSpeech.voice = AVSpeechSynthesisVoice(language: "en-US")
    nextSpeech.rate = AVSpeechUtteranceMinimumSpeechRate
    speechsynth.speakUtterance(nextSpeech)
}

Currently, the speaking starts AFTER the while loop completes.

How can I make it speak DURING each iteration and finish speaking before moving on to the next iteration of the loop?


Solution

  • Treat each word as a task, trigger the next task in Delegate's didFinishSpeechUtterance method.

    import UIKit
    import AVFoundation
    
    class ViewController: UIViewController, AVSpeechSynthesizerDelegate {
    
        var queue: dispatch_queue_t = dispatch_queue_create(
            "com.test.whatever.queue", DISPATCH_QUEUE_SERIAL)
        var index: Int = 0
        let words: [String] = ["word one","word two","word three","word four"]
        var speechsynth: AVSpeechSynthesizer = AVSpeechSynthesizer()
    
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view, typically from a nib.
            speechsynth.delegate = self
            self.speechCurrentWord()
        }
    
        func speechCurrentWord(){
            dispatch_async(queue, { () -> Void in
                var beforeSpeechString : String = " "
                var beforeSpeech:AVSpeechUtterance = AVSpeechUtterance(string: beforeSpeechString)
                self.speechsynth.speakUtterance(beforeSpeech)
    
                var nextSpeech:AVSpeechUtterance = AVSpeechUtterance(string: self.words[self.index])
                nextSpeech.voice = AVSpeechSynthesisVoice(language: "en-US")
                nextSpeech.rate = AVSpeechUtteranceMinimumSpeechRate
                self.speechsynth.speakUtterance(nextSpeech)
            })
        }
    
        func speechSynthesizer(synthesizer: AVSpeechSynthesizer!, didFinishSpeechUtterance utterance: AVSpeechUtterance!) {
            index++
            if index < self.words.count {
                self.speechCurrentWord()
            }
        }
    }