Here is a simple app to demonstrate problem:
import SwiftUI
import AVFoundation
struct ContentView: View {
var synthVM = SpeakerViewModel()
var body: some View {
VStack {
Text("Hello, world!")
.padding()
HStack {
Button("Speak") {
if self.synthVM.speaker.isPaused {
self.synthVM.speaker.continueSpeaking()
} else {
self.synthVM.speak(text: "Привет на корабле! Кто это пришел к нам, чтобы посмотреть на это произведение?")
}
}
Button("Pause") {
if self.synthVM.speaker.isSpeaking {
self.synthVM.speaker.pauseSpeaking(at: .word)
}
}
Button("Stop") {
self.synthVM.speaker.stopSpeaking(at: .word)
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
class SpeakerViewModel: NSObject {
var speaker = AVSpeechSynthesizer()
override init() {
super.init()
self.speaker.delegate = self
}
func speak(text: String) {
let utterance = AVSpeechUtterance(string: text)
utterance.voice = AVSpeechSynthesisVoice(language: "ru")
speaker.speak(utterance)
}
}
extension SpeakerViewModel: AVSpeechSynthesizerDelegate {
func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didStart utterance: AVSpeechUtterance) {
print("started")
}
func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didPause utterance: AVSpeechUtterance) {
print("paused")
}
func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didContinue utterance: AVSpeechUtterance) {}
func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didCancel utterance: AVSpeechUtterance) {}
func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, willSpeakRangeOfSpeechString characterRange: NSRange, utterance: AVSpeechUtterance) {
guard let rangeInString = Range(characterRange, in: utterance.speechString) else { return }
print("Will speak: \(utterance.speechString[rangeInString])")
}
func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance) {
print("finished")
}
}
On simulator all works fine, but on real device there are many strange words appears in synthesis speak.
And willSpeakRangeOfSpeechString
output is different on simulator and real device
Simulator:
started
Will speak: Привет
Will speak: на
Will speak: корабле!
Will speak: Кто
Will speak: это
Will speak: пришел
Will speak: к
Will speak: нам,
Will speak: чтобы
Will speak: посмотреть
Will speak: на
Will speak: это
Will speak: произведение?
finished
iPhone output have errors:
2021-10-12 17:09:32.613273+0300 VoiceTest[9027:203522] [AXTTSCommon] Broken user rule: \b([234567890]+)2 (мили|кварты|чашки|{столовых }ложки)(?=$|\s|[[:punct:]»\xa0]) > Error Domain=NSCocoaErrorDomain Code=2048 "The value “\b([234567890]+)2 (мили|кварты|чашки|{столовых }ложки)(?=$|\s|[[:punct:]»\xa0])” is invalid." UserInfo={NSInvalidValue=\b([234567890]+)2 (мили|кварты|чашки|{столовых }ложки)(?=$|\s|[[:punct:]»\xa0])}
2021-10-12 17:09:32.613548+0300 VoiceTest[9027:203522] [AXTTSCommon] Broken user rule: \b(1\d+)2 (мили|кварты|чашки|{столовых }ложки)(?=$|\s|[[:punct:]»\xa0]) > Error Domain=NSCocoaErrorDomain Code=2048 "The value “\b(1\d+)2 (мили|кварты|чашки|{столовых }ложки)(?=$|\s|[[:punct:]»\xa0])” is invalid." UserInfo={NSInvalidValue=\b(1\d+)2 (мили|кварты|чашки|{столовых }ложки)(?=$|\s|[[:punct:]»\xa0])}
2021-10-12 17:09:32.613725+0300 VoiceTest[9027:203522] [AXTTSCommon] Broken user rule: \b2 (мили|кварты|чашки|{столовых }ложки)(?=$|\s|[[:punct:]»\xa0]) > Error Domain=NSCocoaErrorDomain Code=2048 "The value “\b2 (мили|кварты|чашки|{столовых }ложки)(?=$|\s|[[:punct:]»\xa0])” is invalid." UserInfo={NSInvalidValue=\b2 (мили|кварты|чашки|{столовых }ложки)(?=$|\s|[[:punct:]»\xa0])}
started
Will speak: Привет
Will speak: на
Will speak: ивет на корабле!
Will speak: Кто
Will speak: это
Will speak: Кто это пришел
Will speak: к
Will speak: нам,
Will speak: чтобы
Will speak: посмотреть
Will speak: на
Will speak: реть на это
Will speak: на это произведение?
finished
Error appears on iOS / iPadOS 15.0, 15.0.1, 15.0.2, 14.7 But all works fine on 14.8
Looks like engine error. How to fix that issue?