Hi. I am trying out writing an LV2 plugin with Rust. (LV2 is an open audio plugin standard.) I realize that there is an existing library for this; I'm doing this as a self-study exercise for working with bindgen and integrating with C stuff.
I've written the bare minimum plugin that just changes the amplitude of a signal up and down. Everything seems to work, but only if I include a call to println!
in any of the extern "C"
functions.
For more detail, plugins work by providing the following function in a shared library (in C):
const LV2_Descriptor*
lv2_descriptor(uint32_t index)
{
return index == 0 ? &descriptor : NULL;
}
so that when the plugin host encounters a NULL
it knows to stop calling this function. A descriptor is a struct with a URI (a string) and a set of lifecycle callbacks.
The equivalent Rust I'm using is
#[no_mangle]
pub extern "C" fn lv2_descriptor(index: i32) -> *const LV2_Descriptor {
if index == 0 {
&d
} else {
std::ptr::null()
}
}
Of course there is more code involved that I've omitted in the body of this post, but the above doesn't work when I test it out with jalv (a terminal based lv2 host), which complains that
lilv_plugin_instantiate(): error: No plugin <http://lv2plug.in/plugins/rust-tom-amp> in <file:///usr/local/lib/lv2/tom-rust-amp.lv2/librust_lv2_ffi_amp.so>
which I take to mean that it was able to call the lv2_descriptors
function, but got NULL
. To verify this, I changed the body of this function to just
#[no_mangle]
pub extern "C" fn lv2_descriptor(index: i32) -> *const LV2_Descriptor {
std::ptr::null()
}
which reproduces the same message from jalv. I also tried the opposite, where the function ignores the index and just returns the descriptor every time, which never returned, meaning that this function is being called successfully by the host, but it doesn't find the plugin with the matching URI.
#[no_mangle]
pub extern "C" fn lv2_descriptor(index: i32) -> *const LV2_Descriptor {
&d
}
Here's what is baffling to me, though. When I added a
println!("The index is: {}", index);
to the top of the body of this function, it printed 0
but also worked (the plugin was launched and I was able to use it). I also tried this in another plugin host (Ardour) and verified similar behavior.
The plugin also works if I place the println!
anywhere there is a lifecycle callback. So it seems like the existence of any println!
causes Rust to compile it in a way that changes how this function works, but I'm not sure why and how to clean this up so I don't need to include println!
unnecessarily.
FWIW my Cargo.toml looks like this
[package]
name = "rust-lv2-ffi-amp"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
[build-dependencies]
bindgen = "0.70.1"
and this all works the same way for debug and release.