From a55463dac8c15b1752091a01fc818f7ad9cc7e90 Mon Sep 17 00:00:00 2001 From: Lee Baker Date: Sat, 6 Aug 2022 06:08:28 -0500 Subject: [PATCH] Don't define the AVFoundation delegate more than once. This prevents a crash when multiple instances of the TTS engine are instantiated. --- src/backends/av_foundation.rs | 197 +++++++++++++++++----------------- 1 file changed, 101 insertions(+), 96 deletions(-) diff --git a/src/backends/av_foundation.rs b/src/backends/av_foundation.rs index a64d9b4..0ffbb8d 100644 --- a/src/backends/av_foundation.rs +++ b/src/backends/av_foundation.rs @@ -7,7 +7,7 @@ use core_foundation::array::CFArray; use core_foundation::string::CFString; use lazy_static::lazy_static; use log::{info, trace}; -use objc::runtime::{Object, Sel}; +use objc::runtime::{Class, Object, Sel}; use objc::{class, declare::ClassDecl, msg_send, sel, sel_impl}; use unic_langid::LanguageIdentifier; @@ -24,109 +24,114 @@ pub(crate) struct AvFoundation { voice: Option, } +fn make_speech_sythesizer_delegate() -> Result<&'static Class, Error> { + let mut decl = ClassDecl::new("MyNSSpeechSynthesizerDelegate", class!(NSObject)) + .ok_or(Error::OperationFailed)?; + decl.add_ivar::("backend_id"); + + extern "C" fn speech_synthesizer_did_start_speech_utterance( + this: &Object, + _: Sel, + _synth: *const Object, + utterance: id, + ) { + trace!("speech_synthesizer_did_start_speech_utterance"); + unsafe { + let backend_id: u64 = *this.get_ivar("backend_id"); + let backend_id = BackendId::AvFoundation(backend_id); + trace!("Locking callbacks"); + let mut callbacks = CALLBACKS.lock().unwrap(); + trace!("Locked"); + let callbacks = callbacks.get_mut(&backend_id).unwrap(); + if let Some(callback) = callbacks.utterance_begin.as_mut() { + trace!("Calling utterance_begin"); + let utterance_id = UtteranceId::AvFoundation(utterance); + callback(utterance_id); + trace!("Called"); + } + } + trace!("Done speech_synthesizer_did_start_speech_utterance"); + } + + extern "C" fn speech_synthesizer_did_finish_speech_utterance( + this: &Object, + _: Sel, + _synth: *const Object, + utterance: id, + ) { + trace!("speech_synthesizer_did_finish_speech_utterance"); + unsafe { + let backend_id: u64 = *this.get_ivar("backend_id"); + let backend_id = BackendId::AvFoundation(backend_id); + trace!("Locking callbacks"); + let mut callbacks = CALLBACKS.lock().unwrap(); + trace!("Locked"); + let callbacks = callbacks.get_mut(&backend_id).unwrap(); + if let Some(callback) = callbacks.utterance_end.as_mut() { + trace!("Calling utterance_end"); + let utterance_id = UtteranceId::AvFoundation(utterance); + callback(utterance_id); + trace!("Called"); + } + } + trace!("Done speech_synthesizer_did_finish_speech_utterance"); + } + + extern "C" fn speech_synthesizer_did_cancel_speech_utterance( + this: &Object, + _: Sel, + _synth: *const Object, + utterance: id, + ) { + trace!("speech_synthesizer_did_cancel_speech_utterance"); + unsafe { + let backend_id: u64 = *this.get_ivar("backend_id"); + let backend_id = BackendId::AvFoundation(backend_id); + trace!("Locking callbacks"); + let mut callbacks = CALLBACKS.lock().unwrap(); + trace!("Locked"); + let callbacks = callbacks.get_mut(&backend_id).unwrap(); + if let Some(callback) = callbacks.utterance_stop.as_mut() { + trace!("Calling utterance_stop"); + let utterance_id = UtteranceId::AvFoundation(utterance); + callback(utterance_id); + trace!("Called"); + } + } + trace!("Done speech_synthesizer_did_cancel_speech_utterance"); + } + + unsafe { + decl.add_method( + sel!(speechSynthesizer:didStartSpeechUtterance:), + speech_synthesizer_did_start_speech_utterance + as extern "C" fn(&Object, Sel, *const Object, id) -> (), + ); + decl.add_method( + sel!(speechSynthesizer:didFinishSpeechUtterance:), + speech_synthesizer_did_finish_speech_utterance + as extern "C" fn(&Object, Sel, *const Object, id) -> (), + ); + decl.add_method( + sel!(speechSynthesizer:didCancelSpeechUtterance:), + speech_synthesizer_did_cancel_speech_utterance + as extern "C" fn(&Object, Sel, *const Object, id) -> (), + ); + } + + Ok(decl.register()) +} + lazy_static! { static ref NEXT_BACKEND_ID: Mutex = Mutex::new(0); + static ref DELEGATE_CLASS: &'static Class = make_speech_sythesizer_delegate().unwrap(); } impl AvFoundation { pub(crate) fn new() -> Result { info!("Initializing AVFoundation backend"); - let mut decl = ClassDecl::new("MyNSSpeechSynthesizerDelegate", class!(NSObject)) - .ok_or(Error::OperationFailed)?; - decl.add_ivar::("backend_id"); - extern "C" fn speech_synthesizer_did_start_speech_utterance( - this: &Object, - _: Sel, - _synth: *const Object, - utterance: id, - ) { - trace!("speech_synthesizer_did_start_speech_utterance"); - unsafe { - let backend_id: u64 = *this.get_ivar("backend_id"); - let backend_id = BackendId::AvFoundation(backend_id); - trace!("Locking callbacks"); - let mut callbacks = CALLBACKS.lock().unwrap(); - trace!("Locked"); - let callbacks = callbacks.get_mut(&backend_id).unwrap(); - if let Some(callback) = callbacks.utterance_begin.as_mut() { - trace!("Calling utterance_begin"); - let utterance_id = UtteranceId::AvFoundation(utterance); - callback(utterance_id); - trace!("Called"); - } - } - trace!("Done speech_synthesizer_did_start_speech_utterance"); - } - - extern "C" fn speech_synthesizer_did_finish_speech_utterance( - this: &Object, - _: Sel, - _synth: *const Object, - utterance: id, - ) { - trace!("speech_synthesizer_did_finish_speech_utterance"); - unsafe { - let backend_id: u64 = *this.get_ivar("backend_id"); - let backend_id = BackendId::AvFoundation(backend_id); - trace!("Locking callbacks"); - let mut callbacks = CALLBACKS.lock().unwrap(); - trace!("Locked"); - let callbacks = callbacks.get_mut(&backend_id).unwrap(); - if let Some(callback) = callbacks.utterance_end.as_mut() { - trace!("Calling utterance_end"); - let utterance_id = UtteranceId::AvFoundation(utterance); - callback(utterance_id); - trace!("Called"); - } - } - trace!("Done speech_synthesizer_did_finish_speech_utterance"); - } - - extern "C" fn speech_synthesizer_did_cancel_speech_utterance( - this: &Object, - _: Sel, - _synth: *const Object, - utterance: id, - ) { - trace!("speech_synthesizer_did_cancel_speech_utterance"); - unsafe { - let backend_id: u64 = *this.get_ivar("backend_id"); - let backend_id = BackendId::AvFoundation(backend_id); - trace!("Locking callbacks"); - let mut callbacks = CALLBACKS.lock().unwrap(); - trace!("Locked"); - let callbacks = callbacks.get_mut(&backend_id).unwrap(); - if let Some(callback) = callbacks.utterance_stop.as_mut() { - trace!("Calling utterance_stop"); - let utterance_id = UtteranceId::AvFoundation(utterance); - callback(utterance_id); - trace!("Called"); - } - } - trace!("Done speech_synthesizer_did_cancel_speech_utterance"); - } - - unsafe { - decl.add_method( - sel!(speechSynthesizer:didStartSpeechUtterance:), - speech_synthesizer_did_start_speech_utterance - as extern "C" fn(&Object, Sel, *const Object, id) -> (), - ); - decl.add_method( - sel!(speechSynthesizer:didFinishSpeechUtterance:), - speech_synthesizer_did_finish_speech_utterance - as extern "C" fn(&Object, Sel, *const Object, id) -> (), - ); - decl.add_method( - sel!(speechSynthesizer:didCancelSpeechUtterance:), - speech_synthesizer_did_cancel_speech_utterance - as extern "C" fn(&Object, Sel, *const Object, id) -> (), - ); - } - - let delegate_class = decl.register(); - let delegate_obj: *mut Object = unsafe { msg_send![delegate_class, new] }; + let delegate_obj: *mut Object = unsafe { msg_send![*DELEGATE_CLASS, new] }; let mut backend_id = NEXT_BACKEND_ID.lock().unwrap(); let rv = unsafe { trace!("Creating synth");