#[cfg(target_os = "macos")] #[link(name = "AppKit", kind = "framework")] use cocoa_foundation::base::{id, nil}; use cocoa_foundation::foundation::NSString; use log::{info, trace}; use objc::declare::ClassDecl; use objc::runtime::*; use objc::*; use crate::{Backend, Error, Features}; pub struct AppKit(*mut Object, *mut Object); impl AppKit { pub fn new() -> Self { info!("Initializing AppKit backend"); 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]; } } } decl.add_method( sel!(enqueueAndSpeak:), enqueue_and_speak as extern "C" fn(&Object, Sel, id) -> (), ); 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]; } } } decl.add_method( sel!(speechSynthesizer:didFinishSpeaking:), speech_synthesizer_did_finish_speaking as extern "C" fn(&Object, Sel, *const Object, BOOL) -> (), ); 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!(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]; AppKit(obj, delegate_obj) } } } impl Backend for AppKit { fn supported_features(&self) -> Features { Features { stop: true, rate: true, volume: true, is_speaking: true, ..Default::default() } } fn speak(&mut self, text: &str, interrupt: bool) -> Result<(), Error> { trace!("speak({}, {})", text, interrupt); 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()"); unsafe { let _: () = msg_send![self.1, clearQueue]; let _: () = msg_send![self.0, stopSpeaking]; } Ok(()) } fn min_rate(&self) -> f32 { 10. } fn max_rate(&self) -> f32 { 500. } fn normal_rate(&self) -> f32 { 175. } fn get_rate(&self) -> Result { let rate: f32 = unsafe { msg_send![self.0, rate] }; Ok(rate) } fn set_rate(&mut self, rate: f32) -> Result<(), Error> { trace!("set_rate({})", rate); unsafe { let _: () = msg_send![self.0, setRate: rate]; } Ok(()) } fn min_pitch(&self) -> f32 { unimplemented!() } fn max_pitch(&self) -> f32 { unimplemented!() } fn normal_pitch(&self) -> f32 { unimplemented!() } fn get_pitch(&self) -> Result { unimplemented!() } fn set_pitch(&mut self, _pitch: f32) -> Result<(), Error> { unimplemented!() } fn min_volume(&self) -> f32 { 0. } fn max_volume(&self) -> f32 { 1. } fn normal_volume(&self) -> f32 { 1. } fn get_volume(&self) -> Result { let volume: f32 = unsafe { msg_send![self.0, volume] }; Ok(volume) } fn set_volume(&mut self, volume: f32) -> Result<(), Error> { unsafe { let _: () = msg_send![self.0, setVolume: volume]; } Ok(()) } fn is_speaking(&self) -> Result { let is_speaking: i8 = unsafe { msg_send![self.0, isSpeaking] }; Ok(is_speaking == YES) } } impl Drop for AppKit { fn drop(&mut self) { unsafe { let _: Object = msg_send![self.0, release]; let _: Object = msg_send![self.1, release]; } } }