// // KMTTSManager.swift // PDF Reader Pro // // Created by liujiajie on 2023/12/12. // @objc(KMTTSManagerDelegate) protocol KMTTSManagerDelegate: AnyObject { @objc optional func ttsMananger(_ tts: KMTTSManager, willSpeak selection: CPDFSelection) @objc optional func ttsManangerDidFinishSpeech(_ tts: KMTTSManager) -> Bool @objc optional func ttsManangerdidErrorSpeech(_ tts: KMTTSManager, message: String) } let KMTTS_SETTING_SPEECHSPEED_KEY = "TTS_Setting_SpeechSpeed_Key" let KMTTS_SETTING_ISCONTINUE_KEY = "TTS_Setting_IsContinue_Key" let KMTTS_SETTING_LANGUAGE_KEY = "TTS_Setting_Language_Key" import Foundation class KMTTSManager: NSObject, NSSpeechSynthesizerDelegate{ var delegate: KMTTSManagerDelegate? var rate: Float { get{ return self.speechSynthesizer.rate } set{ self.speechSynthesizer.rate = newValue UserDefaults.standard.setValue(newValue, forKey: KMTTS_SETTING_SPEECHSPEED_KEY) UserDefaults.standard.synchronize() } } var isContinue = false { willSet { } didSet { UserDefaults.standard.setValue(isContinue, forKey: KMTTS_SETTING_ISCONTINUE_KEY) UserDefaults.standard.synchronize() } } func isSpeaking() -> Bool { return self.speechSynthesizer.isSpeaking } var isPaused = false var speechSynthesizer: NSSpeechSynthesizer! var speechPDFPage: CPDFPage? var speechPDFSelection: CPDFSelection? static let defalutManager = KMTTSManager() override init() { super.init() let array = NSSpeechSynthesizer.availableVoices var voice: NSSpeechSynthesizer.VoiceName = array.first ?? NSSpeechSynthesizer.VoiceName(rawValue: "") if (UserDefaults.standard.object(forKey: KMTTS_SETTING_LANGUAGE_KEY) != nil){ voice = UserDefaults.standard.object(forKey: KMTTS_SETTING_LANGUAGE_KEY) as! NSSpeechSynthesizer.VoiceName } self.speechSynthesizer = NSSpeechSynthesizer(voice: voice) self.speechSynthesizer.delegate = self self.speechSynthesizer.usesFeedbackWindow = true var rate: Float = 175 if let savedRate = UserDefaults.standard.object(forKey: KMTTS_SETTING_SPEECHSPEED_KEY) as? Float { rate = savedRate } self.speechSynthesizer.rate = rate var isContinue = false if let savedContinue = UserDefaults.standard.object(forKey: KMTTS_SETTING_ISCONTINUE_KEY) as? Bool { isContinue = savedContinue } self.isContinue = isContinue } func voice() -> NSSpeechSynthesizer.VoiceName { return self.speechSynthesizer.voice() ?? NSSpeechSynthesizer.VoiceName(rawValue: "") } func setVoice(voice: NSSpeechSynthesizer.VoiceName) { self.speechSynthesizer.setVoice(voice) UserDefaults.standard.setValue(voice, forKey: KMTTS_SETTING_LANGUAGE_KEY) UserDefaults.standard.synchronize() } //MARK: Public func startSpeakingPDFPage(_ page: CPDFPage) -> Bool { guard let string = page.string else { return false } guard string.count > 0 else { return false } isPaused = false if speechSynthesizer.isSpeaking { speechSynthesizer.stopSpeaking(at: .immediateBoundary) } speechPDFPage = page speechPDFSelection = nil return speechSynthesizer.startSpeaking(string) } func startSpeakingPDFSelection(_ selection: CPDFSelection) -> Bool { guard let string = selection.string() else { return false } guard string.count > 0 else { return false } isPaused = false if speechSynthesizer.isSpeaking { speechSynthesizer.stopSpeaking(at: .immediateBoundary) } speechPDFPage = nil speechPDFSelection = selection return speechSynthesizer.startSpeaking(string) } func stopSpeaking() { if speechSynthesizer.isSpeaking || isPaused { isPaused = false speechSynthesizer.stopSpeaking(at: .immediateBoundary) } } func pauseSpeaking() { if speechSynthesizer.isSpeaking { isPaused = true speechSynthesizer.pauseSpeaking(at: .immediateBoundary) } } func continueSpeaking() { isPaused = false speechSynthesizer.continueSpeaking() } func availableVoices() -> [NSSpeechSynthesizer.VoiceName] { return NSSpeechSynthesizer.availableVoices } func attributesForVoice(_ voice: NSSpeechSynthesizer.VoiceName) -> [NSSpeechSynthesizer.VoiceAttributeKey : Any] { return NSSpeechSynthesizer.attributes(forVoice: voice) } func speechSynthesizer(_ sender: NSSpeechSynthesizer, didFinishSpeaking finishedSpeaking: Bool) { isPaused = false let _ = delegate?.ttsManangerDidFinishSpeech?(self) } func speechSynthesizer(_ sender: NSSpeechSynthesizer, willSpeakWord characterRange: NSRange, of string: String) { var selection: CPDFSelection? if let page = speechPDFPage { if characterRange.length > 0 { selection = page.selection(for: characterRange) } } else if let pdfSelection = speechPDFSelection { selection = pdfSelection } if let data = selection { delegate?.ttsMananger?(self, willSpeak: data) } } func speechSynthesizer(_ sender: NSSpeechSynthesizer, didEncounterErrorAt characterIndex: Int, of string: String, message: String) { delegate?.ttsManangerdidErrorSpeech?(self, message: message) } func speechSynthesizer(_ sender: NSSpeechSynthesizer, didEncounterSyncMessage message: String) { delegate?.ttsManangerdidErrorSpeech?(self, message: message) } }