robbert-vdh / nih-plug

Rust VST3 and CLAP plugin framework and plugins - because everything is better when you do it yourself
ISC License
1.63k stars 146 forks source link

Asynchronous code inside the "process" method #149

Open SnowyTusk opened 3 months ago

SnowyTusk commented 3 months ago

The task I want to accomplish: When a suitable event occurs in the process, I want to send an asynchronous HTTP request for my requirements. At the moment, I am unable to make an asynchronous call. Instead, I have to make a synchronous call to the main thread. However, in this case, the request will hang if there is nobody to accept it. Also, it occupies the time of the main thread in any case.

// TODO: I want to make this function completely asynchronous.
fn send() {
    let handle = thread::spawn(|| {
        let runtime = tokio::runtime::Builder::new_current_thread()
            .enable_all()
            .build()
            .unwrap();

        runtime.block_on(async {
            // TODO: More specifically, I need to call this function in an asynchronous manner.
            send_http_request().await;
        });
    });

    handle.join().unwrap();
}
 fn process(
        &mut self,
        _buffer: &mut Buffer,
        _aux: &mut AuxiliaryBuffers,
        context: &mut impl ProcessContext<Self>,
    ) -> ProcessStatus {

// ...

context.send_event(NoteEvent::NoteOn {
    timing: 1,
    voice_id: None,
    channel: 1,
    note: generator::generate_random_note(),
    velocity: 1.0,
});

send();

I have read about the "execute_background" function, but I am still not sure how to use it or if it is even suitable for my needs. I have tried using "tokio::spawn(async)" separately, but it immediately crashes with an error, so there is no way for me to debug the program. Can you please recommend a way to work with asynchronous code, such as calling it in the "process" function?

Rydwxz commented 3 months ago

Forgive me if I'm overstepping by making this comment because I'm not a maintainer or a true expert. But you are trying to do problematic stuff.

You can't use tokio or any other async runtime inside nih-plug, as it cannot subordinate it's runtime to the needs of nih-plug. We have to run process() on the hosts say so, not according to an internal schedule.

Secondly you absolutely can't spawn a thread (or use await()!!!) inside process() because it's not real time safe. You can not allocate memory here at all. Whatever method of communication you use between threads, it must be fully set up and ready to use at the end of init()

SnowyTusk commented 3 months ago

Forgive me if I'm overstepping by making this comment because I'm not a maintainer or a true expert. But you are trying to do problematic stuff.

You can't use tokio or any other async runtime inside nih-plug, as it cannot subordinate it's runtime to the needs of nih-plug. We have to run process() on the hosts say so, not according to an internal schedule.

Secondly you absolutely can't spawn a thread inside process() because it's not real time safe. You can not allocate memory here at all. Whatever method of communication you use between threads, it must be fully set up and ready to use at the end of init()

That's what I understood after asking the question here. In general, I think I have enough performance, even considering the lock. Maybe it will be possible to call the code via the GUI thread, but I'm not sure. I don't have enough experience with Rust and this library. Anyway, thanks for your comment.

SnowyTusk commented 3 months ago

image I created a monster, but it works! Rust + nih-plug = VST3. Flutter + serverpod = UI. Rust sends data to Flutter via HTTP. I understand that there may be simpler ways (for example, using the same sockets), but I wanted to try it this way. It turned out to be a rough draft, but now I can work on it further and create some cool effects. Now, I have no limitations on visualization. I also attempted to use flutter_rust_bridge, but was unable to figure out how to get the GUI to work from Flutter. It is likely that this is currently impossible and very difficult to debug. Therefore, I have split the program into two parts. At least one of these parts can be developed using hot reload and debugging.