ndarilek / tts-rs

115 stars 25 forks source link

AVFoundation: add ability to get the current voice #26

Open leecbaker opened 2 years ago

leecbaker commented 2 years ago

This adds the ability to get the current voice in AVFoundation. This is either the most recent voice passed to set_voice(), or the system default voice.

Here's an example of this feature in use:

#[cfg(target_os = "macos")]
use cocoa_foundation::base::id;
#[cfg(target_os = "macos")]
use cocoa_foundation::foundation::NSRunLoop;
#[cfg(target_os = "macos")]
use objc::{msg_send, sel, sel_impl};

use tts::*;

fn main() -> Result<(), Error> {
    env_logger::init();
    let mut tts = Tts::default()?;

    // Check to ensure we can use this feature
    if !tts.supported_features().get_voice {
        return Err(Error::UnsupportedFeature);
    }

    // First, lets see what the default voice is. We'll print the name, and
    // say something in it (to ensure we can speak with it)
    let voice_on_start = tts.voice().unwrap();
    println!(
        "Voice on start: {}",
        voice_on_start
            .as_ref()
            .map_or("None".to_owned(), |voice: &Voice| voice.name())
    );

    if let Some(voice) = voice_on_start {
        tts.speak(format!("The default voice is {}", voice.name()), false)?;
    } else {
        tts.speak("No default voice", false)?;
    }

    // Pick a different voice
    let voices = tts.voices().unwrap();
    let chosen_voice = &voices[5];

    println!("Setting voice: {}", chosen_voice.name());
    tts.set_voice(chosen_voice).unwrap();

    // Say something with the new voice to ensure that it actually changed
    tts.speak(format!("The new voice is {}", chosen_voice.name()), false)?;

    let voice_now = &tts.voice().unwrap();
    println!(
        "Voice is now: {}",
        voice_now
            .as_ref()
            .map_or("None".to_owned(), |voice: &Voice| voice.name())
    );

    assert_eq!(
        voice_now.as_ref().map(|voice: &Voice| voice.name()),
        tts.voice()?.map(|voice| voice.name())
    );

    // The below is only needed to make the example run on MacOS because there is no NSRunLoop in this context.
    // It shouldn't be needed in an app or game that almost certainly has one already.
    #[cfg(target_os = "macos")]
    {
        let run_loop: id = unsafe { NSRunLoop::currentRunLoop() };
        unsafe {
            let _: () = msg_send![run_loop, run];
        }
    }

    Ok(())
}