如何修复我的应用程序中使用 AVSpeechSynthesizer 播放播放列表的这个相当复杂的错误?
How can I make fix this quite complex bug in my app that uses AVSpeechSynthesizer to play a playlist?
(如果有人能想到更好的标题,请编辑!)
我注意到通常,当我调用 AVSpeechSynthesizer.stopSpeaking(at: .immediate)
时,会调用 didCancel
委托方法。但是,如果我在最后一个音节停止合成器,它会调用 didFinish
.
重现步骤:
使用一个按钮创建一个应用程序。将按钮的 IBAction 连接到此方法:
let synthesiser = AVSpeechSynthesizer()
@IBAction func click() {
if synthesiser.isSpeaking {
print("Manually stopping")
synthesiser.stopSpeaking(at: .immediate)
} else {
let utterance = AVSpeechUtterance(string: "One Two Three Four Internationalization")
utterance.rate = AVSpeechUtteranceMinimumSpeechRate
utterance.voice = AVSpeechSynthesisVoice(language: "en-US")
synthesiser.delegate = self
synthesiser.speak(utterance)
}
}
实现这两个委托方法:
func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance) {
print("Stopped!")
}
func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didCancel utterance: AVSpeechUtterance) {
print("Cancelled!")
}
按下按钮开始合成,再次按下停止。如果你停在中间,它会打印:
Manually stopping
Cancelled!
这是预期的。如果你让它说整行,最终它会打印:
Stopped!
这也是意料之中的。但是,如果您在最后一个音节处停止它,那么它会打印:
Manually stopping
Stopped!
我预计第二行是"Cancelled!",就像停在中间一样。
为什么这对我很重要?
在我的应用程序中,我有一个用户可以播放的话语列表(即播放列表)。用户可以按按钮转到上一个或下一个话语。如果一段话播放完,则自动播放下一段话。
// these are properties of the VC
var playlist: Playlist!
var currentIndex = 0 {
didSet {
updateTextView()
}
}
var currentUtterance: Utterance {
return playlist.items[currentIndex]
}
var isPlaying = false
// plays the previous utterance in the playlist
@objc func previousPressed() {
guard currentIndex > 0 else { return }
previous()
}
// plays the next utterance in the playlist
@objc func nextPressed() {
guard currentIndex < playlist.items.count - 1 else { return }
next()
}
func next() {
currentIndex += 1
playCurrentUtterance()
}
func previous() {
currentIndex -= 1
playCurrentUtterance()
}
func playCurrentUtterance() {
speechSynthesiser.stopSpeaking(at: .immediate)
speechSynthesiser.speak(currentUtterance.avUtterance)
isPlaying = true
}
func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance) {
if currentIndex >= playlist.items.count - 1 { // this checks whether this is the last utterance
isPlaying = false
} else {
// when one utterance finishes, play the next one
next()
}
}
当用户在合成器说出最后一个音节时转到下一个或上一个话语时,就会出现该错误。假设用户转到下一个话语。 speechSynthesiser.stopSpeaking(at: .immediate)
导致 didFinish
被调用,所以 next
实际上被调用了两次(一次被 nextPressed
一次被 didFinish
调用)!结果,currentIndex
增加了两次! playCurrentUtterance
也被调用了两次,这意味着 speechSynthesiser.speak
被调用了两次。但是,合成器一次只能说一个话语,并且仍在说 next 话语(与 next next 话语相反)。
我该如何修复这个错误?
将此 属性 添加到您的 viewController
:
var manuallyStopping = false
调用时设置stopSpeaking()
:
manuallyStopping = synthesizer.stopSpeaking(at: .immediate)
在didFinish()
中勾选,如果设置了就按didCancel()
处理,然后再设置false
.
同时在didCancel()
中将manuallyStopping
设置为false
(如果有人能想到更好的标题,请编辑!)
我注意到通常,当我调用 AVSpeechSynthesizer.stopSpeaking(at: .immediate)
时,会调用 didCancel
委托方法。但是,如果我在最后一个音节停止合成器,它会调用 didFinish
.
重现步骤:
使用一个按钮创建一个应用程序。将按钮的 IBAction 连接到此方法:
let synthesiser = AVSpeechSynthesizer()
@IBAction func click() {
if synthesiser.isSpeaking {
print("Manually stopping")
synthesiser.stopSpeaking(at: .immediate)
} else {
let utterance = AVSpeechUtterance(string: "One Two Three Four Internationalization")
utterance.rate = AVSpeechUtteranceMinimumSpeechRate
utterance.voice = AVSpeechSynthesisVoice(language: "en-US")
synthesiser.delegate = self
synthesiser.speak(utterance)
}
}
实现这两个委托方法:
func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance) {
print("Stopped!")
}
func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didCancel utterance: AVSpeechUtterance) {
print("Cancelled!")
}
按下按钮开始合成,再次按下停止。如果你停在中间,它会打印:
Manually stopping
Cancelled!
这是预期的。如果你让它说整行,最终它会打印:
Stopped!
这也是意料之中的。但是,如果您在最后一个音节处停止它,那么它会打印:
Manually stopping
Stopped!
我预计第二行是"Cancelled!",就像停在中间一样。
为什么这对我很重要?
在我的应用程序中,我有一个用户可以播放的话语列表(即播放列表)。用户可以按按钮转到上一个或下一个话语。如果一段话播放完,则自动播放下一段话。
// these are properties of the VC
var playlist: Playlist!
var currentIndex = 0 {
didSet {
updateTextView()
}
}
var currentUtterance: Utterance {
return playlist.items[currentIndex]
}
var isPlaying = false
// plays the previous utterance in the playlist
@objc func previousPressed() {
guard currentIndex > 0 else { return }
previous()
}
// plays the next utterance in the playlist
@objc func nextPressed() {
guard currentIndex < playlist.items.count - 1 else { return }
next()
}
func next() {
currentIndex += 1
playCurrentUtterance()
}
func previous() {
currentIndex -= 1
playCurrentUtterance()
}
func playCurrentUtterance() {
speechSynthesiser.stopSpeaking(at: .immediate)
speechSynthesiser.speak(currentUtterance.avUtterance)
isPlaying = true
}
func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance) {
if currentIndex >= playlist.items.count - 1 { // this checks whether this is the last utterance
isPlaying = false
} else {
// when one utterance finishes, play the next one
next()
}
}
当用户在合成器说出最后一个音节时转到下一个或上一个话语时,就会出现该错误。假设用户转到下一个话语。 speechSynthesiser.stopSpeaking(at: .immediate)
导致 didFinish
被调用,所以 next
实际上被调用了两次(一次被 nextPressed
一次被 didFinish
调用)!结果,currentIndex
增加了两次! playCurrentUtterance
也被调用了两次,这意味着 speechSynthesiser.speak
被调用了两次。但是,合成器一次只能说一个话语,并且仍在说 next 话语(与 next next 话语相反)。
我该如何修复这个错误?
将此 属性 添加到您的 viewController
:
var manuallyStopping = false
调用时设置stopSpeaking()
:
manuallyStopping = synthesizer.stopSpeaking(at: .immediate)
在didFinish()
中勾选,如果设置了就按didCancel()
处理,然后再设置false
.
同时在didCancel()
manuallyStopping
设置为false