Swift iOS Text To Speech 无法在循环中使用 "delay"

Swift iOS Text To Speech not working with "delay" in loop

我正在尝试让 iOS 文本到语音合成器 "say" 一个短语列表,短语之间的延迟可变。例如,我可能希望它说 "Hello",然后等待 5 秒,然后 "Is anyone there?",然后等待 10 秒,然后说 "Hello?"...等等。

我在下面做了一个简单的例子来说明我正在尝试做的事情。我知道语音合成器正在说话,额外的话语被添加到队列中并按照收到的顺序说话。

我已经尝试了很多方法来实现这种循环延迟。使用 print 语句测试延迟确认它们正在工作,但它们似乎干扰了文本语音功能,该功能说第一个短语但等到 for 循环完成后再说其余部分。我认为任何这些类型的延迟都会起作用,因为我假设语音合成器是事件驱动的。

我希望得到一些帮助,或者至少了解它为什么不起作用。谢谢!

这里是示例代码:iPhone6模拟器,Xcode7.3

import UIKit
import AVFoundation

class ViewController: UIViewController {

    let speechSynthesizer = AVSpeechSynthesizer()
    var phraseArray: [String] = ["One", "Two", "Three", "Four", "Five", "Six", "Seven"]

    override func viewDidLoad() {
        super.viewDidLoad()
        for phrase in phraseArray{
            let speechUtterance = AVSpeechUtterance(string: phrase)
            speechSynthesizer.speakUtterance(speechUtterance)

            //"delay()" goes here.  It needs to be a variable length delay.

        }
    }
}

以下是我尝试过的一些延迟方法:

  1. 将 class 设置为语音合成器的委托,并 运行 一个 while 循环,直到合成器完成。

  2. 基于时间的延迟:referenceDate = NSDate() while(NSDate().timeIntervalSinceDate(referenceDate) < 0.5) {}

  3. 我尝试了 "delay" 堆栈解决方案,例如:

    func delay(delay:Double, closure:()->()) { dispatch_after( dispatch_time( DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC)) ), dispatch_get_main_queue(), closure) }

  4. 睡眠()

这样的事情怎么样:

import UIKit
import AVFoundation

func delay(_ delay:Double, closure:@escaping ()->()) {
    let when = DispatchTime.now() + delay
    DispatchQueue.main.asyncAfter(deadline: when, execute: closure)
}

class ViewController: UIViewController {

    let speechSynthesizer = AVSpeechSynthesizer()

    override func viewDidLoad() {
        super.viewDidLoad()

        speak([("Hello", 5.0), ("Is there anyone there?", 10.0), ("Hello?", 0.0)])
    }

    func speak(_ phrases: [(phrase: String, wait: Double)]) {
        if let (phrase, wait) = phrases.first {
            let speechUtterance = AVSpeechUtterance(string: phrase)
            speechSynthesizer.speak(speechUtterance)
            let rest = Array(phrases.dropFirst())
            if !rest.isEmpty {
                delay(wait) {
                    self.speak(rest)
                }
            }
        }
    }    
}

备注:

  • 一个元组数组被传递给speak。元组对包含要说的短语和在说下一个短语之前等待的延迟。
  • speak 从数组中取出第一项,说出短语并在等待延迟后将数组的其余部分(如果不为空)再次传递给 speak
  • delay 由@matt 编写,来自 here

由于最后的延迟没有任何用处,您可以将其反过来,让第一个短语在延迟后说出。

func speak(_ phrases: [(wait: Double, phrase: String)]) {
    if let (wait, phrase) = phrases.first {
        delay(wait) {
            let speechUtterance = AVSpeechUtterance(string: phrase)
            self.speechSynthesizer.speak(speechUtterance)
            let rest = Array(phrases.dropFirst())
            if !rest.isEmpty {
                self.speak(rest)
            }
        }
    }
}

你会像这样使用这个:

// Wait 5 seconds before starting...
speak([(5.0, "I'm sorry Dave."), (2.0, "I can't do that.")])