Defining and using C APIs: jack-sys

Friends

I am doing some audio programming and I wish to develop a Jack audio client.

For my purposes my client has to be the Jack timebase master. The details and ins and outs of Jack are not the issue here, but the use of the FFI and callbacks into and from the C API.

The crate jack-sys has the facilities I require, if I could work out how to use them.

I need to call the function: jack_sys::jack_set_timebase_callback and pass it a callback function (it would be a function pointer if I were in C) and a bit of other data.

I cannot figure out how to do it.

This is what I have attempted:
With my callback function:

fn timebase_callback(                                                                                                                                                    
    state: jack_sys::jack_transport_state_t,                                                                                                                             
    nframes: jack_sys::jack_nframes_t,                                                                                                                                   
    pos: *mut jack_sys::jack_position_t,                                                                                                                                 
    new_pos: c_int,                                                                                                                                                      
    arg: *mut c_void,                                                                                                                                                    
) {                                                                                                                                                                      
    println!(                                                                                                                                                            
        "time_base_callback(.., {}, {:?}, {}, {:?});",                                                                                                                   
        nframes, pos, new_pos, arg                                                                                                                                       
    );                                                                                                                                                                   
}                                                                                                                                                                        

Utilising it:

    let (client, _status) =
        jack::Client::new("rust_jack_trans", jack::ClientOptions::NO_START_SERVER).unwrap();
    struct MyVoidPtr;
    let mut my_ptr = MyVoidPtr;
    let my_void_ptr: *mut c_void = &mut my_ptr as *mut _ as *mut c_void;
    jack_sys::jack_set_timebase_callback(client.raw(), 1, Some(timebase_callback), my_void_ptr);

The definition of the callback is incorrect. The error (below in full) says it expects "C" fn and found "Rust fn. Ok. I would like to pass a pointer to my rust function. I clearly am not doing it correctly. How do I do it?

Also while I do not care much about the void pointer for the arguments, yet, the definition of of my_void_ptr seems to be a magic incantation. How does it work?

error[E0308]: mismatched types                                                                                                                                           
   --> src/main.rs:111:64
    |
111 |     jack_sys::jack_set_timebase_callback(client.raw(), 1, Some(timebase_callback), my_void_ptr);
    |                                                                ^^^^^^^^^^^^^^^^^ expected "C" fn, found "Rust" fn
    |
    = note: expected fn pointer `unsafe extern "C" fn(_, _, _, _, _)`
                  found fn item `fn(_, _, _, _, _) {timebase_callback}`

Rust functions use Rust calling convention by default. To declare a function that uses C calling convention it's necessary to use extern "C". For example:

extern "C" fn timebase_callback(
    state: jack_sys::jack_transport_state_t,
    nframes: jack_sys::jack_nframes_t,
    pos: *mut jack_sys::jack_position_t,
    new_pos: c_int,
    arg: *mut c_void,
) {
    println!(
        "time_base_callback(.., {}, {:?}, {}, {:?});",
        nframes, pos, new_pos, arg
    );
}
3 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.