diff --git a/ios/Plugin/TextToSpeech.swift b/ios/Plugin/TextToSpeech.swift index 90444f4..4ad022b 100644 --- a/ios/Plugin/TextToSpeech.swift +++ b/ios/Plugin/TextToSpeech.swift @@ -9,6 +9,7 @@ enum QUEUE_STRATEGY: Int { let synthesizer = AVSpeechSynthesizer() var calls: [CAPPluginCall] = [] let queue = DispatchQueue(label: "backgroundAudioSetup", qos: .userInitiated, attributes: [], autoreleaseFrequency: .inherit, target: nil) + private weak var plugin: TextToSpeechPlugin? override init() { super.init() @@ -25,6 +26,22 @@ enum QUEUE_STRATEGY: Int { } } + init(plugin: TextToSpeechPlugin) { + self.plugin = plugin + super.init() + self.synthesizer.delegate = self + // set session in background to avoid UI hangs. + queue.async { + do { + let avAudioSessionCategory: AVAudioSession.Category = .playback + try AVAudioSession.sharedInstance().setCategory(avAudioSessionCategory, mode: .default, options: .duckOthers) + try AVAudioSession.sharedInstance().setActive(true) + } catch { + print("Error setting up AVAudioSession: \(error)") + } + } + } + public func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didCancel utterance: AVSpeechUtterance) { self.resolveCurrentCall() } @@ -33,6 +50,24 @@ enum QUEUE_STRATEGY: Int { self.resolveCurrentCall() } + public func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, willSpeakRangeOfSpeechString characterRange: NSRange, utterance: AVSpeechUtterance) { + guard let plugin = self.plugin else { return } + + let start = characterRange.location + let end = characterRange.location + characterRange.length + + if let range = Range(characterRange, in: utterance.speechString) { + let spokenWord = String(utterance.speechString[range]) + let ret = [ + "start": start, + "end": end, + "spokenWord": spokenWord + ] as [String: Any] + + plugin.notifyListeners("onRangeStart", data: ret) + } + } + @objc public func speak(_ text: String, _ lang: String, _ rate: Float, _ pitch: Float, _ category: String, _ volume: Float, _ voice: Int, _ queueStrategy: Int, _ call: CAPPluginCall) throws { if queueStrategy == QUEUE_STRATEGY.QUEUE_FLUSH.rawValue { self.synthesizer.stopSpeaking(at: .immediate) diff --git a/ios/Plugin/TextToSpeechPlugin.swift b/ios/Plugin/TextToSpeechPlugin.swift index 903c7c6..30e8302 100644 --- a/ios/Plugin/TextToSpeechPlugin.swift +++ b/ios/Plugin/TextToSpeechPlugin.swift @@ -10,7 +10,11 @@ import AVFoundation public class TextToSpeechPlugin: CAPPlugin { private static let errorUnsupportedLanguage = "This language is not supported." - private let implementation = TextToSpeech() + private var implementation: TextToSpeech! + + public override func load() { + implementation = TextToSpeech(plugin: self) + } @objc public func speak(_ call: CAPPluginCall) { let text = call.getString("text") ?? ""