From dc1c00f4469c52d3a83bc262f39da0558159422e Mon Sep 17 00:00:00 2001 From: Nolan Darilek Date: Wed, 12 Aug 2020 15:14:17 -0500 Subject: [PATCH] Good news: `NSSpeechSynthesizer` speech now queues. Bad news: my brain bleeds. --- src/backends/ns_speech_synthesizer.rs | 111 ++++++++++++++++++++------ 1 file changed, 87 insertions(+), 24 deletions(-) diff --git a/src/backends/ns_speech_synthesizer.rs b/src/backends/ns_speech_synthesizer.rs index d7be450..2531a40 100644 --- a/src/backends/ns_speech_synthesizer.rs +++ b/src/backends/ns_speech_synthesizer.rs @@ -1,6 +1,6 @@ #[cfg(target_os = "macos")] #[link(name = "AppKit", kind = "framework")] -use cocoa_foundation::base::nil; +use cocoa_foundation::base::{id, nil}; use cocoa_foundation::foundation::NSString; use log::{info, trace}; use objc::declare::ClassDecl; @@ -14,29 +14,82 @@ pub struct NSSpeechSynthesizerBackend(*mut Object, *mut Object); impl NSSpeechSynthesizerBackend { pub fn new() -> Self { info!("Initializing NSSpeechSynthesizer backend"); - let mut obj: *mut Object = unsafe { msg_send![class!(NSSpeechSynthesizer), alloc] }; - obj = unsafe { msg_send![obj, init] }; - let mut decl = ClassDecl::new("MyNSSpeechSynthesizerDelegate", class!(NSObject)).unwrap(); - extern "C" fn speech_synthesizer_did_finish_speaking( - _: &Object, - _: Sel, - _: *const Object, - _: BOOL, - ) { - println!("Got it"); - } unsafe { + let obj: *mut Object = msg_send![class!(NSSpeechSynthesizer), new]; + let mut decl = + ClassDecl::new("MyNSSpeechSynthesizerDelegate", class!(NSObject)).unwrap(); + decl.add_ivar::("synth"); + decl.add_ivar::("strings"); + extern "C" fn enqueue_and_speak(this: &Object, _: Sel, string: id) { + unsafe { + let strings: id = *this.get_ivar("strings"); + let _: () = msg_send![strings, addObject: string]; + let count: u32 = msg_send![strings, count]; + if count == 1 { + let str: id = msg_send!(strings, firstObject); + let synth: id = *this.get_ivar("synth"); + let _: BOOL = msg_send![synth, startSpeakingString: str]; + } + } + } + extern "C" fn speech_synthesizer_did_finish_speaking( + this: &Object, + _: Sel, + synth: *const Object, + _: BOOL, + ) { + unsafe { + let strings: id = *this.get_ivar("strings"); + let str: id = msg_send!(strings, firstObject); + let _: () = msg_send![str, release]; + let _: () = msg_send!(strings, removeObjectAtIndex:0); + let count: u32 = msg_send![strings, count]; + if count > 0 { + let str: id = msg_send!(strings, firstObject); + let _: BOOL = msg_send![synth, startSpeakingString: str]; + } + } + } + extern "C" fn clear_queue(this: &Object, _: Sel) { + unsafe { + let strings: id = *this.get_ivar("strings"); + let mut count: u32 = msg_send![strings, count]; + while count > 0 { + let str: id = msg_send!(strings, firstObject); + let _: () = msg_send![str, release]; + let _: () = msg_send!(strings, removeObjectAtIndex:0); + count = msg_send![strings, count]; + } + } + } decl.add_method( sel!(speechSynthesizer:didFinishSpeaking:), speech_synthesizer_did_finish_speaking as extern "C" fn(&Object, Sel, *const Object, BOOL) -> (), - ) - }; - let delegate_class = decl.register(); - let delegate_obj: *mut Object = unsafe { msg_send![delegate_class, new] }; - let _: Object = unsafe { msg_send![obj, setDelegate: delegate_obj] }; - NSSpeechSynthesizerBackend(obj, delegate_obj) + ); + decl.add_method( + sel!(enqueueAndSpeak:), + enqueue_and_speak as extern "C" fn(&Object, Sel, id) -> (), + ); + decl.add_method( + sel!(clearQueue), + clear_queue as extern "C" fn(&Object, Sel) -> (), + ); + let delegate_class = decl.register(); + let delegate_obj: *mut Object = msg_send![delegate_class, new]; + delegate_obj.as_mut().unwrap().set_ivar("synth", obj); + let strings: id = msg_send![class!(NSMutableArray), new]; + delegate_obj.as_mut().unwrap().set_ivar("strings", strings); + let _: Object = msg_send![obj, setDelegate: delegate_obj]; + NSSpeechSynthesizerBackend(obj, delegate_obj) + } } + + /*fn pop_and_speak(&mut self) { + if let Some(str) = self.2.first() { + let _: BOOL = unsafe { msg_send![self.0, startSpeakingString: *str] }; + } + }*/ } impl Backend for NSSpeechSynthesizerBackend { @@ -52,14 +105,23 @@ impl Backend for NSSpeechSynthesizerBackend { fn speak(&mut self, text: &str, interrupt: bool) -> Result<(), Error> { trace!("speak({}, {})", text, interrupt); - let str = unsafe { NSString::alloc(nil).init_str(text) }; - let _: BOOL = unsafe { msg_send![self.0, startSpeakingString: str] }; + if interrupt { + self.stop()?; + } + unsafe { + let str = NSString::alloc(nil).init_str(text); + let _: () = msg_send![self.1, enqueueAndSpeak: str]; + } Ok(()) } fn stop(&mut self) -> Result<(), Error> { trace!("stop()"); - unimplemented!() + unsafe { + let _: () = msg_send![self.1, clearQueue]; + let _: () = msg_send![self.0, stopSpeaking]; + } + Ok(()) } fn min_rate(&self) -> f32 { @@ -123,15 +185,16 @@ impl Backend for NSSpeechSynthesizerBackend { } fn is_speaking(&self) -> Result { - unimplemented!() + let is_speaking: i8 = unsafe { msg_send![self.0, isSpeaking] }; + Ok(is_speaking == YES) } } impl Drop for NSSpeechSynthesizerBackend { fn drop(&mut self) { unsafe { - let _: Object = msg_send!(self.0, release); - let _: Object = msg_send!(self.1, release); + let _: Object = msg_send![self.0, release]; + let _: Object = msg_send![self.1, release]; } } }