diff --git a/src/backends/av_foundation.rs b/src/backends/av_foundation.rs index bdd5620..e097b1e 100644 --- a/src/backends/av_foundation.rs +++ b/src/backends/av_foundation.rs @@ -6,13 +6,14 @@ use cocoa_foundation::base::{id, nil}; use cocoa_foundation::foundation::NSString; use lazy_static::lazy_static; use log::{info, trace}; -use objc::runtime::*; -use objc::*; +use objc::runtime::{Object, Sel}; +use objc::{class, declare::ClassDecl, msg_send, sel, sel_impl}; -use crate::{Backend, BackendId, Error, Features, UtteranceId}; +use crate::{Backend, BackendId, Error, Features, UtteranceId, CALLBACKS}; pub(crate) struct AvFoundation { id: BackendId, + delegate: *mut Object, synth: *mut Object, rate: f32, volume: f32, @@ -26,11 +27,71 @@ lazy_static! { impl AvFoundation { pub(crate) fn new() -> Self { info!("Initializing AVFoundation backend"); + let mut decl = ClassDecl::new("MyNSSpeechSynthesizerDelegate", class!(NSObject)).unwrap(); + decl.add_ivar::("backend_id"); + + extern "C" fn speech_synthesizer_did_start_speech_utterance( + this: &Object, + _: Sel, + synth: *const Object, + utterance: id, + ) { + unsafe { + let backend_id: u64 = *this.get_ivar("backend_id"); + let backend_id = BackendId::AvFoundation(backend_id); + let callbacks = CALLBACKS.lock().unwrap(); + let callbacks = callbacks.get(&backend_id).unwrap(); + if let Some(callback) = callbacks.utterance_begin { + let utterance_id = UtteranceId::AvFoundation(utterance); + callback(utterance_id); + } + } + } + + extern "C" fn speech_synthesizer_did_finish_speech_utterance( + this: &Object, + _: Sel, + synth: *const Object, + utterance: id, + ) { + unsafe { + let backend_id: u64 = *this.get_ivar("backend_id"); + let backend_id = BackendId::AvFoundation(backend_id); + let callbacks = CALLBACKS.lock().unwrap(); + let callbacks = callbacks.get(&backend_id).unwrap(); + if let Some(callback) = callbacks.utterance_end { + let utterance_id = UtteranceId::AvFoundation(utterance); + callback(utterance_id); + } + } + } + + 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) -> (), + ); + } + + let delegate_class = decl.register(); + let delegate_obj: *mut Object = unsafe { msg_send![delegate_class, new] }; let mut backend_id = NEXT_BACKEND_ID.lock().unwrap(); let rv = unsafe { let synth: *mut Object = msg_send![class!(AVSpeechSynthesizer), new]; + delegate_obj + .as_mut() + .unwrap() + .set_ivar("backend_id", *backend_id); + let _: () = msg_send![synth, setDelegate: delegate_obj]; AvFoundation { id: BackendId::AvFoundation(*backend_id), + delegate: delegate_obj, synth: synth, rate: 0.5, volume: 1., @@ -157,6 +218,7 @@ impl Backend for AvFoundation { impl Drop for AvFoundation { fn drop(&mut self) { unsafe { + let _: Object = msg_send![self.delegate, release]; let _: Object = msg_send![self.synth, release]; } }