2020-09-02 21:03:04 +00:00
|
|
|
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
2020-08-13 16:08:00 +00:00
|
|
|
#[link(name = "AVFoundation", kind = "framework")]
|
2020-09-23 16:28:56 +00:00
|
|
|
use std::sync::Mutex;
|
|
|
|
|
2021-03-15 18:47:17 +00:00
|
|
|
use cocoa_foundation::base::{id, nil, NO};
|
2020-08-13 16:08:00 +00:00
|
|
|
use cocoa_foundation::foundation::NSString;
|
2020-09-23 16:28:56 +00:00
|
|
|
use lazy_static::lazy_static;
|
2020-08-13 16:08:00 +00:00
|
|
|
use log::{info, trace};
|
2020-09-23 17:21:05 +00:00
|
|
|
use objc::runtime::{Object, Sel};
|
|
|
|
use objc::{class, declare::ClassDecl, msg_send, sel, sel_impl};
|
2020-08-13 16:08:00 +00:00
|
|
|
|
2020-09-23 17:21:05 +00:00
|
|
|
use crate::{Backend, BackendId, Error, Features, UtteranceId, CALLBACKS};
|
2020-08-13 16:08:00 +00:00
|
|
|
|
2020-11-03 03:44:47 +00:00
|
|
|
#[derive(Clone, Debug)]
|
2020-09-23 16:28:56 +00:00
|
|
|
pub(crate) struct AvFoundation {
|
|
|
|
id: BackendId,
|
2020-09-23 17:21:05 +00:00
|
|
|
delegate: *mut Object,
|
2020-08-13 16:08:00 +00:00
|
|
|
synth: *mut Object,
|
|
|
|
rate: f32,
|
|
|
|
volume: f32,
|
|
|
|
pitch: f32,
|
|
|
|
}
|
|
|
|
|
2020-09-23 16:28:56 +00:00
|
|
|
lazy_static! {
|
|
|
|
static ref NEXT_BACKEND_ID: Mutex<u64> = Mutex::new(0);
|
|
|
|
}
|
|
|
|
|
2020-08-13 16:08:00 +00:00
|
|
|
impl AvFoundation {
|
2020-09-23 16:28:56 +00:00
|
|
|
pub(crate) fn new() -> Self {
|
2020-08-13 16:08:00 +00:00
|
|
|
info!("Initializing AVFoundation backend");
|
2020-09-23 17:21:05 +00:00
|
|
|
let mut decl = ClassDecl::new("MyNSSpeechSynthesizerDelegate", class!(NSObject)).unwrap();
|
|
|
|
decl.add_ivar::<u64>("backend_id");
|
|
|
|
|
|
|
|
extern "C" fn speech_synthesizer_did_start_speech_utterance(
|
|
|
|
this: &Object,
|
|
|
|
_: Sel,
|
2020-09-25 16:12:44 +00:00
|
|
|
_synth: *const Object,
|
2020-09-23 17:21:05 +00:00
|
|
|
utterance: id,
|
|
|
|
) {
|
2021-03-12 11:58:30 +00:00
|
|
|
trace!("speech_synthesizer_did_start_speech_utterance");
|
2020-09-23 17:21:05 +00:00
|
|
|
unsafe {
|
|
|
|
let backend_id: u64 = *this.get_ivar("backend_id");
|
|
|
|
let backend_id = BackendId::AvFoundation(backend_id);
|
2021-03-12 11:58:30 +00:00
|
|
|
trace!("Locking callbacks");
|
2020-09-25 16:12:44 +00:00
|
|
|
let mut callbacks = CALLBACKS.lock().unwrap();
|
2021-03-12 11:58:30 +00:00
|
|
|
trace!("Locked");
|
2020-09-25 16:12:44 +00:00
|
|
|
let callbacks = callbacks.get_mut(&backend_id).unwrap();
|
|
|
|
if let Some(callback) = callbacks.utterance_begin.as_mut() {
|
2021-03-12 11:58:30 +00:00
|
|
|
trace!("Calling utterance_begin");
|
2020-09-23 17:21:05 +00:00
|
|
|
let utterance_id = UtteranceId::AvFoundation(utterance);
|
2020-11-03 17:03:55 +00:00
|
|
|
callback(utterance_id);
|
2021-03-12 11:58:30 +00:00
|
|
|
trace!("Called");
|
2020-09-23 17:21:05 +00:00
|
|
|
}
|
|
|
|
}
|
2021-03-12 11:58:30 +00:00
|
|
|
trace!("Done speech_synthesizer_did_start_speech_utterance");
|
2020-09-23 17:21:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
extern "C" fn speech_synthesizer_did_finish_speech_utterance(
|
|
|
|
this: &Object,
|
|
|
|
_: Sel,
|
2020-09-25 16:12:44 +00:00
|
|
|
_synth: *const Object,
|
2020-09-23 17:21:05 +00:00
|
|
|
utterance: id,
|
|
|
|
) {
|
2021-03-12 11:58:30 +00:00
|
|
|
trace!("speech_synthesizer_did_finish_speech_utterance");
|
2020-09-23 17:21:05 +00:00
|
|
|
unsafe {
|
|
|
|
let backend_id: u64 = *this.get_ivar("backend_id");
|
|
|
|
let backend_id = BackendId::AvFoundation(backend_id);
|
2021-03-12 11:58:30 +00:00
|
|
|
trace!("Locking callbacks");
|
2020-09-25 16:12:44 +00:00
|
|
|
let mut callbacks = CALLBACKS.lock().unwrap();
|
2021-03-12 11:58:30 +00:00
|
|
|
trace!("Locked");
|
2020-09-25 16:12:44 +00:00
|
|
|
let callbacks = callbacks.get_mut(&backend_id).unwrap();
|
|
|
|
if let Some(callback) = callbacks.utterance_end.as_mut() {
|
2021-03-12 11:58:30 +00:00
|
|
|
trace!("Calling utterance_end");
|
2020-09-23 17:21:05 +00:00
|
|
|
let utterance_id = UtteranceId::AvFoundation(utterance);
|
2020-11-03 17:03:55 +00:00
|
|
|
callback(utterance_id);
|
2021-03-12 11:58:30 +00:00
|
|
|
trace!("Called");
|
2020-09-23 17:21:05 +00:00
|
|
|
}
|
|
|
|
}
|
2021-03-12 11:58:30 +00:00
|
|
|
trace!("Done speech_synthesizer_did_finish_speech_utterance");
|
2020-09-23 17:21:05 +00:00
|
|
|
}
|
|
|
|
|
2020-10-08 13:07:33 +00:00
|
|
|
extern "C" fn speech_synthesizer_did_cancel_speech_utterance(
|
|
|
|
this: &Object,
|
|
|
|
_: Sel,
|
|
|
|
_synth: *const Object,
|
|
|
|
utterance: id,
|
|
|
|
) {
|
2021-03-12 11:58:30 +00:00
|
|
|
trace!("speech_synthesizer_did_cancel_speech_utterance");
|
2020-10-08 13:07:33 +00:00
|
|
|
unsafe {
|
|
|
|
let backend_id: u64 = *this.get_ivar("backend_id");
|
|
|
|
let backend_id = BackendId::AvFoundation(backend_id);
|
2021-03-12 11:58:30 +00:00
|
|
|
trace!("Locking callbacks");
|
2020-10-08 13:07:33 +00:00
|
|
|
let mut callbacks = CALLBACKS.lock().unwrap();
|
2021-03-12 11:58:30 +00:00
|
|
|
trace!("Locked");
|
2020-10-08 13:07:33 +00:00
|
|
|
let callbacks = callbacks.get_mut(&backend_id).unwrap();
|
|
|
|
if let Some(callback) = callbacks.utterance_stop.as_mut() {
|
2021-03-12 11:58:30 +00:00
|
|
|
trace!("Calling utterance_stop");
|
2020-10-08 13:07:33 +00:00
|
|
|
let utterance_id = UtteranceId::AvFoundation(utterance);
|
2020-11-03 17:03:55 +00:00
|
|
|
callback(utterance_id);
|
2021-03-12 11:58:30 +00:00
|
|
|
trace!("Called");
|
2020-10-08 13:07:33 +00:00
|
|
|
}
|
|
|
|
}
|
2021-03-12 11:58:30 +00:00
|
|
|
trace!("Done speech_synthesizer_did_cancel_speech_utterance");
|
2020-10-08 13:07:33 +00:00
|
|
|
}
|
|
|
|
|
2020-09-23 17:21:05 +00:00
|
|
|
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) -> (),
|
|
|
|
);
|
2020-10-08 13:07:33 +00:00
|
|
|
decl.add_method(
|
|
|
|
sel!(speechSynthesizer:didCancelSpeechUtterance:),
|
|
|
|
speech_synthesizer_did_cancel_speech_utterance
|
|
|
|
as extern "C" fn(&Object, Sel, *const Object, id) -> (),
|
|
|
|
);
|
2020-09-23 17:21:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let delegate_class = decl.register();
|
|
|
|
let delegate_obj: *mut Object = unsafe { msg_send![delegate_class, new] };
|
2020-09-23 16:28:56 +00:00
|
|
|
let mut backend_id = NEXT_BACKEND_ID.lock().unwrap();
|
|
|
|
let rv = unsafe {
|
2021-03-12 12:59:49 +00:00
|
|
|
trace!("Creating synth");
|
2020-08-13 16:08:00 +00:00
|
|
|
let synth: *mut Object = msg_send![class!(AVSpeechSynthesizer), new];
|
2021-03-12 12:59:49 +00:00
|
|
|
trace!("Allocated {:?}", synth);
|
2020-09-23 17:21:05 +00:00
|
|
|
delegate_obj
|
|
|
|
.as_mut()
|
|
|
|
.unwrap()
|
|
|
|
.set_ivar("backend_id", *backend_id);
|
2021-03-12 12:59:49 +00:00
|
|
|
trace!("Set backend ID in delegate");
|
2020-09-23 17:21:05 +00:00
|
|
|
let _: () = msg_send![synth, setDelegate: delegate_obj];
|
2021-03-12 12:59:49 +00:00
|
|
|
trace!("Assigned delegate: {:?}", delegate_obj);
|
2020-08-13 16:08:00 +00:00
|
|
|
AvFoundation {
|
2020-09-23 16:28:56 +00:00
|
|
|
id: BackendId::AvFoundation(*backend_id),
|
2020-09-23 17:21:05 +00:00
|
|
|
delegate: delegate_obj,
|
2021-03-12 11:48:14 +00:00
|
|
|
synth,
|
2020-08-13 16:08:00 +00:00
|
|
|
rate: 0.5,
|
|
|
|
volume: 1.,
|
|
|
|
pitch: 1.,
|
|
|
|
}
|
2020-09-23 16:28:56 +00:00
|
|
|
};
|
|
|
|
*backend_id += 1;
|
|
|
|
rv
|
2020-08-13 16:08:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Backend for AvFoundation {
|
2020-09-23 16:28:56 +00:00
|
|
|
fn id(&self) -> Option<BackendId> {
|
|
|
|
Some(self.id)
|
|
|
|
}
|
|
|
|
|
2020-08-13 16:08:00 +00:00
|
|
|
fn supported_features(&self) -> Features {
|
|
|
|
Features {
|
|
|
|
stop: true,
|
|
|
|
rate: true,
|
|
|
|
pitch: true,
|
|
|
|
volume: true,
|
|
|
|
is_speaking: true,
|
2020-09-23 16:28:56 +00:00
|
|
|
utterance_callbacks: true,
|
2020-08-13 16:08:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-22 19:08:19 +00:00
|
|
|
fn speak(&mut self, text: &str, interrupt: bool) -> Result<Option<UtteranceId>, Error> {
|
2020-08-13 16:08:00 +00:00
|
|
|
trace!("speak({}, {})", text, interrupt);
|
2021-03-12 11:50:08 +00:00
|
|
|
if interrupt && self.is_speaking()? {
|
2020-08-13 16:08:00 +00:00
|
|
|
self.stop()?;
|
|
|
|
}
|
2021-03-15 18:06:49 +00:00
|
|
|
let mut utterance: id;
|
2020-08-13 16:08:00 +00:00
|
|
|
unsafe {
|
2021-03-12 12:38:46 +00:00
|
|
|
trace!("Allocating utterance string");
|
2021-03-15 18:46:22 +00:00
|
|
|
let mut str = NSString::alloc(nil);
|
|
|
|
str = str.init_str(text);
|
2021-03-12 12:38:46 +00:00
|
|
|
trace!("Allocating utterance");
|
2020-09-22 19:08:19 +00:00
|
|
|
utterance = msg_send![class!(AVSpeechUtterance), alloc];
|
2021-03-12 12:38:46 +00:00
|
|
|
trace!("Initializing utterance");
|
2021-03-15 18:03:41 +00:00
|
|
|
utterance = msg_send![utterance, initWithString: str];
|
2021-03-12 12:38:46 +00:00
|
|
|
trace!("Setting rate to {}", self.rate);
|
2020-08-13 16:08:00 +00:00
|
|
|
let _: () = msg_send![utterance, setRate: self.rate];
|
2021-03-12 12:38:46 +00:00
|
|
|
trace!("Setting volume to {}", self.volume);
|
2020-08-13 16:08:00 +00:00
|
|
|
let _: () = msg_send![utterance, setVolume: self.volume];
|
2021-03-12 12:38:46 +00:00
|
|
|
trace!("Setting pitch to {}", self.pitch);
|
2020-08-13 16:08:00 +00:00
|
|
|
let _: () = msg_send![utterance, setPitchMultiplier: self.pitch];
|
2021-03-12 12:38:46 +00:00
|
|
|
trace!("Enqueuing");
|
2020-08-13 16:08:00 +00:00
|
|
|
let _: () = msg_send![self.synth, speakUtterance: utterance];
|
2021-03-12 12:38:46 +00:00
|
|
|
trace!("Done queuing");
|
2020-08-13 16:08:00 +00:00
|
|
|
}
|
2020-09-22 19:08:19 +00:00
|
|
|
Ok(Some(UtteranceId::AvFoundation(utterance)))
|
2020-08-13 16:08:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn stop(&mut self) -> Result<(), Error> {
|
|
|
|
trace!("stop()");
|
|
|
|
unsafe {
|
|
|
|
let _: () = msg_send![self.synth, stopSpeakingAtBoundary: 0];
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn min_rate(&self) -> f32 {
|
|
|
|
0.1
|
|
|
|
}
|
|
|
|
|
|
|
|
fn max_rate(&self) -> f32 {
|
|
|
|
2.
|
|
|
|
}
|
|
|
|
|
|
|
|
fn normal_rate(&self) -> f32 {
|
|
|
|
0.5
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_rate(&self) -> Result<f32, Error> {
|
|
|
|
Ok(self.rate)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_rate(&mut self, rate: f32) -> Result<(), Error> {
|
|
|
|
trace!("set_rate({})", rate);
|
|
|
|
self.rate = rate;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn min_pitch(&self) -> f32 {
|
|
|
|
0.5
|
|
|
|
}
|
|
|
|
|
|
|
|
fn max_pitch(&self) -> f32 {
|
|
|
|
2.0
|
|
|
|
}
|
|
|
|
|
|
|
|
fn normal_pitch(&self) -> f32 {
|
|
|
|
1.0
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_pitch(&self) -> Result<f32, Error> {
|
|
|
|
Ok(self.pitch)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_pitch(&mut self, pitch: f32) -> Result<(), Error> {
|
2021-03-12 12:28:02 +00:00
|
|
|
trace!("set_pitch({})", pitch);
|
2020-08-13 16:08:00 +00:00
|
|
|
self.pitch = pitch;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn min_volume(&self) -> f32 {
|
|
|
|
0.
|
|
|
|
}
|
|
|
|
|
|
|
|
fn max_volume(&self) -> f32 {
|
|
|
|
1.
|
|
|
|
}
|
|
|
|
|
|
|
|
fn normal_volume(&self) -> f32 {
|
|
|
|
1.
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_volume(&self) -> Result<f32, Error> {
|
|
|
|
Ok(self.volume)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_volume(&mut self, volume: f32) -> Result<(), Error> {
|
2021-03-12 12:28:02 +00:00
|
|
|
trace!("set_volume({})", volume);
|
2020-08-13 16:08:00 +00:00
|
|
|
self.volume = volume;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn is_speaking(&self) -> Result<bool, Error> {
|
2021-03-12 12:28:02 +00:00
|
|
|
trace!("is_speaking()");
|
2020-08-13 16:08:00 +00:00
|
|
|
let is_speaking: i8 = unsafe { msg_send![self.synth, isSpeaking] };
|
2021-03-15 19:02:05 +00:00
|
|
|
Ok(is_speaking != NO as i8)
|
2020-08-13 16:08:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Drop for AvFoundation {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
unsafe {
|
2020-09-23 17:21:05 +00:00
|
|
|
let _: Object = msg_send![self.delegate, release];
|
2020-08-13 16:08:00 +00:00
|
|
|
let _: Object = msg_send![self.synth, release];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|