oden/third-party/vendor/objc2/examples/speech_synthethis.rs
2024-03-08 11:03:01 -08:00

178 lines
5.1 KiB
Rust

//! Speak synthethized text.
//!
//! This uses `NSSpeechSynthesizer` on macOS, and `AVSpeechSynthesizer` on
//! other Apple platforms. Note that `AVSpeechSynthesizer` _is_ available on
//! macOS, but only since 10.15!
//!
//! TODO: Unsure about when to use `&mut` here?
//!
//! Works on macOS >= 10.7 and iOS > 7.0.
#![deny(unsafe_op_in_unsafe_fn)]
#![cfg_attr(feature = "gnustep-1-7", allow(unused))]
use std::thread;
use std::time::Duration;
use objc2::foundation::{NSObject, NSString};
use objc2::rc::{Id, Owned};
use objc2::{extern_class, msg_send, msg_send_id, ns_string, ClassType};
#[cfg(all(feature = "apple", target_os = "macos"))]
mod appkit {
use objc2::{foundation::NSCopying, rc::Shared};
use super::*;
#[link(name = "AppKit", kind = "framework")]
extern "C" {}
extern_class!(
/// <https://developer.apple.com/documentation/appkit/nsspeechsynthesizer?language=objc>
pub struct NSSpeechSynthesizer;
unsafe impl ClassType for NSSpeechSynthesizer {
type Super = NSObject;
}
);
impl NSSpeechSynthesizer {
// Uses default voice
pub fn new() -> Id<Self, Owned> {
unsafe { msg_send_id![Self::class(), new] }
}
fn set_rate(&mut self, rate: f32) {
unsafe { msg_send![self, setRate: rate] }
}
fn set_volume(&mut self, volume: f32) {
unsafe { msg_send![self, setVolume: volume] }
}
fn start_speaking(&mut self, s: &NSString) {
unsafe { msg_send![self, startSpeakingString: s] }
}
pub fn speak(&mut self, utterance: &Utterance) {
// Convert to the range 90-720 that `NSSpeechSynthesizer` seems to
// support
//
// Note that you'd probably want a nonlinear conversion here to
// make it match `AVSpeechSynthesizer`.
self.set_rate(90.0 + (utterance.rate * (360.0 - 90.0)));
self.set_volume(utterance.volume);
self.start_speaking(&utterance.string);
}
pub fn is_speaking(&self) -> bool {
unsafe { msg_send![self, isSpeaking] }
}
}
// Shim to make NSSpeechSynthesizer work similar to AVSpeechSynthesizer
pub struct Utterance {
rate: f32,
volume: f32,
string: Id<NSString, Shared>,
}
impl Utterance {
pub fn new(string: &NSString) -> Self {
Self {
rate: 0.5,
volume: 1.0,
string: string.copy(),
}
}
pub fn set_rate(&mut self, rate: f32) {
self.rate = rate;
}
pub fn set_volume(&mut self, volume: f32) {
self.volume = volume;
}
}
}
#[cfg(all(feature = "apple", not(target_os = "macos")))]
mod avfaudio {
use super::*;
#[link(name = "AVFoundation", kind = "framework")]
extern "C" {}
extern_class!(
/// <https://developer.apple.com/documentation/avfaudio/avspeechsynthesizer?language=objc>
#[derive(Debug)]
pub struct AVSpeechSynthesizer;
unsafe impl ClassType for AVSpeechSynthesizer {
type Super = NSObject;
}
);
impl AVSpeechSynthesizer {
pub fn new() -> Id<Self, Owned> {
unsafe { msg_send_id![Self::class(), new] }
}
pub fn speak(&mut self, utterance: &AVSpeechUtterance) {
unsafe { msg_send![self, speakUtterance: utterance] }
}
pub fn is_speaking(&self) -> bool {
unsafe { msg_send![self, isSpeaking] }
}
}
extern_class!(
/// <https://developer.apple.com/documentation/avfaudio/avspeechutterance?language=objc>
#[derive(Debug)]
pub struct AVSpeechUtterance;
unsafe impl ClassType for AVSpeechUtterance {
type Super = NSObject;
}
);
impl AVSpeechUtterance {
pub fn new(string: &NSString) -> Id<Self, Owned> {
unsafe { msg_send_id![msg_send_id![Self::class(), alloc], initWithString: string] }
}
pub fn set_rate(&mut self, rate: f32) {
unsafe { msg_send![self, setRate: rate] }
}
pub fn set_volume(&mut self, volume: f32) {
unsafe { msg_send![self, setVolume: volume] }
}
}
}
#[cfg(all(feature = "apple", target_os = "macos"))]
use appkit::{NSSpeechSynthesizer as Synthesizer, Utterance};
#[cfg(all(feature = "apple", not(target_os = "macos")))]
use avfaudio::{AVSpeechSynthesizer as Synthesizer, AVSpeechUtterance as Utterance};
#[cfg(feature = "apple")]
fn main() {
let mut synthesizer = Synthesizer::new();
let mut utterance = Utterance::new(ns_string!("Hello from Rust!"));
utterance.set_rate(0.5);
utterance.set_volume(0.5);
synthesizer.speak(&utterance);
// Wait until speech has properly started up
thread::sleep(Duration::from_millis(1000));
// Wait until finished speaking
while synthesizer.is_speaking() {
thread::sleep(Duration::from_millis(100));
}
}
#[cfg(feature = "gnustep-1-7")]
fn main() {
panic!("this example is only available on Apple targets");
}