💥 Reimplement VST3 audio processing

In the same way as 50c25c1cf0 did it for
VST2 plugins. Input and output audio data is now stored in a shared
memory buffer instead of being sent over the sockets. This reduces the
bridging overhead to a minimum since copying data was the most expensive
operation we were doing and we now only need to copy the entire buffer
once per processing cycle.
This commit is contained in:
Robbert van der Helm
2021-06-11 13:56:42 +02:00
parent a7d8063db4
commit dec19dc12a
8 changed files with 444 additions and 363 deletions
+23 -8
View File
@@ -182,11 +182,19 @@ uint32 PLUGIN_API Vst3PluginProxyImpl::getLatencySamples() {
tresult PLUGIN_API
Vst3PluginProxyImpl::setupProcessing(Steinberg::Vst::ProcessSetup& setup) {
// TOOD: Set up the shared audio buffers next
return bridge
.send_audio_processor_message(YaAudioProcessor::SetupProcessing{
.instance_id = instance_id(), .setup = setup})
.result;
const YaAudioProcessor::SetupProcessingResponse response =
bridge.send_audio_processor_message(YaAudioProcessor::SetupProcessing{
.instance_id = instance_id(), .setup = setup});
// We have now set up the shared audio buffers on the Wine side, and we'll
// be able to able to connect to them by using the same audio configuration
if (!process_buffers) {
process_buffers.emplace(response.audio_buffers_config);
} else {
process_buffers->resize(response.audio_buffers_config);
}
return response.result;
}
tresult PLUGIN_API Vst3PluginProxyImpl::setProcessing(TBool state) {
@@ -220,9 +228,12 @@ Vst3PluginProxyImpl::process(Steinberg::Vst::ProcessData& data) {
last_audio_thread_priority_synchronization = now;
}
// We reuse this existing object to avoid allocations
// We reuse this existing object to avoid allocations.
// `YaProcessData::repopulate()` will write the input audio to the shared
// audio buffers, so they're not stored within the request object itself.
assert(process_buffers);
process_request.instance_id = instance_id();
process_request.data.repopulate(data);
process_request.data.repopulate(data, *process_buffers);
process_request.new_realtime_priority = new_realtime_priority;
// HACK: This is a bit ugly. This `YaProcessData::Response` object actually
@@ -245,7 +256,11 @@ Vst3PluginProxyImpl::process(Steinberg::Vst::ProcessData& data) {
MessageReference<YaAudioProcessor::Process>(process_request),
process_response);
process_request.data.write_back_outputs(data);
// At this point the shared audio buffers should contain the output audio,
// so we'll write that back to the host along with any metadata (which in
// practice are only the silence flags), as well as any output parameter
// changes and events
process_request.data.write_back_outputs(data, *process_buffers);
return process_response.result;
}
+21 -8
View File
@@ -438,14 +438,16 @@ class Vst3PluginProxyImpl : public Vst3PluginProxy {
std::atomic_size_t current_context_menu_id;
/**
* NOTE: We'll reuse the request objects for the audio processor so we can
* keep the process data object (which contains vectors and other heap
* allocated data structure) alive. We'll then just fill this object
* with new data every processing cycle to prevent allocations. Then,
* we pass a `MessageReference<YaAudioProcessor::Process>` to our
* sockets. This together with `bitisery::ext::MessageReference` will
* let us serialize from and to existing objects without having to
* copy or reallocate them.
* We'll reuse the request objects for the audio processor so we can keep
* the process data object (which contains vectors and other heap allocated
* data structure) alive. We'll then just fill this object with new data
* every processing cycle to prevent allocations. Then, we pass a
* `MessageReference<YaAudioProcessor::Process>` to our sockets. This
* together with `bitisery::ext::MessageReference` will let us serialize
* from and to existing objects without having to copy or reallocate them.
*
* To reduce the amount of copying during audio processing we'll write the
* audio data to a shared memory object stored in `process_buffers` first.
*/
YaAudioProcessor::Process process_request;
@@ -456,6 +458,17 @@ class Vst3PluginProxyImpl : public Vst3PluginProxy {
*/
YaAudioProcessor::ProcessResponse process_response;
/**
* A shared memory object to share audio buffers between the native plugin
* and the Wine plugin host. Copying audio is the most significant source of
* bridging overhead during audio processing, and this way we can reduce the
* amount of copies required to only once for the input audio, and one more
* copy when copying the results back to the host.
*
* This will be set up during `IAudioProcessor::setupProcessing()`.
*/
std::optional<AudioShmBuffer> process_buffers;
// Caches
/**