mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-05-14 20:40:03 +02:00
💥 Reimplement VST2 audio processing
We now use shared memory to store the input and output audio buffers. This means that we have to copy less data every processing cycle, since a single copy to and a single copy from the shared memory object suffices now. This should reduce the DSP load for VST2 plugins (especially when used in a plugin group) marginally to significantly depending on the plugins used and the system configuration.
This commit is contained in:
@@ -24,6 +24,7 @@
|
||||
#include <vestige/aeffectx.h>
|
||||
#include <boost/container/small_vector.hpp>
|
||||
|
||||
#include "../audio-shm.h"
|
||||
#include "../bitsery/ext/in-place-variant.h"
|
||||
#include "../bitsery/traits/small-vector.h"
|
||||
#include "../utils.h"
|
||||
@@ -226,6 +227,26 @@ struct WantsAEffectUpdate {
|
||||
void serialize(S&) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* Marker struct to indicate that the Wine plugin host should set up shared
|
||||
* memory buffers for audio processing. The size for this depends on the maximum
|
||||
* block size indicated by the host using `effSetBlockSize()` and whether the
|
||||
* host called `effSetProcessPrecision()` to indicate that the plugin is going
|
||||
* to receive double precision audio or not.
|
||||
*
|
||||
* HACK: We need to do some manual work after the plugin has handled
|
||||
* `effMainsChanged`, and our current setup doesn't allow us to do that
|
||||
* from the `passthrough_event()` function. So for the time being we'll
|
||||
* have to do this manually in the `receive_events()` handler, see
|
||||
* `Vst2Bridge::run()`.
|
||||
*/
|
||||
struct WantsAudioShmBufferConfig {
|
||||
using Response = AudioShmBuffer::Config;
|
||||
|
||||
template <typename S>
|
||||
void serialize(S&) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* Marker struct to indicate that that the event writes arbitrary data into one
|
||||
* of its own buffers and uses the void pointer to store start of that data,
|
||||
@@ -295,6 +316,7 @@ struct Vst2EventResult {
|
||||
using Payload = std::variant<std::nullptr_t,
|
||||
std::string,
|
||||
AEffect,
|
||||
AudioShmBuffer::Config,
|
||||
ChunkData,
|
||||
DynamicSpeakerArrangement,
|
||||
VstIOProperties,
|
||||
@@ -395,6 +417,7 @@ struct Vst2Event {
|
||||
DynamicVstEvents,
|
||||
DynamicSpeakerArrangement,
|
||||
WantsAEffectUpdate,
|
||||
WantsAudioShmBufferConfig,
|
||||
WantsChunkBuffer,
|
||||
VstIOProperties,
|
||||
VstMidiKeyName,
|
||||
@@ -485,29 +508,28 @@ struct Parameter {
|
||||
};
|
||||
|
||||
/**
|
||||
* A buffer of audio for the plugin to process, or the response of that
|
||||
* processing. The number of samples is encoded in each audio buffer's length.
|
||||
* This is used for both `process()/processReplacing()` and
|
||||
* `processDoubleReplacing()`.
|
||||
* When the host calls `processReplacing()`, `processDoubleReplacing()`, or the
|
||||
* deprecated `process()` function on our VST2 plugin, we'll write the input
|
||||
* buffers to an `AudioShmBuffer` object that's shared between the native plugin
|
||||
* an the Wine plugin host, and we'll then send this object to the Wine plugin
|
||||
* host with the rest of the .
|
||||
*/
|
||||
struct AudioBuffers {
|
||||
using Response = AudioBuffers;
|
||||
struct Vst2ProcessRequest {
|
||||
using Response = Ack;
|
||||
|
||||
/**
|
||||
* An audio buffer for each of the plugin's audio channels. This uses floats
|
||||
* or doubles depending on whether `process()/processReplacing()` or
|
||||
* `processDoubleReplacing()` got called.
|
||||
*/
|
||||
std::variant<std::vector<std::vector<float>>,
|
||||
std::vector<std::vector<double>>>
|
||||
buffers;
|
||||
|
||||
/**
|
||||
* The number of frames in a sample. If buffers is not empty, then
|
||||
* `buffers[0].size() == sample_frames`.
|
||||
* The number of samples per channel. We'll trust the host to never provide
|
||||
* more samples than the maximum it indicated during `effSetBlockSize`.
|
||||
*/
|
||||
int sample_frames;
|
||||
|
||||
/**
|
||||
* Whether the host calling `processDoubleReplacing()` or
|
||||
* `processReplacing()`. On Linux only REAPER seems to use double precision
|
||||
* audio.
|
||||
*/
|
||||
bool double_precision;
|
||||
|
||||
/**
|
||||
* We'll prefetch the current transport information as part of handling an
|
||||
* audio processing call. This lets us a void an unnecessary callback (or in
|
||||
@@ -533,21 +555,8 @@ struct AudioBuffers {
|
||||
|
||||
template <typename S>
|
||||
void serialize(S& s) {
|
||||
s.ext(
|
||||
buffers,
|
||||
bitsery::ext::InPlaceVariant{
|
||||
[](S& s, std::vector<std::vector<float>>& buffer) {
|
||||
s.container(buffer, max_audio_channels, [](S& s, auto& v) {
|
||||
s.container4b(v, max_buffer_size);
|
||||
});
|
||||
},
|
||||
[](S& s, std::vector<std::vector<double>>& buffer) {
|
||||
s.container(buffer, max_audio_channels, [](S& s, auto& v) {
|
||||
s.container8b(v, max_buffer_size);
|
||||
});
|
||||
},
|
||||
});
|
||||
s.value4b(sample_frames);
|
||||
s.value1b(double_precision);
|
||||
|
||||
s.ext(current_time_info, bitsery::ext::StdOptional{});
|
||||
s.value4b(current_process_level);
|
||||
|
||||
Reference in New Issue
Block a user