Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MacOS: Only first phrase is being pronounced #40

Open
ninjaboy opened this issue Dec 18, 2022 · 8 comments
Open

MacOS: Only first phrase is being pronounced #40

ninjaboy opened this issue Dec 18, 2022 · 8 comments

Comments

@ninjaboy
Copy link
Contributor

Tried using it my macos console app and noticed that only first occurence of tts.speak produces sound.
Retried with simple example and the problem is reproducible

image

use std::{thread, time};

use tts::*;

fn main() -> Result<(), Error> {
    // env_logger::init();
    let mut tts = Tts::default()?;
    let mut phrase = 1;
    loop {
        println!("{phrase}");
        tts.speak(format!("Phrase {}", phrase), false)?;
        let time = time::Duration::from_secs(5);
        thread::sleep(time);
        phrase += 1;
    }
}
@ndarilek
Copy link
Owner

Please see the hello_world example for macOS-specific changes, specifically:

    // 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];
        }
    }

Please re-open if that doesn't work, or submit a PR if you know how to integrate that into the library itself.

Thanks.

@ninjaboy
Copy link
Contributor Author

Yeah, I tried that and it hangs indefinitely after the first phrase

#[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 std::{thread, time};
use tts::*;

fn main() -> Result<(), Error> {
    // env_logger::init();
    let mut tts = Tts::default()?;
    let mut phrase = 1;
    loop {
        println!("{phrase}");
        tts.speak(format!("Phrase {}", phrase), false)?;
        let time = time::Duration::from_secs(5);
        thread::sleep(time);
        phrase += 1;
        #[cfg(target_os = "macos")]
        {
            let run_loop: id = unsafe { NSRunLoop::currentRunLoop() };
            unsafe {
                let _: () = msg_send![run_loop, run];
            }
        }
    }
}

@ninjaboy
Copy link
Contributor Author

Sorry, I can't reopen this.
Please let me know if the above integration of the quick fix is not correct. Thanks in advance

@ndarilek ndarilek reopened this Dec 20, 2022
@ndarilek
Copy link
Owner

Sorry, not sure how to fix this and it works for what I need it to do under macOS.

I'll leave it open for folks with more macOS experience to help.

@ninjaboy
Copy link
Contributor Author

ninjaboy commented Dec 20, 2022

Ok, I think what needs to be done is to call something like this:

let _: () = msg_send![run_loop, runMode:NSDefaultRunLoopMode beforeDate:NSDate::distantFuture()];

https://developer.apple.com/documentation/foundation/nsrunloop/1411525-runmode?language=objc

I am very new to Rust so, not sure of what to pass here: NSDate::distantFuture()

Any idea? I can try to proceed further :)

@ndarilek

@ninjaboy
Copy link
Contributor Author

Ok, I got it working


#[cfg(target_os = "macos")]
use cocoa_foundation::base::id;
#[cfg(target_os = "macos")]
use cocoa_foundation::foundation::NSRunLoop;
use cocoa_foundation::foundation::{NSDate, NSDefaultRunLoopMode};
use objc::{
    class,
    runtime::{self, Object},
};
#[cfg(target_os = "macos")]
use objc::{msg_send, sel, sel_impl};
use std::{thread, time};
use tts::*;

fn main() -> Result<(), Error> {
    // env_logger::init();
    let mut tts = Tts::default()?;
    let mut phrase = 1;
    loop {
        println!("{phrase}");
        tts.speak(format!("Phrase {}", phrase), true)
            .expect("Failed");
        #[cfg(target_os = "macos")]
        {
            let run_loop: id = unsafe { NSRunLoop::currentRunLoop() };
            unsafe {
                let date: id = msg_send![class!(NSDate), distantFuture];
                let _: () = msg_send![run_loop, runMode:NSDefaultRunLoopMode beforeDate:date];
            }
        }

        let time = time::Duration::from_secs(1);
        thread::sleep(time);
        phrase += 1;
    }
}

Need to see how to include this call into the library after each call to speak.

@ninjaboy
Copy link
Contributor Author

@ndarilek , #41 at least for an example

@noxowl
Copy link

noxowl commented Nov 3, 2023

I'm currently facing this problem.
In a multithreaded environment (not sure if it matters), invoking TTS and then using the above solution will not work.
Also, applying the above solution to the main thread will hang the main thread's loop.
The expected behaviour is for on_utterance_end() to be called, but it is not.

But as far as I know, using opencv-rust's highgui works as expected without writing an NSRunLoop.
This is probably because highgui already has code for cocoa_foundation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants