控制循环执行
Controlling the loop execution
我正在尝试用时间间隔发音句子,但问题是在合成器第一次发音后,循环会直接运行到最后。台词很好,但中间没有停顿。
如何在语音合成任务完成后才循环切换到下一个项目?
编辑:也许,循环每次都等待 didFinish
,然后 didFinish
告诉循环什么时候可以继续?
let speaker = Speaker()
let capitals = ["Canberra is the capital of Australia", "Seoul is the capital of South Korea", "Tokyo is the capital of Japan", "Berlin is the capital of Germany"]
var body: some View {
Button("Play Sound") {
playSound()
}
}
func playSound() {
for item in 0..<capitals.count {
let timer = Timer.scheduledTimer(withTimeInterval: 20, repeats: false) { timer in
speaker.speak("\(capitals[item])")
print("I am out")
}
}
}
...
import AVFoundation
class Speaker: NSObject {
let synth = AVSpeechSynthesizer()
override init() {
super.init()
synth.delegate = self
}
func speak(_ string: String) {
let utterance = AVSpeechUtterance(string: string)
utterance.voice = AVSpeechSynthesisVoice(language: "en-GB")
utterance.rate = 0.5
synth.speak(utterance)
}
}
extension Speaker: AVSpeechSynthesizerDelegate {
func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance) {
print("all done")
}
}
您始终可以为此使用 Combine
import Combine
let speaker = Speaker()
let capitals = ["Canberra is the capital of Australia", "Seoul is the capital of South Korea", "Tokyo is the capital of Japan", "Berlin is the capital of Germany"]
var playerCancellable: AnyCancellable? = nil
Button("Play Sound") {
playSound()
}
func playSound() {
// Fairly standard timer publisher. The call to .autoconnect() tells the timer to start publishing when subscribed to.
let timer = Timer.publish(every: 20, on: .main, in: .default)
.autoconnect()
// Publishers.Zip takes two publishers.
// It will only publish when there is a "symmetrical" output. It behaves in a similar manner as `zip` on sequences.
// So, in this scenario, you will not get the next element of your array until the timer emits another event.
// In the call to sink, we ignore the first element of the tuple relating to the timer
playerCancellable = Publishers.Zip(timer, capitals.publisher)
.sink { _, item in
speaker.speak(item)
}
}
编辑
您在评论中提到您希望能够可变地控制话语之间的延迟。这不是真正可以使用 Timer 的东西。我进行了一些修改,因为我发现这是一个有趣的问题,并且能够按照您在评论中描述的那样进行这项工作:
class Speaker: NSObject {
let synth = AVSpeechSynthesizer()
private var timedPhrases: [(phrase: String, delay: TimeInterval)]
// This is so you don't potentially block the main queue
private let queue = DispatchQueue(label: "Phrase Queue")
override init() {
timed = []
super.init()
synth.delegate = self
}
init(_ timedPhrases: [(phrase: String, delay: TimeInterval)]) {
self.timedPhrases = timedPhrases
super.init()
synth.delegate = self
}
private func speak(_ string: String) {
let utterance = AVSpeechUtterance(string: string)
utterance.voice = AVSpeechSynthesisVoice(language: "en-GB")
utterance.rate = 0.5
synth.speak(utterance)
}
func speak() {
guard let first = timed.first else { return }
speak(first.value)
timed = Array(timed.dropFirst())
}
}
extension Speaker: AVSpeechSynthesizerDelegate {
func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance) {
if !timed.isEmpty {
queue.sync {
Thread.sleep(forTimeInterval: TimeInterval(timed.first!.delay))
self.speak()
}
} else {
print("all done")
}
}
}
let speaker = let speaker = Speaker([
(phrase: "1", delay: 0),
(phrase: "2", delay: 3),
(phrase: "3", delay: 1),
(phrase: "4", delay: 5),
(phrase: "5", delay: 10)
])
speaker.speak()
对此持怀疑态度。我真的不认为使用 Thread.sleep 是一个很好的做法,但也许这会给你一些关于如何处理它的想法。如果你想要可变时间,Timer 实例不会给你。
我正在尝试用时间间隔发音句子,但问题是在合成器第一次发音后,循环会直接运行到最后。台词很好,但中间没有停顿。
如何在语音合成任务完成后才循环切换到下一个项目?
编辑:也许,循环每次都等待 didFinish
,然后 didFinish
告诉循环什么时候可以继续?
let speaker = Speaker()
let capitals = ["Canberra is the capital of Australia", "Seoul is the capital of South Korea", "Tokyo is the capital of Japan", "Berlin is the capital of Germany"]
var body: some View {
Button("Play Sound") {
playSound()
}
}
func playSound() {
for item in 0..<capitals.count {
let timer = Timer.scheduledTimer(withTimeInterval: 20, repeats: false) { timer in
speaker.speak("\(capitals[item])")
print("I am out")
}
}
}
...
import AVFoundation
class Speaker: NSObject {
let synth = AVSpeechSynthesizer()
override init() {
super.init()
synth.delegate = self
}
func speak(_ string: String) {
let utterance = AVSpeechUtterance(string: string)
utterance.voice = AVSpeechSynthesisVoice(language: "en-GB")
utterance.rate = 0.5
synth.speak(utterance)
}
}
extension Speaker: AVSpeechSynthesizerDelegate {
func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance) {
print("all done")
}
}
您始终可以为此使用 Combine
import Combine
let speaker = Speaker()
let capitals = ["Canberra is the capital of Australia", "Seoul is the capital of South Korea", "Tokyo is the capital of Japan", "Berlin is the capital of Germany"]
var playerCancellable: AnyCancellable? = nil
Button("Play Sound") {
playSound()
}
func playSound() {
// Fairly standard timer publisher. The call to .autoconnect() tells the timer to start publishing when subscribed to.
let timer = Timer.publish(every: 20, on: .main, in: .default)
.autoconnect()
// Publishers.Zip takes two publishers.
// It will only publish when there is a "symmetrical" output. It behaves in a similar manner as `zip` on sequences.
// So, in this scenario, you will not get the next element of your array until the timer emits another event.
// In the call to sink, we ignore the first element of the tuple relating to the timer
playerCancellable = Publishers.Zip(timer, capitals.publisher)
.sink { _, item in
speaker.speak(item)
}
}
编辑
您在评论中提到您希望能够可变地控制话语之间的延迟。这不是真正可以使用 Timer 的东西。我进行了一些修改,因为我发现这是一个有趣的问题,并且能够按照您在评论中描述的那样进行这项工作:
class Speaker: NSObject {
let synth = AVSpeechSynthesizer()
private var timedPhrases: [(phrase: String, delay: TimeInterval)]
// This is so you don't potentially block the main queue
private let queue = DispatchQueue(label: "Phrase Queue")
override init() {
timed = []
super.init()
synth.delegate = self
}
init(_ timedPhrases: [(phrase: String, delay: TimeInterval)]) {
self.timedPhrases = timedPhrases
super.init()
synth.delegate = self
}
private func speak(_ string: String) {
let utterance = AVSpeechUtterance(string: string)
utterance.voice = AVSpeechSynthesisVoice(language: "en-GB")
utterance.rate = 0.5
synth.speak(utterance)
}
func speak() {
guard let first = timed.first else { return }
speak(first.value)
timed = Array(timed.dropFirst())
}
}
extension Speaker: AVSpeechSynthesizerDelegate {
func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance) {
if !timed.isEmpty {
queue.sync {
Thread.sleep(forTimeInterval: TimeInterval(timed.first!.delay))
self.speak()
}
} else {
print("all done")
}
}
}
let speaker = let speaker = Speaker([
(phrase: "1", delay: 0),
(phrase: "2", delay: 3),
(phrase: "3", delay: 1),
(phrase: "4", delay: 5),
(phrase: "5", delay: 10)
])
speaker.speak()
对此持怀疑态度。我真的不认为使用 Thread.sleep 是一个很好的做法,但也许这会给你一些关于如何处理它的想法。如果你想要可变时间,Timer 实例不会给你。