// yabridge: a Wine VST bridge // Copyright (C) 2020-2021 Robbert van der Helm // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #pragma once #include #include #include #include #include #include #include "../utils.h" #include "../vst24.h" #include "common.h" // These constants are limits used by bitsery /** * The maximum number of audio channels supported. Some plugins report a huge * amount of input channels, even though they don't even process any incoming * audio. Renoise seems to report 112 speakers per audio channel, so this limit * is now quite a bit higher than it should have to be. */ constexpr size_t max_audio_channels = 16384; /** * The maximum number of samples in a buffer. */ constexpr size_t max_buffer_size = 16384; /** * The maximum number of MIDI events in a single `VstEvents` struct. */ constexpr size_t max_midi_events = max_buffer_size / sizeof(size_t); /** * The maximum size in bytes of a string or buffer passed through a void pointer * in one of the dispatch functions. This is used to create buffers for plugins * to write strings to. */ [[maybe_unused]] constexpr size_t max_string_length = 64; /** * The maximum size for the buffer we're receiving chunks in. Allows for up to * 50 MB chunks. Hopefully no plugin will come anywhere near this limit, but it * will add up when plugins start to audio include samples in their presets. */ constexpr size_t binary_buffer_size = 50 << 20; /** * Update an `AEffect` object, copying values from `updated_plugin` to `plugin`. * This will copy all flags and regular values, leaving all pointers in `plugin` * untouched. This should be updating the same values as the serialization * function right below this. */ AEffect& update_aeffect(AEffect& plugin, const AEffect& updated_plugin) noexcept; /** * The serialization function for `AEffect` structs. This will s serialize all * of the values but it will not touch any of the pointer fields. That way you * can deserialize to an existing `AEffect` instance. Since we can't always * deserialize directly into an existing `AEffect`, there is also another * function called `update_aeffect()` that copies values from one `AEffect` to * another. Both of these functions should be updating the same values. */ template void serialize(S& s, AEffect& plugin) { s.value4b(plugin.magic); s.value4b(plugin.numPrograms); s.value4b(plugin.numParams); s.value4b(plugin.numInputs); s.value4b(plugin.numOutputs); s.value4b(plugin.flags); s.value4b(plugin.initialDelay); s.value4b(plugin.empty3a); s.value4b(plugin.empty3b); s.value4b(plugin.unkown_float); s.value4b(plugin.uniqueID); s.value4b(plugin.version); } template void serialize(S& s, VstIOProperties& props) { s.container1b(props.data); } template void serialize(S& s, VstMidiKeyName& key_name) { s.container1b(key_name.data); } template void serialize(S& s, VstParameterProperties& props) { s.value4b(props.stepFloat); s.value4b(props.smallStepFloat); s.value4b(props.largeStepFloat); s.container1b(props.label); s.value4b(props.flags); s.value4b(props.minInteger); s.value4b(props.maxInteger); s.value4b(props.stepInteger); s.value4b(props.largeStepInteger); s.container1b(props.shortLabel); s.value2b(props.displayIndex); s.value2b(props.category); s.value2b(props.numParametersInCategory); s.value2b(props.reserved); s.container1b(props.categoryLabel); s.container1b(props.future); } template void serialize(S& s, VstRect& rect) { s.value2b(rect.top); s.value2b(rect.left); s.value2b(rect.right); s.value2b(rect.bottom); } template void serialize(S& s, VstTimeInfo& time_info) { s.value8b(time_info.samplePos); s.value8b(time_info.sampleRate); s.value8b(time_info.nanoSeconds); s.value8b(time_info.ppqPos); s.value8b(time_info.tempo); s.value8b(time_info.barStartPos); s.value8b(time_info.cycleStartPos); s.value8b(time_info.cycleEndPos); s.value4b(time_info.timeSigNumerator); s.value4b(time_info.timeSigDenominator); s.container1b(time_info.empty3); s.value4b(time_info.flags); } /** * Wrapper for chunk data. */ struct ChunkData { std::vector buffer; }; /** * A wrapper around `VstEvents` that stores the data in a vector instead of a * C-style array. Needed until bitsery supports C-style arrays * https://github.com/fraillt/bitsery/issues/28. An advantage of this approach * is that RAII will handle cleanup for us. * * Before serialization the events are read from a C-style array into a vector * using this class's constructor, and after deserializing the original struct * can be reconstructed using the `as_c_events()` method. */ class alignas(16) DynamicVstEvents { public: DynamicVstEvents() noexcept; explicit DynamicVstEvents(const VstEvents& c_events); /** * Construct a `VstEvents` struct from the events vector. This contains a * pointer to that vector's elements, so the returned object should not * outlive this struct. */ VstEvents& as_c_events(); /** * MIDI events are sent in batches. */ std::vector events; template void serialize(S& s) { s.container(events, max_midi_events, [](S& s, VstEvent& event) { s.container1b(event.dump); }); } private: /** * Some buffer we can build a `VstEvents` object in. This object can be * populated with contents of the `VstEvents` vector using the * `as_c_events()` method. * * The reason why this is necessary is because the `VstEvents` struct is * actually a variable size object. In the definition in * `vestige/aeffectx.h` the struct contains a single element `VstEvent` * pointer array, but the actual length of this array is * `VstEvents::numEvents`. Because there is no real limit on the number of * MIDI events the host can send at once we have to build this object on the * heap by hand. */ std::vector vst_events_buffer; }; /** * A wrapper around `VstSpeakerArrangement` that works the same way as the above * wrapper for `VstEvents`. This is needed because the `VstSpeakerArrangement` * struct is actually a variable sized array. Even though it will be very * unlikely that we'll encounter systems with more than 8 speakers, it is * something we should be able to support. * * Before serialization the events are read from a C-style array into a vector * using this class's constructor, and after deserializing the original struct * can be reconstructed using the `as_c_speaker_arrangement()` method. */ class alignas(16) DynamicSpeakerArrangement { public: DynamicSpeakerArrangement() noexcept; explicit DynamicSpeakerArrangement( const VstSpeakerArrangement& speaker_arrangement); /** * Construct a dynamically sized `VstSpeakerArrangement` object based on * this object. */ VstSpeakerArrangement& as_c_speaker_arrangement(); /** * Reconstruct the dynamically sized `VstSpeakerArrangement` object and * return the raw data buffer. Needed to write the results back to the host * since we can't just reassign the object. */ std::vector& as_raw_data(); /** * The flags field from `VstSpeakerArrangement` */ int flags; /** * Information about the speakers in a particular input or output * configuration. */ std::vector speakers; template void serialize(S& s) { s.value4b(flags); s.container( speakers, max_audio_channels, [](S& s, VstSpeaker& speaker) { s.container1b(speaker.data); }); } private: /** * Some buffer we can build a `VstSpeakerArrangement` object in. This object * can be populated using the `as_c_speaker_arrangement()` method. * * This is necessary because the `VstSpeakerArrangement` struct contains a * dynamically sized array of length `VstSpeakerArrangement::num_speakers`. * We build this object in a byte sized vector to make allocating enough * heap space easy and safe. */ std::vector speaker_arrangement_buffer; }; /** * Marker struct to indicate that the other side (the Wine VST host) should send * an updated copy of the plugin's `AEffect` object. Should not be needed since * the plugin should be calling `audioMasterIOChanged()` after it has changed * its object, but some improperly coded plugins will only initialize their * flags, IO properties and parameter counts after `effEditOpen()`. */ struct WantsAEffectUpdate {}; /** * 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, * with the return value indicating the size of the array. */ struct WantsChunkBuffer {}; /** * Marker struct to indicate that the event handler will write a pointer to a * `VstRect` struct into the void pointer. It's also possible that the plugin * doesn't do anything. In that case we'll serialize the response as a null * pointer. */ struct WantsVstRect {}; /** * Marker struct to indicate that the event handler will return a pointer to a * `VstTimeInfo` struct that should be returned transfered. */ struct WantsVstTimeInfo {}; /** * Marker struct to indicate that that the event requires some buffer to write * a C-string into. */ struct WantsString {}; /** * An event as dispatched by the VST host. These events will get forwarded to * the VST host process running under Wine. The fields here mirror those * arguments sent to the `AEffect::dispatch` function. */ struct Vst2Event { /** * VST events are passed a void pointer that can contain a variety of * different data types depending on the event's opcode. This is typically * either: * * - A null pointer, used for simple events. * - A char pointer to a null terminated string, used for passing strings to * the plugin such as when renaming presets. Bitsery handles the * serialization for us. * * NOTE: Bitsery does not support null terminated C-strings without a * known size. We can replace `std::string` with `char*` once it * does for clarity's sake. * * - A byte vector for handling chunk data during `effSetChunk()`. We can't * reuse the regular string handling here since the data may contain null * bytes and `std::string::as_c_str()` might cut off everything after the * first null byte. * - An X11 window handle. * - Specific data structures from `aeffextx.h`. For instance an event with * the opcode `effProcessEvents` the hosts passes a `VstEvents` struct * containing MIDI events, and `audioMasterIOChanged` lets the host know * that the `AEffect` struct has changed. * * - Some empty buffer for the plugin to write its own data to, for instance * for a plugin to report its name or the label for a certain parameter. * There are two separate cases here. This is typically a short null * terminated C-string. We'll assume this as the default case when none of * the above options apply. * * - Either the plugin writes arbitrary data and uses its return value to * indicate how much data was written (i.e. for the `effGetChunk` * opcode). For this we use a vector of bytes instead of a string since * - Or the plugin will write a short null terminated C-string there. * We'll assume that this is the default if none of the above options * apply. * * @relates passthrough_event */ using Payload = std::variant; int opcode; int index; native_intptr_t value; float option; /** * The event dispatch function has a void pointer parameter that's often * used to either pass additional data for the event or to provide a buffer * for the plugin to write a string into. * * The `VstEvents` struct passed for the `effProcessEvents` event contains * an array of pointers. This requires some special handling which is why we * have to use an `std::variant` instead of a simple string buffer. Luckily * Bitsery can do all the hard work for us. */ Payload payload; /** * The same as the above value, but for values passed through the `intptr_t` * value parameter. `effGetSpeakerArrangement` and * `effSetSpeakerArrangement` are the only events that use this. */ std::optional value_payload; template void serialize(S& s) { s.value4b(opcode); s.value4b(index); s.value8b(value); s.value4b(option); s.object(payload); s.ext(value_payload, bitsery::ext::StdOptional(), [](S& s, auto& v) { s.object(v); }); } }; template void serialize(S& s, Vst2Event::Payload& payload) { s.ext(payload, bitsery::ext::StdVariant{ [](S&, std::nullptr_t&) {}, [](S& s, std::string& string) { s.text1b(string, max_string_length); }, [](S& s, ChunkData& chunk) { s.container1b(chunk.buffer, binary_buffer_size); }, [](S& s, native_size_t& window_handle) { s.value8b(window_handle); }, [](S& s, AEffect& effect) { s.object(effect); }, [](S& s, DynamicVstEvents& events) { s.object(events); }, [](S& s, DynamicSpeakerArrangement& speaker_arrangement) { s.object(speaker_arrangement); }, [](S& s, VstIOProperties& props) { s.object(props); }, [](S& s, VstMidiKeyName& key_name) { s.object(key_name); }, [](S& s, VstParameterProperties& props) { s.object(props); }, [](S&, WantsAEffectUpdate&) {}, [](S&, WantsChunkBuffer&) {}, [](S&, WantsVstRect&) {}, [](S&, WantsVstTimeInfo&) {}, [](S&, WantsString&) {}}); } /** * The response for an event. This is usually either: * * - Nothing, on which case only the return value from the callback function * gets passed along. * - A (short) string. * - Some binary blob stored as a byte vector. During `effGetChunk` this will * contain some chunk data that should be written to * `Vst2PluginBridge::chunk_data`. * - A specific struct in response to an event such as `audioMasterGetTime` or * `audioMasterIOChanged`. * - An X11 window pointer for the editor window. * * @relates passthrough_event */ using EventResultPayload = std::variant; template void serialize(S& s, EventResultPayload& payload) { s.ext(payload, bitsery::ext::StdVariant{ [](S&, std::nullptr_t&) {}, [](S& s, std::string& string) { s.text1b(string, max_string_length); }, [](S& s, ChunkData& chunk) { s.container1b(chunk.buffer, binary_buffer_size); }, [](S& s, AEffect& effect) { s.object(effect); }, [&](DynamicSpeakerArrangement& speaker_arrangement) -> void* { return &speaker_arrangement.as_c_speaker_arrangement(); }, [](S& s, VstIOProperties& props) { s.object(props); }, [](S& s, VstMidiKeyName& key_name) { s.object(key_name); }, [](S& s, VstParameterProperties& props) { s.object(props); }, [](S& s, VstRect& rect) { s.object(rect); }, [](S& s, VstTimeInfo& time_info) { s.object(time_info); }}); } /** * AN instance of this should be sent back as a response to an incoming event. */ struct EventResult { /** * The result that should be returned from the dispatch function. */ native_intptr_t return_value; /** * Events typically either just return their return value or write a string * into the void pointer, but sometimes an event response should forward * some kind of special struct. */ EventResultPayload payload; /** * The same as the above value, but for returning values written to the * `intptr_t` value parameter. This is only used during * `effGetSpeakerArrangement`. */ std::optional value_payload; template void serialize(S& s) { s.value8b(return_value); s.object(payload); s.ext(value_payload, bitsery::ext::StdOptional(), [](S& s, auto& v) { s.object(v); }); } }; /** * Represents a call to either `getParameter` or `setParameter`, depending on * whether `value` contains a value or not. */ struct Parameter { int index; std::optional value; template void serialize(S& s) { s.value4b(index); s.ext(value, bitsery::ext::StdOptional(), [](S& s, auto& v) { s.value4b(v); }); } }; /** * The result of a `getParameter` or a `setParameter` call. For `setParameter` * this struct won't contain any values and mostly acts as an acknowledgement * from the Wine VST host. */ struct ParameterResult { std::optional value; template void serialize(S& s) { s.ext(value, bitsery::ext::StdOptional(), [](S& s, auto& v) { s.value4b(v); }); } }; /** * 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()`. */ struct AudioBuffers { /** * 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>> buffers; /** * The number of frames in a sample. If buffers is not empty, then * `buffers[0].size() == sample_frames`. */ int sample_frames; /** * 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 * some cases, more than one) during every processing cycle. */ std::optional current_time_info; /** * Some plugins will also ask for the current process level during audio * processing. To prevent unnecessary expensive callbacks there, we'll * prefetch this information as well. */ int current_process_level; /** * We'll periodically synchronize the realtime priority setting of the * host's audio thread with the Wine plugin host. We'll do this * approximately every ten seconds, as doing this getting and setting * scheduler information has a non trivial amount of overhead (even if it's * only a single microsoecond). */ std::optional new_realtime_priority; template void serialize(S& s) { s.ext( buffers, bitsery::ext::StdVariant{ [](S& s, std::vector>& buffer) { s.container(buffer, max_audio_channels, [](S& s, auto& v) { s.container4b(v, max_buffer_size); }); }, [](S& s, std::vector>& buffer) { s.container(buffer, max_audio_channels, [](S& s, auto& v) { s.container8b(v, max_buffer_size); }); }, }); s.value4b(sample_frames); s.ext(current_time_info, bitsery::ext::StdOptional{}); s.value4b(current_process_level); s.ext(new_realtime_priority, bitsery::ext::StdOptional{}, [](S& s, int& priority) { s.value4b(priority); }); } };