Open glowcoil opened 3 years ago
Great catch, and thanks for the analysis! Indeed a C API with "two arrays that might be aliased" doesn't go well with Rust's way of doing things.
Here's my take on the options:
vst-rs
to be a safe, non-allocating wrapper around the VST2 API.split
method but change its return type, and plugin code can just add get
and set
in appropriate places. The compiler optimizations that this prevents are exactly the ones that we don't want because they are unsound when the arrays are aliased.My suggestion would be to have both 3 and 4. We would need to duplicate the Inputs
and Outputs
structs into shared and exclusive variants (maybe keep them as they are for the exclusive ones and add a single shared one which can be the same for inputs and outputs). Both split
and zip
would provide the shared variant, and we can add methods to access the exclusive ones.
In VST2, it is possible and even common for hosts to pass the same pointer for both an input and output buffer (source). This means that the API of
AudioBuffer
, which allows mutably borrowing any output buffer at the same time as any input buffer, is unsound.There are several possible solutions to this, each with downsides:
vst-rs
keep scratch buffers allocated for copying aliased buffers into. Has the advantage of not changing the public API, but introduces complexity and overhead.vst-rs
would have to allocate the worst-case amount of output channels × maximum block size, reallocate when maximum block size changes, and possibly split calls to process() if a host lies about the maximum block size. I don't really like this solution since I think it involvesvst-rs
doing too much.&[Cell<f32>]
(like thelv2
crate currently does). This fixes the soundness issue, but it's somewhat cumbersome and limiting as an API (and may end up inhibiting compiler optimizations).split
method and replace it with separateinputs
andoutputs
methods). This makes it more cumbersome to write toy examples that process sample-by-sample directly from input buffers into output buffers, but it actually shouldn't cause problems for any plugin that does even a single layer of intermediate buffering/block-based processing between its input and outputs.The fourth option is my preferred solution. It should also be possible to provide the second and third options as fallback alternatives for when the fourth one is too cumbersome to deal with.