RustAudio / rust-jack

Rust bindings for JACK. A realtime sound server for audio and midi IO
http://rustaudio.github.io/rust-jack/
MIT License
210 stars 33 forks source link

Keeping activate_async result in a struct #195

Closed phstrauss closed 3 weeks ago

phstrauss commented 3 months ago

More probably a bug by me. In my first rust-jack (and even Rust, so be gentle with a beginner :) tryout, I would like to make a small oscilloscope and STFT spectrum analyzer using egui and rust-jack. I need to keep the client around in my code for stopping it on an input from the GUI, so (for now at least) I would like to write something along those lines:

pub struct SinePlot {
    line_plot: LinePlot, // egui plot
    jclient: (jack::AsyncClient<Notifications, dyn jack::ProcessHandler>, jack::Error), // app.rs LINE 108
}

...

impl Default for SinePlot {
    fn default() -> Self {
        let (client, _status) = jack::Client::new("birdsong_rs", jack::ClientOptions::NO_START_SERVER).unwrap();

    // Register ports. They will be used in a callback that will be
    // called when new data is available.
    let in_l = client
        .register_port("in_l", jack::AudioIn::default())
        .unwrap();
    let in_r = client
        .register_port("in_r", jack::AudioIn::default())
        .unwrap();
    let mut out_l = client
        .register_port("out_l", jack::AudioOut::default())
        .unwrap();
    let mut out_r = client
        .register_port("out_r", jack::AudioOut::default())
        .unwrap();

    let process_callback = move |_: &jack::Client, ps: &jack::ProcessScope| -> jack::Control {
        let out_l_p = out_l.as_mut_slice(ps);
        let out_r_p = out_r.as_mut_slice(ps);
        let in_l_p = in_l.as_slice(ps);
        let in_r_p = in_r.as_slice(ps);
        out_l_p.clone_from_slice(in_l_p);
        out_r_p.clone_from_slice(in_r_p);
        jack::Control::Continue
    };
    let process = jack::ClosureProcessHandler::new(process_callback);

    // Activate the client, which starts the processing.

        Self {
            line_plot: LinePlot::default(),
            // or Rc::new(client.activate...) in case it's needed :
            jclient: client.activate_async(Notifications, process).unwrap(),
        }
    }
}

But typing the result of async_client is not easy, nor having an allocation size for it it seems:

error[E0038]: the trait "ProcessHandler" cannot be made into an object
   --> src/app.rs:108:48
    |
108 |     jclient: (jack::AsyncClient<Notifications, dyn jack::ProcessHandler>, jack::Error),
    |                                                ^^^^^^^^^^^^^^^^^^^^^^^^ "ProcessHandler" cannot be made into an object
    |
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
   --> /home/catseye/.cargo/registry/src/index.crates.io-6f17d22bba15001f/jack-0.11.4/src/client/callbacks.rs:78:11
    |
78  |     const SLOW_SYNC:bool = false;
    |           ^^^^^^^^^ the trait cannot be made into an object because it contains this associated `const`
    = help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `ProcessHandler` for this new enum and using it instead:
              ()
              jack::ClosureProcessHandler<F>

error[E0277]: the size for values of type "(dyn ProcessHandler + 'static)" cannot be known at compilation time
   --> src/app.rs:108:14
    |
108 |     jclient: (jack::AsyncClient<Notifications, dyn jack::ProcessHandler>, jack::Error),
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
    |
    = help: the trait "Sized" is not implemented for "(dyn ProcessHandler + 'static)"
note: required by a bound in "AsyncClient"
   --> /home/catseye/.cargo/registry/src/index.crates.io-6f17d22bba15001f/jack-0.11.4/src/client/async_client.rs:33:27
    |
33  | pub struct AsyncClient<N, P> {
    |                           ^ required by this bound in "AsyncClient"

How do I write my interwoven GUI and jack code to keep the (async_)client around so that I can stop it once it is started?

phstrauss commented 3 months ago

I've changed the jclient type to:

jclient: jack::AsyncClient<Notifications, jack::ClosureProcessHandler<dyn 'static + Send + FnMut(&jack::Client, &jack::ProcessScope) -> jack::Control>>,

and know the compiler seems straight to the point in its message:

error[E0277]: the size for values of type `(dyn for<'a, 'b> FnMut(&'a Client, &'b ProcessScope) -> Control + Send + 'static)` cannot be known at compilation time
  --> src/app.rs:97:14
   |
97 | ...t: jack::AsyncClient<Notifications, jack::ClosureProcessHandler<dyn 'static + Send + FnMut(&jack::Client, &jack::ProcessScope) -> jack::Control...
   |       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
   |
   = help: the trait `Sized` is not implemented for `(dyn for<'a, 'b> FnMut(&'a Client, &'b ProcessScope) -> Control + Send + 'static)`
note: required by a bound in `ClosureProcessHandler`
  --> /home/catseye/.cargo/registry/src/index.crates.io-6f17d22bba15001f/jack-0.11.4/src/client/handler_impls.rs:16:34
   |
16 | pub struct ClosureProcessHandler<F: 'static + Send + FnMut(&Client, &ProcessScope) -> Control> {
   |                                  ^ required by this bound in `ClosureProcessHandler`

For more information about this error, try `rustc --explain E0277`.
error: could not compile `eframe_template` (lib) due to 1 previous error
wmedrano commented 3 months ago

How about:

pub struct SinePlot<N, P> {
    line_plot: LinePlot, // egui plot
    jclient: jack::AsyncClient<N, P>,
}

...

let sine_plot = SinePlot {
    line_plot,
    jclient,
}
phstrauss commented 2 months ago

Yup, thanks!