From f9924e0e8152d7895a84f2b3d0b5ae556169cc10 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 24 Sep 2022 12:28:15 +0100 Subject: [PATCH] Replace vestige with a more liberally-licensed version Signed-off-by: falkTX --- LICENSING.md | 14 +- distrho/DistrhoInfo.hpp | 9 - distrho/src/DistrhoPluginVST2.cpp | 348 +++++----- distrho/src/xaymar-vst2/LICENSE | 11 + distrho/src/xaymar-vst2/README.md | 21 + distrho/src/xaymar-vst2/vst.h | 1008 +++++++++++++++++++++++++++++ 6 files changed, 1234 insertions(+), 177 deletions(-) create mode 100644 distrho/src/xaymar-vst2/LICENSE create mode 100644 distrho/src/xaymar-vst2/README.md create mode 100644 distrho/src/xaymar-vst2/vst.h diff --git a/LICENSING.md b/LICENSING.md index c7afdcdc..37595800 100644 --- a/LICENSING.md +++ b/LICENSING.md @@ -2,8 +2,7 @@ Even though DPF is quite liberally licensed, not all plugin formats follow the same ideals. This is usually due to plugin APIs/headers being tied to a specific license or having commercial restrictions. -This file describes the licensing that applies to each individual plugin format as a way to make it clear what is possible and compatible. -Note that if you are making GPLv2+ licensed plugins this does not apply to you, as so far everything is GPLv2+ compatible. +This file describes the licensing that applies to each individual plugin format as a way to make it clear what is possible and compatible. Regardless of target format, DPF itself needs to be mentioned in attribution. See the [LICENSE](LICENSE) file for copyright details. @@ -14,7 +13,7 @@ See the [LICENSE](LICENSE) file for copyright details. | LADSPA | LGPLv2.1+ | ??? (*) | 2000-2002 Richard W. E. Furse, Paul Barton-Davis, Stefan Westerfeld | | DSSI | LGPLv2.1+ | ??? (*) | **DSSI**: 2004, 2009 Chris Cannam, Steve Harris and Sean Bolton;
**ALSA**: 1998-2001 Jaroslav Kysela, Abramo Bagnara, Takashi Iwai | | LV2 | ISC | Copyright attribution | 2006-2020 Steve Harris, David Robillard;
2000-2002 Richard W.E. Furse, Paul Barton-Davis, Stefan Westerfeld | -| VST2 | GPLv2+ or commercial | Must be GPLv2+ compatible or alternatively use Steinberg VST2 SDK (no longer available for new plugins) | GPLv2+ compatible license or custom agreement with Steinberg | +| VST2 | BSD-3 | Copyright attribution | 2020-2022 Michael Fabian 'Xaymar' Dirks | | VST3 | ISC | Copyright attribution | (none, only DPF files used) | | CLAP | MIT | Copyright attribution | 2014-2022 Alexandre Bique | @@ -33,12 +32,9 @@ These formats are very limited and not much used anymore anyway, feel free to sk ### VST2 special note -By default DPF uses the free reverse-engineered [vestige header](distrho/src/vestige/vestige.h) file. -This file is GPLv2+ licensed, so that applies to plugins built with it as well. -You can alternatively build DPF-based VST2 plugins using the official Steinberg VST2 SDK, -simply set the `VESTIGE_HEADER` compiler macro to `0` during build. -You will need to provide your own VST2 SDK files then, as DPF does not ship with them. -Note there are legal issues surrounding releasing new VST2 plugins using the official SDK, as that is no longer supported by Steinberg. +The DPF's VST2 implementation uses https://github.com/Xaymar/vst2sdk which is a liberally-licensed "clean room" untainted reverse engineered "SDK" for the VST2 interface. +Previously "vestige" was used, but was problematic due to it being GPLv2 licensed. +With the Xaymar's work, both open-source and proprietary plugins can be created from the same source, which helps in maintenance on DPF side. ### VST3 special note diff --git a/distrho/DistrhoInfo.hpp b/distrho/DistrhoInfo.hpp index e954d393..01fa3932 100644 --- a/distrho/DistrhoInfo.hpp +++ b/distrho/DistrhoInfo.hpp @@ -876,15 +876,6 @@ START_NAMESPACE_DISTRHO */ #define DGL_USE_OPENGL3 -/** - Whether to use the GPLv2+ vestige header instead of the official Steinberg VST2 SDK.@n - This is a boolean, and enabled (set to 1) by default.@n - Set this to 0 in order to create non-GPL binaries. - (but then at your own discretion in regards to Steinberg licensing)@n - When set to 0, DPF will import the VST2 definitions from `"vst/aeffectx.h"` (not shipped with DPF). - */ -#define VESTIGE_HEADER 1 - /** @} */ /* ------------------------------------------------------------------------------------------------------------ diff --git a/distrho/src/DistrhoPluginVST2.cpp b/distrho/src/DistrhoPluginVST2.cpp index 54490725..31747dc5 100644 --- a/distrho/src/DistrhoPluginVST2.cpp +++ b/distrho/src/DistrhoPluginVST2.cpp @@ -25,50 +25,72 @@ # include "../extra/RingBuffer.hpp" #endif -#ifndef __cdecl -# define __cdecl -#endif - -#ifndef VESTIGE_HEADER -# define VESTIGE_HEADER 1 -#endif -#define VST_FORCE_DEPRECATED 0 - #include #include #include #include -#if VESTIGE_HEADER -# include "vestige/vestige.h" -#define effFlagsProgramChunks (1 << 5) -#define effSetProgramName 4 -#define effGetParamLabel 6 -#define effGetParamDisplay 7 -#define effGetChunk 23 -#define effSetChunk 24 -#define effCanBeAutomated 26 -#define effGetProgramNameIndexed 29 -#define effGetPlugCategory 35 -#define effVendorSpecific 50 -#define effEditKeyDown 59 -#define effEditKeyUp 60 -#define kVstVersion 2400 -struct ERect { - int16_t top, left, bottom, right; -}; -#else -# include "vst/aeffectx.h" +#ifndef __stdcall +# define __stdcall #endif +#include "xaymar-vst2/vst.h" + +extern "C" { + +// define the midi stuff ourselves +typedef struct _VstMidiEvent { + int32_t type; + int32_t byteSize; + int32_t deltaFrames; + int32_t _ignore1[3]; + char midiData[4]; + char _ignore2[4]; +} VstMidiEvent; + +typedef union _VstEvent { + int32_t type; + VstMidiEvent midi; // type 1 +} VstEvent; + +typedef struct _HostVstEvents { + int numEvents; + void* reserved; + const VstEvent* events[]; +} HostVstEvents; + +typedef struct _PluginVstEvents { + int numEvents; + void* reserved; + VstEvent* events[1]; +} PluginVstEvents; + +// info from online documentation of VST provided by Steinberg +typedef struct _VstTimeInfo { + double samplePos; + double sampleRate; + double nanoSeconds; + double ppqPos; + double tempo; + double barStartPos; + double cycleStartPos; + double cycleEndPos; + int32_t timeSigNumerator; + int32_t timeSigDenominator; + int32_t smpteOffset; + int32_t smpteFrameRate; + int32_t samplesToNextClock; + int32_t flags; +} VstTimeInfo; + +} + START_NAMESPACE_DISTRHO // -------------------------------------------------------------------------------------------------------------------- typedef std::map StringMap; -static constexpr const int kVstMidiEventSize = static_cast(sizeof(VstMidiEvent)); - #if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT static const writeMidiFunc writeMidiCallback = nullptr; #endif @@ -136,8 +158,8 @@ static const setStateFunc setStateCallback = nullptr; class UIVst { public: - UIVst(const audioMasterCallback audioMaster, - AEffect* const effect, + UIVst(const vst_host_callback audioMaster, + vst_effect* const effect, ParameterAndNotesHelper* const uiHelper, PluginExporter* const plugin, const intptr_t winId, const float scaleFactor) @@ -259,7 +281,7 @@ public: // ------------------------------------------------------------------- protected: - inline intptr_t hostCallback(const int32_t opcode, + inline intptr_t hostCallback(const VST_HOST_OPCODE opcode, const int32_t index = 0, const intptr_t value = 0, void* const ptr = nullptr, @@ -270,7 +292,7 @@ protected: void editParameter(const uint32_t index, const bool started) const { - hostCallback(started ? audioMasterBeginEdit : audioMasterEndEdit, index); + hostCallback(started ? VST_HOST_OPCODE_2B : VST_HOST_OPCODE_2C, index); } void setParameterValue(const uint32_t index, const float realValue) @@ -279,7 +301,7 @@ protected: const float perValue(ranges.getNormalizedValue(realValue)); fPlugin->setParameterValue(index, realValue); - hostCallback(audioMasterAutomate, index, 0, nullptr, perValue); + hostCallback(VST_HOST_OPCODE_00, index, 0, nullptr, perValue); } void setSize(uint width, uint height) @@ -289,7 +311,7 @@ protected: width /= scaleFactor; height /= scaleFactor; # endif - hostCallback(audioMasterSizeWindow, width, height); + hostCallback(VST_HOST_OPCODE_0F, width, height); } # if DISTRHO_PLUGIN_WANT_MIDI_INPUT @@ -313,8 +335,8 @@ protected: private: // Vst stuff - const audioMasterCallback fAudioMaster; - AEffect* const fEffect; + const vst_host_callback fAudioMaster; + vst_effect* const fEffect; ParameterAndNotesHelper* const fUiHelper; PluginExporter* const fPlugin; @@ -370,7 +392,7 @@ private: class PluginVst : public ParameterAndNotesHelper { public: - PluginVst(const audioMasterCallback audioMaster, AEffect* const effect) + PluginVst(const vst_host_callback audioMaster, vst_effect* const effect) : fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback, nullptr), fAudioMaster(audioMaster), fEffect(effect) @@ -454,10 +476,10 @@ public: switch (opcode) { - case effGetProgram: + case VST_EFFECT_OPCODE_03: // get program return 0; - case effSetProgramName: + case VST_EFFECT_OPCODE_04: // set program name if (char* const programName = (char*)ptr) { strncpy(fProgramName, programName, 32); @@ -465,7 +487,7 @@ public: } break; - case effGetProgramName: + case VST_EFFECT_OPCODE_05: // get program name if (char* const programName = (char*)ptr) { strncpy(programName, fProgramName, 24); @@ -473,7 +495,7 @@ public: } break; - case effGetProgramNameIndexed: + case VST_EFFECT_OPCODE_1D: // get program name indexed if (char* const programName = (char*)ptr) { strncpy(programName, fProgramName, 24); @@ -481,7 +503,7 @@ public: } break; - case effGetParamDisplay: + case VST_EFFECT_OPCODE_PARAM_GETVALUE: if (ptr != nullptr && index < static_cast(fPlugin.getParameterCount())) { const uint32_t hints = fPlugin.getParameterHints(index); @@ -519,7 +541,7 @@ public: } break; - case effSetSampleRate: + case VST_EFFECT_OPCODE_SET_SAMPLE_RATE: fPlugin.setSampleRate(opt, true); #if DISTRHO_PLUGIN_HAS_UI @@ -528,26 +550,26 @@ public: #endif break; - case effSetBlockSize: + case VST_EFFECT_OPCODE_SET_BLOCK_SIZE: fPlugin.setBufferSize(value, true); break; - case effMainsChanged: + case VST_EFFECT_OPCODE_SUSPEND: if (value != 0) { #if DISTRHO_PLUGIN_WANT_MIDI_INPUT fMidiEventCount = 0; // tell host we want MIDI events - hostCallback(audioMasterWantMidi); + hostCallback(VST_HOST_OPCODE_06); #endif // deactivate for possible changes fPlugin.deactivateIfNeeded(); // check if something changed - const uint32_t bufferSize = static_cast(hostCallback(audioMasterGetBlockSize)); - const double sampleRate = static_cast(hostCallback(audioMasterGetSampleRate)); + const uint32_t bufferSize = static_cast(hostCallback(VST_HOST_OPCODE_11)); + const double sampleRate = static_cast(hostCallback(VST_HOST_OPCODE_10)); if (bufferSize != 0) fPlugin.setBufferSize(bufferSize, true); @@ -564,7 +586,7 @@ public: break; #if DISTRHO_PLUGIN_HAS_UI - case effEditGetRect: + case VST_EFFECT_OPCODE_WINDOW_GETRECT: if (fVstUI != nullptr) { fVstRect.right = fVstUI->getWidth(); @@ -597,12 +619,13 @@ public: fVstRect.bottom /= scaleFactor; #endif } - *(ERect**)ptr = &fVstRect; + *(vst_rect**)ptr = &fVstRect; return 1; - case effEditOpen: - delete fVstUI; // hosts which don't pair effEditOpen/effEditClose calls (Minihost Modular) + case VST_EFFECT_OPCODE_WINDOW_CREATE: + delete fVstUI; // for hosts which don't pair create/destroy calls (Minihost Modular) fVstUI = nullptr; + { # if DISTRHO_OS_MAC if (! fUsingNsView) @@ -643,7 +666,7 @@ public: } break; - case effEditClose: + case VST_EFFECT_OPCODE_WINDOW_DESTROY: if (fVstUI != nullptr) { delete fVstUI; @@ -652,18 +675,18 @@ public: } break; - case effEditIdle: + case VST_EFFECT_OPCODE_13: // window idle if (fVstUI != nullptr) fVstUI->idle(); break; # if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI - case effEditKeyDown: + case VST_EFFECT_OPCODE_3B: // key down if (fVstUI != nullptr) return fVstUI->handlePluginKeyEvent(true, index, value); break; - case effEditKeyUp: + case VST_EFFECT_OPCODE_3C: // key up if (fVstUI != nullptr) return fVstUI->handlePluginKeyEvent(false, index, value); break; @@ -671,7 +694,7 @@ public: #endif // DISTRHO_PLUGIN_HAS_UI #if DISTRHO_PLUGIN_WANT_STATE - case effGetChunk: + case VST_EFFECT_OPCODE_17: // get chunk { if (ptr == nullptr) return 0; @@ -758,7 +781,7 @@ public: return ret; } - case effSetChunk: + case VST_EFFECT_OPCODE_18: // set chunk { if (value <= 1 || ptr == nullptr) return 0; @@ -842,39 +865,41 @@ public: #endif // DISTRHO_PLUGIN_WANT_STATE #if DISTRHO_PLUGIN_WANT_MIDI_INPUT - case effProcessEvents: + case VST_EFFECT_OPCODE_19: // process events if (! fPlugin.isActive()) { // host has not activated the plugin yet, nasty! - vst_dispatcher(effMainsChanged, 0, 1, nullptr, 0.0f); + vst_dispatcher(VST_EFFECT_OPCODE_SUSPEND, 0, 1, nullptr, 0.0f); } - if (const VstEvents* const events = (const VstEvents*)ptr) + if (const HostVstEvents* const events = (const HostVstEvents*)ptr) { if (events->numEvents == 0) break; for (int i=0, count=events->numEvents; i < count; ++i) { - const VstMidiEvent* const vstMidiEvent((const VstMidiEvent*)events->events[i]); + const VstEvent* const vstEvent = events->events[i]; - if (vstMidiEvent == nullptr) + if (vstEvent == nullptr) break; - if (vstMidiEvent->type != kVstMidiType) + if (vstEvent->type != 1) continue; if (fMidiEventCount >= kMaxMidiEvents) break; + const VstMidiEvent& vstMidiEvent(events->events[i]->midi); + MidiEvent& midiEvent(fMidiEvents[fMidiEventCount++]); - midiEvent.frame = vstMidiEvent->deltaFrames; + midiEvent.frame = vstMidiEvent.deltaFrames; midiEvent.size = 3; - std::memcpy(midiEvent.data, vstMidiEvent->midiData, sizeof(uint8_t)*3); + std::memcpy(midiEvent.data, vstMidiEvent.midiData, sizeof(uint8_t)*3); } } break; #endif - case effCanBeAutomated: + case VST_EFFECT_OPCODE_PARAM_ISAUTOMATABLE: if (index < static_cast(fPlugin.getParameterCount())) { const uint32_t hints(fPlugin.getParameterHints(index)); @@ -885,7 +910,7 @@ public: } break; - case effCanDo: + case VST_EFFECT_OPCODE_SUPPORTS: if (const char* const canDo = (const char*)ptr) { #if DISTRHO_OS_MAC && DISTRHO_PLUGIN_HAS_UI @@ -924,9 +949,9 @@ public: } break; - case effVendorSpecific: + case VST_EFFECT_OPCODE_CUSTOM: #if DISTRHO_PLUGIN_HAS_UI && !defined(DISTRHO_OS_MAC) - if (index == CCONST('P', 'r', 'e', 'S') && value == CCONST('A', 'e', 'C', 's')) + if (index == d_cconst('P', 'r', 'e', 'S') && value == d_cconst('A', 'e', 'C', 's')) { if (d_isEqual(fLastScaleFactor, opt)) break; @@ -948,13 +973,13 @@ public: return 0; } - float vst_getParameter(const int32_t index) + float vst_getParameter(const uint32_t index) { const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); return ranges.getNormalizedValue(fPlugin.getParameterValue(index)); } - void vst_setParameter(const int32_t index, const float value) + void vst_setParameter(const uint32_t index, const float value) { const uint32_t hints = fPlugin.getParameterHints(index); const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); @@ -986,7 +1011,7 @@ public: if (! fPlugin.isActive()) { // host has not activated the plugin yet, nasty! - vst_dispatcher(effMainsChanged, 0, 1, nullptr, 0.0f); + vst_dispatcher(VST_EFFECT_OPCODE_SUSPEND, 0, 1, nullptr, 0.0f); } if (sampleFrames <= 0) @@ -996,22 +1021,22 @@ public: } #if DISTRHO_PLUGIN_WANT_TIMEPOS - static const int kWantVstTimeFlags(kVstTransportPlaying|kVstPpqPosValid|kVstTempoValid|kVstTimeSigValid); + static constexpr const int kWantVstTimeFlags = 0x2602; - if (const VstTimeInfo* const vstTimeInfo = (const VstTimeInfo*)hostCallback(audioMasterGetTime, 0, kWantVstTimeFlags)) + if (const VstTimeInfo* const vstTimeInfo = (const VstTimeInfo*)hostCallback(VST_HOST_OPCODE_07, 0, kWantVstTimeFlags)) { fTimePosition.frame = vstTimeInfo->samplePos; - fTimePosition.playing = vstTimeInfo->flags & kVstTransportPlaying; + fTimePosition.playing = vstTimeInfo->flags & 0x2; // ticksPerBeat is not possible with VST2 fTimePosition.bbt.ticksPerBeat = 1920.0; - if (vstTimeInfo->flags & kVstTempoValid) + if (vstTimeInfo->flags & 0x400) fTimePosition.bbt.beatsPerMinute = vstTimeInfo->tempo; else fTimePosition.bbt.beatsPerMinute = 120.0; - if ((vstTimeInfo->flags & (kVstPpqPosValid|kVstTimeSigValid)) == (kVstPpqPosValid|kVstTimeSigValid)) + if ((vstTimeInfo->flags & 0x2200) == 0x2200) { const double ppqPos = std::abs(vstTimeInfo->ppqPos); const int ppqPerBar = vstTimeInfo->timeSigNumerator * 4 / vstTimeInfo->timeSigDenominator; @@ -1091,8 +1116,8 @@ private: PluginExporter fPlugin; // VST stuff - const audioMasterCallback fAudioMaster; - AEffect* const fEffect; + const vst_host_callback fAudioMaster; + vst_effect* const fEffect; // Temporary data char fProgramName[32]; @@ -1108,9 +1133,9 @@ private: // UI stuff #if DISTRHO_PLUGIN_HAS_UI - UIVst* fVstUI; - ERect fVstRect; - float fLastScaleFactor; + UIVst* fVstUI; + vst_rect fVstRect; + float fLastScaleFactor; # if DISTRHO_OS_MAC bool fUsingNsView; # endif @@ -1127,7 +1152,7 @@ private: // ------------------------------------------------------------------- // host callback - intptr_t hostCallback(const int32_t opcode, + intptr_t hostCallback(const VST_HOST_OPCODE opcode, const int32_t index = 0, const intptr_t value = 0, void* const ptr = nullptr, @@ -1185,7 +1210,7 @@ private: } const ParameterRanges& ranges(fPlugin.getParameterRanges(i)); - hostCallback(audioMasterAutomate, i, 0, nullptr, ranges.getNormalizedValue(curValue)); + hostCallback(VST_HOST_OPCODE_00, i, 0, nullptr, ranges.getNormalizedValue(curValue)); } } @@ -1200,7 +1225,7 @@ private: #if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST bool requestParameterValueChange(const uint32_t index, const float value) { - hostCallback(audioMasterAutomate, index, 0, nullptr, value); + hostCallback(VST_HOST_OPCODE_00, index, 0, nullptr, value); return true; } @@ -1216,23 +1241,20 @@ private: if (midiEvent.size > 4) return true; - VstEvents vstEvents; - std::memset(&vstEvents, 0, sizeof(VstEvents)); - - VstMidiEvent vstMidiEvent; - std::memset(&vstMidiEvent, 0, sizeof(VstMidiEvent)); + PluginVstEvents vstEvents = {}; + VstMidiEvent vstMidiEvent = {}; vstEvents.numEvents = 1; vstEvents.events[0] = (VstEvent*)&vstMidiEvent; - vstMidiEvent.type = kVstMidiType; - vstMidiEvent.byteSize = kVstMidiEventSize; + vstMidiEvent.type = 1; + vstMidiEvent.byteSize = static_cast(sizeof(VstMidiEvent));; vstMidiEvent.deltaFrames = midiEvent.frame; for (uint8_t i=0; ipluginPtr != nullptr) return 1; - const audioMasterCallback audioMaster = exteffect->audioMaster; + const vst_host_callback audioMaster = exteffect->audioMaster; - d_nextBufferSize = audioMaster(effect, audioMasterGetBlockSize, 0, 0, nullptr, 0.0f); - d_nextSampleRate = audioMaster(effect, audioMasterGetSampleRate, 0, 0, nullptr, 0.0f); + d_nextBufferSize = audioMaster(effect, VST_HOST_OPCODE_11, 0, 0, nullptr, 0.0f); + d_nextSampleRate = audioMaster(effect, VST_HOST_OPCODE_10, 0, 0, nullptr, 0.0f); d_nextCanRequestParameterValueChanges = true; // some hosts are not ready at this point or return 0 buffersize/samplerate @@ -1360,7 +1382,7 @@ static intptr_t vst_dispatcherCallback(AEffect* const effect, const int32_t opco } return 0; - case effClose: + case VST_EFFECT_OPCODE_DESTROY: if (ExtendedAEffect* const exteffect = getExtendedEffect(effect)) { // delete plugin object @@ -1385,7 +1407,7 @@ static intptr_t vst_dispatcherCallback(AEffect* const effect, const int32_t opco } return 0; - case effGetParamLabel: + case VST_EFFECT_OPCODE_PARAM_GETLABEL: if (ptr != nullptr && index < static_cast(sPlugin->getParameterCount())) { strncpy((char*)ptr, sPlugin->getParameterUnit(index), 8); @@ -1393,7 +1415,7 @@ static intptr_t vst_dispatcherCallback(AEffect* const effect, const int32_t opco } return 0; - case effGetParamName: + case VST_EFFECT_OPCODE_PARAM_GETNAME: if (ptr != nullptr && index < static_cast(sPlugin->getParameterCount())) { const String& shortName(sPlugin->getParameterShortName(index)); @@ -1405,25 +1427,25 @@ static intptr_t vst_dispatcherCallback(AEffect* const effect, const int32_t opco } return 0; - case effGetParameterProperties: + case VST_EFFECT_OPCODE_38: // FIXME VST_EFFECT_OPCODE_GET_PARAMETER_PROPERTIES is wrong by 1 if (ptr != nullptr && index < static_cast(sPlugin->getParameterCount())) { - if (VstParameterProperties* const properties = (VstParameterProperties*)ptr) + if (vst_parameter_properties* const properties = (vst_parameter_properties*)ptr) { - memset(properties, 0, sizeof(VstParameterProperties)); + memset(properties, 0, sizeof(vst_parameter_properties)); // full name - strncpy(properties->label, + strncpy(properties->name, sPlugin->getParameterName(index), - sizeof(properties->label)); + sizeof(properties->name)); // short name const String& shortName(sPlugin->getParameterShortName(index)); if (shortName.isNotEmpty()) - strncpy(properties->shortLabel, + strncpy(properties->label, sPlugin->getParameterShortName(index), - sizeof(properties->shortLabel)); + sizeof(properties->label)); // parameter hints const uint32_t hints = sPlugin->getParameterHints(index); @@ -1433,20 +1455,20 @@ static intptr_t vst_dispatcherCallback(AEffect* const effect, const int32_t opco if (hints & kParameterIsBoolean) { - properties->flags |= kVstParameterIsSwitch; + properties->flags |= VST_PARAMETER_FLAGS_SWITCH; } if (hints & kParameterIsInteger) { const ParameterRanges& ranges(sPlugin->getParameterRanges(index)); - properties->flags |= kVstParameterUsesIntegerMinMax; - properties->minInteger = static_cast(ranges.min); - properties->maxInteger = static_cast(ranges.max); + properties->flags |= VST_PARAMETER_FLAGS_INTEGER_LIMITS; + properties->min_value_i32 = static_cast(ranges.min); + properties->max_value_i32 = static_cast(ranges.max); } if (hints & kParameterIsLogarithmic) { - properties->flags |= kVstParameterCanRamp; + properties->flags |= VST_PARAMETER_FLAGS_UNKNOWN6; // can ramp } // parameter group (category in vst) @@ -1461,10 +1483,11 @@ static intptr_t vst_dispatcherCallback(AEffect* const effect, const int32_t opco if (portGroup.groupId == groupId) { + properties->flags |= VST_PARAMETER_FLAGS_CATEGORY; properties->category = i + 1; - strncpy(properties->categoryLabel, + strncpy(properties->category_label, portGroup.name.buffer(), - sizeof(properties->categoryLabel)); + sizeof(properties->category_label)); break; } } @@ -1473,7 +1496,7 @@ static intptr_t vst_dispatcherCallback(AEffect* const effect, const int32_t opco { for (uint32_t i=0, count=sPlugin->getParameterCount(); i < count; ++i) if (sPlugin->getParameterGroupId(i) == groupId) - ++properties->numParametersInCategory; + ++properties->num_parameters_in_category; } } @@ -1482,14 +1505,14 @@ static intptr_t vst_dispatcherCallback(AEffect* const effect, const int32_t opco } return 0; - case effGetPlugCategory: + case VST_EFFECT_OPCODE_EFFECT_CATEGORY: #if DISTRHO_PLUGIN_IS_SYNTH - return kPlugCategSynth; + return VST_CATEGORY_02; #else - return kPlugCategEffect; + return VST_CATEGORY_01; #endif - case effGetEffectName: + case VST_EFFECT_OPCODE_EFFECT_NAME: if (char* const cptr = (char*)ptr) { strncpy(cptr, sPlugin->getName(), 32); @@ -1497,7 +1520,7 @@ static intptr_t vst_dispatcherCallback(AEffect* const effect, const int32_t opco } return 0; - case effGetVendorString: + case VST_EFFECT_OPCODE_VENDOR_NAME: if (char* const cptr = (char*)ptr) { strncpy(cptr, sPlugin->getMaker(), 32); @@ -1505,7 +1528,7 @@ static intptr_t vst_dispatcherCallback(AEffect* const effect, const int32_t opco } return 0; - case effGetProductString: + case VST_EFFECT_OPCODE_PRODUCT_NAME: if (char* const cptr = (char*)ptr) { strncpy(cptr, sPlugin->getLabel(), 32); @@ -1513,12 +1536,15 @@ static intptr_t vst_dispatcherCallback(AEffect* const effect, const int32_t opco } return 0; - case effGetVendorVersion: + case VST_EFFECT_OPCODE_VENDOR_VERSION: return sPlugin->getVersion(); - case effGetVstVersion: - return kVstVersion; - }; + case VST_EFFECT_OPCODE_VST_VERSION: + return VST_VERSION_2_4_0_0; + + default: + break; + } // handle advanced opcodes if (PluginVst* const pluginPtr = getEffectPlugin(effect)) @@ -1527,26 +1553,26 @@ static intptr_t vst_dispatcherCallback(AEffect* const effect, const int32_t opco return 0; } -static float vst_getParameterCallback(AEffect* const effect, const int32_t index) +static float vst_getParameterCallback(vst_effect* const effect, const uint32_t index) { if (PluginVst* const pluginPtr = getEffectPlugin(effect)) return pluginPtr->vst_getParameter(index); return 0.0f; } -static void vst_setParameterCallback(AEffect* const effect, const int32_t index, const float value) +static void vst_setParameterCallback(vst_effect* const effect, const uint32_t index, const float value) { if (PluginVst* const pluginPtr = getEffectPlugin(effect)) pluginPtr->vst_setParameter(index, value); } -static void vst_processCallback(AEffect* const effect, float** const inputs, float** const outputs, const int32_t sampleFrames) +static void vst_processCallback(vst_effect* const effect, const float* const* const inputs, float** const outputs, const int32_t sampleFrames) { if (PluginVst* const pluginPtr = getEffectPlugin(effect)) pluginPtr->vst_processReplacing(const_cast(inputs), outputs, sampleFrames); } -static void vst_processReplacingCallback(AEffect* const effect, float** const inputs, float** const outputs, const int32_t sampleFrames) +static void vst_processReplacingCallback(vst_effect* const effect, const float* const* const inputs, float** const outputs, const int32_t sampleFrames) { if (PluginVst* const pluginPtr = getEffectPlugin(effect)) pluginPtr->vst_processReplacing(const_cast(inputs), outputs, sampleFrames); @@ -1558,18 +1584,18 @@ END_NAMESPACE_DISTRHO DISTRHO_PLUGIN_EXPORT #if defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WASM) || defined(DISTRHO_OS_WINDOWS) -const AEffect* VSTPluginMain(audioMasterCallback audioMaster); +const vst_effect* VSTPluginMain(vst_host_callback audioMaster); #else -const AEffect* VSTPluginMain(audioMasterCallback audioMaster) asm ("main"); +const vst_effect* VSTPluginMain(vst_host_callback audioMaster) asm ("main"); #endif DISTRHO_PLUGIN_EXPORT -const AEffect* VSTPluginMain(const audioMasterCallback audioMaster) +const vst_effect* VSTPluginMain(const vst_host_callback audioMaster) { USE_NAMESPACE_DISTRHO // old version - if (audioMaster(nullptr, audioMasterVersion, 0, 0, nullptr, 0.0f) == 0) + if (audioMaster(nullptr, VST_HOST_OPCODE_01 /* version */, 0, 0, nullptr, 0.0f) == 0) return nullptr; // find plugin bundle @@ -1621,9 +1647,13 @@ const AEffect* VSTPluginMain(const audioMasterCallback audioMaster) std::memset(effect, 0, sizeof(ExtendedAEffect)); // vst fields - effect->magic = kEffectMagic; - effect->uniqueID = sPlugin->getUniqueId(); - effect->version = sPlugin->getVersion(); + #ifdef WORDS_BIGENDIAN + effect->magic_number = 0x50747356; + #else + effect->magic_number = 0x56737450; + #endif + effect->unique_id = sPlugin->getUniqueId(); + effect->version = sPlugin->getVersion(); // VST doesn't support parameter outputs. we can fake them, but it is a hack. Disabled by default. #ifdef DPF_VST_SHOW_PARAMETER_OUTPUTS @@ -1646,29 +1676,29 @@ const AEffect* VSTPluginMain(const audioMasterCallback audioMaster) #endif // plugin fields - effect->numParams = numParams; - effect->numPrograms = 1; - effect->numInputs = DISTRHO_PLUGIN_NUM_INPUTS; - effect->numOutputs = DISTRHO_PLUGIN_NUM_OUTPUTS; + effect->num_params = numParams; + effect->num_programs = 1; + effect->num_inputs = DISTRHO_PLUGIN_NUM_INPUTS; + effect->num_outputs = DISTRHO_PLUGIN_NUM_OUTPUTS; // plugin flags - effect->flags |= effFlagsCanReplacing; + effect->flags |= 1 << 4; // uses process_float #if DISTRHO_PLUGIN_IS_SYNTH - effect->flags |= effFlagsIsSynth; + effect->flags |= 1 << 8; #endif #if DISTRHO_PLUGIN_HAS_UI - effect->flags |= effFlagsHasEditor; + effect->flags |= 1 << 0; #endif #if DISTRHO_PLUGIN_WANT_STATE - effect->flags |= effFlagsProgramChunks; + effect->flags |= 1 << 5; #endif // static calls - effect->dispatcher = vst_dispatcherCallback; - effect->process = vst_processCallback; - effect->getParameter = vst_getParameterCallback; - effect->setParameter = vst_setParameterCallback; - effect->processReplacing = vst_processReplacingCallback; + effect->control = vst_dispatcherCallback; + effect->process = vst_processCallback; + effect->get_parameter = vst_getParameterCallback; + effect->set_parameter = vst_setParameterCallback; + effect->process_float = vst_processReplacingCallback; // special values effect->valid = 101; diff --git a/distrho/src/xaymar-vst2/LICENSE b/distrho/src/xaymar-vst2/LICENSE new file mode 100644 index 00000000..5c85a6dd --- /dev/null +++ b/distrho/src/xaymar-vst2/LICENSE @@ -0,0 +1,11 @@ +Copyright 2020 Michael Fabian 'Xaymar' Dirks + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/distrho/src/xaymar-vst2/README.md b/distrho/src/xaymar-vst2/README.md new file mode 100644 index 00000000..025ea27b --- /dev/null +++ b/distrho/src/xaymar-vst2/README.md @@ -0,0 +1,21 @@ +# About the Project +This is a completely "clean room" untainted reverse engineered "SDK" for the VST 2.x interface. It was reverse engineered from binaries where no license restricting the reverse engineering was attached, or where the legal system explicitly allowed reverse engineering for the purpose of interoperability. + +# Frequently Asked Questions +## Is this legal? Can I use this in my own product? +**Disclaimer:** I am not a lawyer. The information presented below is purely from available copyright laws that I could find about this topic. You should always consult with a lawyer first before including this in your product. + +As this only enables interoperability with existing VST 2.x programs and addons, it is considered to be reverse engineering in the name of interoperability. In most of the developed world, this is considered completely legal and is fine to be used by anyone, as long as it is not the only function of the product. + +Note that this does not grant any patent licenses, nor does it grant you any right to use trademarks in the names. That could mean that you can't advertise your product as having support for VST, and can't use VST in the name or presentation of the product at all unless you have permission to do so. + +## Why recreate an SDK for something officially abandoned by the creators? +There is a ton of software that is only capable of loading VST2.x audio effects, and Steinberg has made no effort to create a VST3-to-VST2-adapter for that software. Notable software includes Audacity and OBS Studio, which both likely felt restricted by the license additions Steinberg added to the GPL license. + +## How did you reverse engineer this? +The reverse engineering was done with various tools (mostly disassemblers to x86 assembly code), hooking into system APIs, attempting to mimic functionality through observation and testing, and other methods. Primarily Visual Studio Code was used to write the header files, and Visual Studio 2019 Express was used to create fake VST plugins/hosts to figure out actual behavior. + +### Which binaries were disassembled? +* A fake VST2 host (using this header) was created to verify against existing plugins. +* A fake VST2 plugin (using this header) was created to verify against existing hosts. +* OBS Studio and Audacity were used to verify compatability between closed source and open source VST hosts. diff --git a/distrho/src/xaymar-vst2/vst.h b/distrho/src/xaymar-vst2/vst.h new file mode 100644 index 00000000..c167c219 --- /dev/null +++ b/distrho/src/xaymar-vst2/vst.h @@ -0,0 +1,1008 @@ +// This was created from released VST2.x plugins, and is technically under the 2-clause BSD license. +// Depending on which country you are in, Steinberg can do fuck all about this. Notable countries for +// this are most members of the United States of America, the entirety of Europe, Japan, and Russia. +// Consult a lawyer if you don't know if clean room reverse engineering is allowed in your country. + +// See README.md for all information. + +// Known additional information: +// - Function call standard seems to be stdcall. +// - Everything is aligned to 8 bytes. + +// VST Versioning: +// - Base-10, thus can't store many version numbers. +// - Always four components, with the major one being able to store the most numbers. +// - Format is A...ABCD, so 1.2.3.4 would turn into 1234. + +#pragma once +#ifndef VST2SDK_VST_H +#define VST2SDK_VST_H + +#define VST_FUNCTION_INTERFACE __stdcall +#define VST_ALIGNMENT 8 +#define VST_MAGICNUMBER 'VstP' + +// Common VST buffer lengths: +// 8: OpCodes(GetLabel, GetName, GetValue) +#define VST_BUFFER_8 8 +// 16: +#define VST_BUFFER_16 16 +// 24: OpCodes? +#define VST_BUFFER_24 24 +// 32: OpCodes(EffectName) +#define VST_BUFFER_32 32 +#define VST_EFFECT_BUFFER_SIZE 32 +// 64: OpCodes(ProductName, VendorName) +#define VST_BUFFER_64 64 +#define VST_VENDOR_BUFFER_SIZE VST_BUFFER_64 +#define VST_PRODUCT_BUFFER_SIZE VST_BUFFER_64 +#define VST_NAME_BUFFER_SIZE VST_BUFFER_64 +// 100: +#define VST_BUFFER_100 100 + +#define VST_MAX_CHANNELS 32 // Couldn't find any audio editing software which would attempt to add more channels. + +#pragma pack(push, VST_ALIGNMENT) + +#ifdef __cplusplus +#include +extern "C" { +#else +#include +#endif + +/******************************************************************************* +|* Enumeration +|*/ +enum VST_VERSION { + VST_VERSION_1 = 0, // Anything before 2.0, used by official plug-ins. + VST_VERSION_1_0_0_0 = 1000, // 1.0, used by some third-party plug-ins. + VST_VERSION_1_1_0_0 = 1100, // 1.1, used by some third-party plug-ins. + VST_VERSION_2 = 2, // 2.0, used by official plug-ins. + VST_VERSION_2_0_0_0 = 2000, // 2.0, used by some third-party plug-ins. + VST_VERSION_2_1_0_0 = 2100, // 2.1 + VST_VERSION_2_2_0_0 = 2200, // 2.2 + VST_VERSION_2_3_0_0 = 2300, // 2.3 + VST_VERSION_2_4_0_0 = 2400, // 2.4 + + // Pad to force 32-bit number. + _VST_VERSION_PAD = 0xFFFFFFFFul, +}; + +enum VST_CATEGORY { + VST_CATEGORY_UNCATEGORIZED = 0x00, + VST_CATEGORY_01 = 0x01, + VST_CATEGORY_02 = 0x02, + VST_CATEGORY_03 = 0x03, + VST_CATEGORY_04 = 0x04, + VST_CATEGORY_05 = 0x05, + VST_CATEGORY_06 = 0x06, + VST_CATEGORY_07 = 0x07, + VST_CATEGORY_RESTORATION = 0x08, // Denoising and similar effects. + VST_CATEGORY_09 = 0x09, + VST_CATEGORY_CONTAINER = 0x0A, // Plugin contains more than one Plugin. + VST_CATEGORY_0B = 0x0B, + VST_CATEGORY_MAX, // Not part of specification, marks maximum category. + + // Pad to force 32-bit number. + _VST_CATEGORY_PAD = 0xFFFFFFFFul, +}; + +enum VST_EFFECT_OPCODE { + /* Create/Initialize the effect (if it has not been created already). + * + * @return Always 0. + */ + VST_EFFECT_OPCODE_00 = 0x00, + VST_EFFECT_OPCODE_CREATE = 0x00, + VST_EFFECT_OPCODE_INITIALIZE = 0x00, + + /* Destroy the effect (if there is any) and free its memory. + * + * This should destroy the actual object created by VST_ENTRYPOINT. + * + * @return Always 0. + */ + VST_EFFECT_OPCODE_01 = 0x01, + VST_EFFECT_OPCODE_DESTROY = 0x01, + + /* Set Program + * + * + */ + VST_EFFECT_OPCODE_02 = 0x02, + + /* Get Program + * + * + */ + VST_EFFECT_OPCODE_03 = 0x03, + + /* Set Program Name + * + * + */ + VST_EFFECT_OPCODE_04 = 0x04, + + /* Get Program Name + * + * "Returns 0. If ptr is valid, sets the first byte of ptr to 0 then returns 0." + */ + VST_EFFECT_OPCODE_05 = 0x05, + + /* Get the value? label for the parameter. + * + * @param p_int1 Parameter index. + * @param p_ptr 'char[8]' + * @return 0 on success, 1 on failure. + */ + VST_EFFECT_OPCODE_06 = 0x06, + VST_EFFECT_OPCODE_PARAM_GETLABEL = 0x06, + + /* Get the string value for the parameter. + * + * @param p_int1 Parameter index. + * @param p_ptr 'char[8]' + * @return 0 on success, 1 on failure. + */ + VST_EFFECT_OPCODE_07 = 0x07, + VST_EFFECT_OPCODE_PARAM_GETVALUE = 0x07, + + /* Get the name for the parameter. + * + * @param p_int1 Parameter index. + * @param p_ptr 'char[8]' + * @return 0 on success, 1 on failure. + */ + VST_EFFECT_OPCODE_08 = 0x08, + VST_EFFECT_OPCODE_PARAM_GETNAME = 0x08, + + /* + * + * + */ + VST_EFFECT_OPCODE_09 = 0x09, + + /* Set the new sample rate for the plugin to use. + * + * @param p_float New sample rate as a float (double on 64-bit because register upgrades). + */ + VST_EFFECT_OPCODE_0A = 0x0A, + VST_EFFECT_OPCODE_SETSAMPLERATE = 0x0A, + VST_EFFECT_OPCODE_SET_SAMPLE_RATE = 0x0A, + + /* Sets the block size, which is the maximum number of samples passed into the effect via process calls. + * + * @param p_int2 The maximum number of samples to be passed in. + */ + VST_EFFECT_OPCODE_0B = 0x0B, + VST_EFFECT_OPCODE_SETBLOCKSIZE = 0x0B, + VST_EFFECT_OPCODE_SET_BLOCK_SIZE = 0x0B, + + /* Effect processing should be suspended/paused. + * + * Unclear if this is should result in a flush of buffers. + * + * @param p_int2 0 if the effect should suspend processing, 1 if it should resume. + */ + VST_EFFECT_OPCODE_0C = 0x0C, + VST_EFFECT_OPCODE_SUSPEND = 0x0C, + + /* Retrieve the client rect size of the plugins window. + * If no window has been created, returns the default rect. + * + * @param p_ptr Pointer of type 'struct vst_rect*'. + * @return On success, returns 1 and updates p_ptr to the rect. On failure, returns 0. + */ + VST_EFFECT_OPCODE_0D = 0x0D, + VST_EFFECT_OPCODE_WINDOW_GETRECT = 0x0D, + + /* Create the window for the plugin. + * + * @param p_ptr HWND of the parent window. + * @return 0 on failure, or HWND on success. + */ + VST_EFFECT_OPCODE_0E = 0x0E, + VST_EFFECT_OPCODE_WINDOW_CREATE = 0x0E, + + /* Destroy the plugins window. + * + * @return Always 0. + */ + VST_EFFECT_OPCODE_0F = 0x0F, + VST_EFFECT_OPCODE_WINDOW_DESTROY = 0x0F, + + /* + * + * + */ + VST_EFFECT_OPCODE_10 = 0x10, + + /* + * + * + */ + VST_EFFECT_OPCODE_11 = 0x11, + + /* + * + * + */ + VST_EFFECT_OPCODE_12 = 0x12, + + /* + * + * + */ + VST_EFFECT_OPCODE_13 = 0x13, + + /* + * + * + */ + VST_EFFECT_OPCODE_14 = 0x14, + + /* + * + * + */ + VST_EFFECT_OPCODE_15 = 0x15, + + /* Always returns the FourCC 'NvEF' (0x4E764566). + */ + VST_EFFECT_OPCODE_16 = 0x16, + + /* Get Chunk + * + * + */ + VST_EFFECT_OPCODE_17 = 0x17, + + /* Set Chunk + * + * + */ + VST_EFFECT_OPCODE_18 = 0x18, + + // VST2.x starts here. + + /* + * + * + */ + VST_EFFECT_OPCODE_19 = 0x19, + + /* Can the parameter be automated? + * + * @param p_int1 Index of the parameter. + * @return 1 if the parameter can be automated, otherwise 0. + */ + VST_EFFECT_OPCODE_1A = 0x1A, + VST_EFFECT_OPCODE_PARAM_ISAUTOMATABLE = 0x1A, + + /* + * + * + */ + VST_EFFECT_OPCODE_1B = 0x1B, + + /* + * + * + */ + VST_EFFECT_OPCODE_1C = 0x1C, + + /* + * + * + */ + VST_EFFECT_OPCODE_1D = 0x1D, // See VST_EFFECT_OPCODE_05 + + /* + * + * + */ + VST_EFFECT_OPCODE_1E = 0x1E, + + /* Input connected. + * + * + */ + VST_EFFECT_OPCODE_1F = 0x1F, + + /* Input disconnected. + * + * + */ + VST_EFFECT_OPCODE_20 = 0x20, + + /* Retrieve the name of the input channel at the given index. + * + * @param p_int1 Index of the input to get the name for. + * @param p_ptr Pointer to a char* buffer able to hold at minimum 20 characters. Might need to be 32 even. + * @return 0 on failure, 1 on success. + */ + VST_EFFECT_OPCODE_21 = 0x21, + VST_EFFECT_OPCODE_INPUT_GETCHANNELNAME = 0x21, + VST_EFFECT_OPCODE_INPUT_CHANNEL_NAME = 0x21, + + /* Retrieve the name of the output channel at the given index. + * + * @param p_int1 Index of the output to get the name for. + * @param p_ptr Pointer to a char* buffer able to hold at minimum 20 characters. Might need to be 32 even. + * @return 0 on failure, 1 on success. + */ + VST_EFFECT_OPCODE_22 = 0x22, + VST_EFFECT_OPCODE_OUTPUT_GETCHANNELNAME = 0x22, + VST_EFFECT_OPCODE_OUTPUT_CHANNEL_NAME = 0x22, + + /* Retrieve category of this effect. + * + * @return The category that this effect is in, see VST_CATEGORY. + */ + VST_EFFECT_OPCODE_23 = 0x23, + VST_EFFECT_OPCODE_EFFECT_CATEGORY = 0x23, + + /* + * + * + */ + VST_EFFECT_OPCODE_24 = 0x24, + + /* + * + * + */ + VST_EFFECT_OPCODE_25 = 0x25, + + /* + * + * + */ + VST_EFFECT_OPCODE_26 = 0x26, + + /* + * + * + */ + VST_EFFECT_OPCODE_27 = 0x27, + + /* + * + * + */ + VST_EFFECT_OPCODE_28 = 0x28, + + /* + * + * + */ + VST_EFFECT_OPCODE_29 = 0x29, + + /* Set the speaker arrangement + * + * @param p_int2 (vst_speaker_arrangement*) Pointer to a pointer to the speaker arrangement for the input. + * @param p_ptr (vst_speaker_arrangement*) Pointer to a pointer to the speaker arrangement for the output. + */ + VST_EFFECT_OPCODE_2A = 0x2A, + VST_EFFECT_OPCODE_SET_SPEAKER_ARRANGEMENT = 0x2A, + + /* + * + * + */ + VST_EFFECT_OPCODE_2B = 0x2B, + + /* Enable/Disable bypassing the effect. + * + * @param p_int2 Zero if bypassing the effect is disabled, otherwise 1. + */ + VST_EFFECT_OPCODE_2C = 0x2C, + VST_EFFECT_OPCODE_BYPASS = 0x2C, + + /* Retrieve the effect name into the ptr buffer. + * + * @param p_ptr char[64] Buffer containing a zero-terminated effect information string. May be shorter than 64 bytes on older hosts. + * @return Always 0, even on failure. + */ + VST_EFFECT_OPCODE_2D = 0x2D, + VST_EFFECT_OPCODE_GETNAME = 0x2D, + VST_EFFECT_OPCODE_EFFECT_NAME = 0x2D, + + /* Translate an error code to a string. + * + * @param p_ptr char[256] Buffer that should contain a zero-terminated error string. + */ + VST_EFFECT_OPCODE_2E = 0x2E, + VST_EFFECT_OPCODE_TRANSLATE_ERROR = 0x2E, + + /* Retrieve the vendor name into the ptr buffer. + * + * @param p_ptr char[64] Buffer containing a zero-terminated vendor information string. May be shorter than 64 bytes on older hosts. + * @return Always 0, even on failure. + */ + VST_EFFECT_OPCODE_2F = 0x2F, + VST_EFFECT_OPCODE_GETVENDOR = 0x2F, + VST_EFFECT_OPCODE_VENDOR_NAME = 0x2F, + + /* See VST_EFFECT_OPCODE_GETNAME + * + * Rarely used, if at all even supported. Not sure what the purpose of this is even. + */ + VST_EFFECT_OPCODE_30 = 0x30, + VST_EFFECT_OPCODE_GETNAME2 = 0x30, + VST_EFFECT_OPCODE_PRODUCT_NAME = 0x30, + + /* Retrieve the vendor version in return value. + * + * @return Version. + */ + VST_EFFECT_OPCODE_31 = 0x31, + VST_EFFECT_OPCODE_GETVENDORVERSION = 0x31, + VST_EFFECT_OPCODE_VENDOR_VERSION = 0x31, + + /* User defined OP Code, for custom interaction. + * + */ + VST_EFFECT_OPCODE_32 = 0x32, + VST_EFFECT_OPCODE_CUSTOM = 0x32, + + /* Test for support of a specific named feature. + * + * @param p_ptr Pointer to a zero-terminated buffer containing the feature name. + * @return Non-zero if the feature is supported, otherwise 0. + */ + VST_EFFECT_OPCODE_33 = 0x33, + VST_EFFECT_OPCODE_SUPPORTS = 0x33, + + /* Number of samples that are at the tail at the end of playback. + * + * @return 0 or 1 for no tail, > 1 for number of samples to tail. + */ + VST_EFFECT_OPCODE_34 = 0x34, + VST_EFFECT_OPCODE_GETTAILSAMPLES = 0x34, + VST_EFFECT_OPCODE_TAIL_SAMPLES = 0x34, + + /* + * + * + */ + VST_EFFECT_OPCODE_35 = 0x35, + + /* + * + * + */ + VST_EFFECT_OPCODE_36 = 0x36, + + /* + * + * + */ + VST_EFFECT_OPCODE_37 = 0x37, + + /* + * + * + */ + VST_EFFECT_OPCODE_38 = 0x38, + + /* Parameter Properties + * + * @param p_ptr vst_parameter_properties* + * @return 1 if supported, otherwise 0. + */ + VST_EFFECT_OPCODE_39 = 0x39, + VST_EFFECT_OPCODE_GET_PARAMETER_PROPERTIES = VST_EFFECT_OPCODE_39, + + /* Retrieve the VST Version supported. + * + * @return Return 0 for <2.0, 2 for 2.0, 2100 for 2.1, 2200 for 2.2, 2300 for 2.3, etc. + */ + VST_EFFECT_OPCODE_3A = 0x3A, + VST_EFFECT_OPCODE_VST_VERSION = 0x3A, + + // VST 2.1 or later + + /* + * + * + */ + VST_EFFECT_OPCODE_3B = 0x3B, + + /* + * + * + */ + VST_EFFECT_OPCODE_3C = 0x3C, + + /* + * + * + */ + VST_EFFECT_OPCODE_3D = 0x3D, + + /* + * + * + */ + VST_EFFECT_OPCODE_3E = 0x3E, + + /* + * + * + */ + VST_EFFECT_OPCODE_3F = 0x3F, + + /* + * + * + */ + VST_EFFECT_OPCODE_40 = 0x40, + + /* + * + * + */ + VST_EFFECT_OPCODE_41 = 0x41, + + /* + * + * + */ + VST_EFFECT_OPCODE_42 = 0x42, + + /* + * + * + */ + VST_EFFECT_OPCODE_43 = 0x43, + + /* + * + * + */ + VST_EFFECT_OPCODE_44 = 0x44, + + // VST 2.3 or later + + /* Retrieve the speaker arrangement. + * + * @param p_int2 (vst_speaker_arrangement**) Pointer to a pointer to the speaker arrangement for the input. + * @param p_ptr (vst_speaker_arrangement**) Pointer to a pointer to the speaker arrangement for the output. + */ + VST_EFFECT_OPCODE_45 = 0x45, + VST_EFFECT_OPCODE_GET_SPEAKER_ARRANGEMENT = 0x45, + + /* + * + * + */ + VST_EFFECT_OPCODE_46 = 0x46, + + /* Begin processing of audio. + * + * + * + */ + VST_EFFECT_OPCODE_PROCESS_BEGIN = 0x47, + + /* End processing of audio. + * + * + * + */ + VST_EFFECT_OPCODE_PROCESS_END = 0x48, + + /* + * + * + */ + VST_EFFECT_OPCODE_49 = 0x49, + + /* + * + * + */ + VST_EFFECT_OPCODE_4A = 0x4A, + + /* + * + * + */ + VST_EFFECT_OPCODE_4B = 0x4B, + + /* + * + * + */ + VST_EFFECT_OPCODE_4C = 0x4C, + + // VST 2.4 or later + + /* + * + * + */ + VST_EFFECT_OPCODE_4D = 0x4D, + + /* + * + * + */ + VST_EFFECT_OPCODE_4E = 0x4E, + + /* + * + * + */ + VST_EFFECT_OPCODE_4F = 0x4F, + + // Highest number of known OPCODE. + VST_EFFECT_OPCODE_MAX, + + // Pad to force 32-bit number. + _VST_EFFECT_OPCODE_PAD = 0xFFFFFFFFul, +}; + +enum VST_HOST_OPCODE { + /* + * @param int1 -1 or Parameter Index + * @return Expected to return... something. + */ + VST_HOST_OPCODE_00 = 0x00, // cb(vst, 0x00, ?, 0, 0); + VST_HOST_OPCODE_01 = 0x01, + VST_HOST_OPCODE_02 = 0x02, // bool cb(0, 0x02, 0, 0, 0); + VST_HOST_OPCODE_03 = 0x03, + VST_HOST_OPCODE_04 = 0x04, + VST_HOST_OPCODE_05 = 0x05, + VST_HOST_OPCODE_06 = 0x06, + VST_HOST_OPCODE_07 = 0x07, + VST_HOST_OPCODE_08 = 0x08, + VST_HOST_OPCODE_09 = 0x09, + VST_HOST_OPCODE_0A = 0x0A, + VST_HOST_OPCODE_0B = 0x0B, + VST_HOST_OPCODE_0C = 0x0C, + VST_HOST_OPCODE_0D = 0x0D, + VST_HOST_OPCODE_0E = 0x0E, + VST_HOST_OPCODE_0F = 0x0F, + VST_HOST_OPCODE_10 = 0x10, + VST_HOST_OPCODE_11 = 0x11, + VST_HOST_OPCODE_12 = 0x12, + VST_HOST_OPCODE_13 = 0x13, + VST_HOST_OPCODE_14 = 0x14, + VST_HOST_OPCODE_15 = 0x15, + VST_HOST_OPCODE_16 = 0x16, + VST_HOST_OPCODE_17 = 0x17, + VST_HOST_OPCODE_18 = 0x18, + VST_HOST_OPCODE_19 = 0x19, + VST_HOST_OPCODE_1A = 0x1A, + VST_HOST_OPCODE_1B = 0x1B, + VST_HOST_OPCODE_1C = 0x1C, + VST_HOST_OPCODE_1D = 0x1D, + VST_HOST_OPCODE_1E = 0x1E, + VST_HOST_OPCODE_1F = 0x1F, + VST_HOST_OPCODE_20 = 0x20, + VST_HOST_OPCODE_21 = 0x21, + VST_HOST_OPCODE_22 = 0x22, + VST_HOST_OPCODE_23 = 0x23, + VST_HOST_OPCODE_24 = 0x24, + VST_HOST_OPCODE_25 = 0x25, + VST_HOST_OPCODE_26 = 0x26, + VST_HOST_OPCODE_27 = 0x27, + VST_HOST_OPCODE_28 = 0x28, + VST_HOST_OPCODE_29 = 0x29, + VST_HOST_OPCODE_2A = 0x2A, + + /* Parameter gained focus. + * + * @param int1 Parameter index. + */ + VST_HOST_OPCODE_2B = 0x2B, + + /* Parameter lost focus. + * + * @param int1 Parameter index. + */ + VST_HOST_OPCODE_2C = 0x2C, + + VST_HOST_OPCODE_2D = 0x2D, + VST_HOST_OPCODE_2E = 0x2E, + VST_HOST_OPCODE_2F = 0x2F, + + // Highest number of known OPCODE. + VST_HOST_OPCODE_MAX, + + // Pad to force 32-bit number. + _VST_HOST_OPCODE_PAD = 0xFFFFFFFFul, +}; + +enum VST_ARRANGEMENT_TYPE { + /* Custom speaker arrangement. + * + * Accidentally discovered through random testing. + */ + VST_ARRANGEMENT_TYPE_CUSTOM = -2, + + /* Unknown/Empty speaker layout. + * + */ + VST_ARRANGEMENT_TYPE_UNKNOWN = -1, + + /* Mono + */ + VST_ARRANGEMENT_TYPE_MONO = 0, + + /* Stereo + */ + VST_ARRANGEMENT_TYPE_STEREO = 1, + + /* 5.1 + */ + VST_ARRANGEMENT_TYPE_5_1 = 0x0F, + + // Pad to force 32-bit number. + _VST_ARRANGEMENT_TYPE_PAD = 0xFFFFFFFFul, +}; + +enum VST_SPEAKER_TYPE { + VST_SPEAKER_TYPE_MONO = 0, + VST_SPEAKER_TYPE_LEFT = 1, + VST_SPEAKER_TYPE_RIGHT = 2, + VST_SPEAKER_TYPE_CENTER = 3, + VST_SPEAKER_TYPE_LFE = 4, + VST_SPEAKER_TYPE_LEFT_SIDE = 5, + VST_SPEAKER_TYPE_RIGHT_SIDE = 6, + + // Pad to force 32-bit number. + _VST_SPEAKER_TYPE_PAD = 0xFFFFFFFFul, +}; + +enum VST_PARAMETER_FLAGS { + /** + * Parameter is an on/off switch. + */ + VST_PARAMETER_FLAGS_SWITCH = 1, + /** + * Limits defined by integers. + */ + VST_PARAMETER_FLAGS_INTEGER_LIMITS = 1 << 1, + /** + * Uses float steps. + */ + VST_PARAMETER_FLAGS_STEP_FLOAT = 1 << 2, + /** + * Uses integer steps. + */ + VST_PARAMETER_FLAGS_STEP_INT = 1 << 3, + /** + * Respect index variable for display ordering. + */ + VST_PARAMETER_FLAGS_INDEX = 1 << 4, + /** + * Respect category value and names. + */ + VST_PARAMETER_FLAGS_CATEGORY = 1 << 5, + VST_PARAMETER_FLAGS_UNKNOWN6 = 1 << 6, + _VST_PARAMETER_FLAGS_PAD = 0xFFFFFFFFul, +}; + +/******************************************************************************* +|* Structures +|*/ + +struct vst_rect { + int16_t left; + int16_t top; + int16_t right; + int16_t bottom; +}; + +struct vst_effect { + int32_t magic_number; // Should always be VST_MAGICNUMBER + + // 64-bit adds 4-byte padding here to align pointers. + + /* Control the VST through an opcode and up to four parameters. + * + * @param this Pointer to the effect itself. + * @param opcode The opcode to run, see VST_EFFECT_OPCODES. + * @param p_int1 Parameter, see VST_EFFECT_OPCODES. + * @param p_int2 Parameter, see VST_EFFECT_OPCODES. + * @param p_ptr Parameter, see VST_EFFECT_OPCODES. + * @param p_float Parameter, see VST_EFFECT_OPCODES. + */ + intptr_t(VST_FUNCTION_INTERFACE* control)(vst_effect* pthis, VST_EFFECT_OPCODE opcode, int32_t p_int1, + intptr_t p_int2, void* p_ptr, float p_float); + + /* Process the given number of samples in inputs and outputs. + * + * Different to process_float how? Never seen any difference. + * + * @param pthis Pointer to the effect itself. + * @param inputs Pointer to an array of 'const float[samples]' with size numInputs. + * @param outputs Pointer to an array of 'float[samples]' with size numOutputs. + * @param samples Number of samples per channel in inputs. + */ + void(VST_FUNCTION_INTERFACE* process)(vst_effect* pthis, const float* const* inputs, float** outputs, + int32_t samples); + + /* Updates the value for the parameter at the given index, or does nothing if out of bounds. + * + * @param pthis Pointer to the effect itself. + * @param index Parameter index. + * @param value New value for the parameter. + */ + void(VST_FUNCTION_INTERFACE* set_parameter)(vst_effect* pthis, uint32_t index, float value); + + /* Returns the value stored for the parameter at index, or 0 if out of bounds. + * + * @param pthis Pointer to the effect itself. + * @param index Parameter index. + * @return float Value of the parameter. + */ + float(VST_FUNCTION_INTERFACE* get_parameter)(vst_effect* pthis, uint32_t index); + + int32_t num_programs; // Number of possible programs. + int32_t num_params; // Number of possible parameters. + int32_t num_inputs; // Number of inputs. + int32_t num_outputs; // Number of outputs. + + /* Bitflags + * + * Bit Description + * 1 Effect has "Editor" + * 2 Unknown (Found in: ReaDelay) + * 3 Unknown (Found in: ReaDelay) + * 4 Unknown (Found in: ReaDelay) + * 5 Has process_float (Found in: ReaDelay, ReaComp, ReaControlMIDI, ReaStream, ReaFir) + * 6 Unknown (Found in: ReaControlMIDI, ReaStream, ReaFir) + * 10 Unknown (Found in: ReaFir) + * 13 Has process_double (Found in: ReaControlMIDI) + */ + int32_t flags; + + // 64-bit adds 4-byte padding here to align pointers. + + void* _unknown_ptr_00[2]; + + /* Initial delay before processing of samples can actually begin in Samples. + * + * Should be updated before or during handling the 0x47 control call. + */ + int32_t delay; + + int32_t _unknown_int32_00[2]; // Unknown int32_t values. + float _unknown_float_00; // Seems to always be 1.0 + + void* effect_internal; // Pointer to Plugin internal data + void* host_internal; // Pointer to Host internal data. + + /* Id of the plugin. + * + * Due to this not being enough for uniqueness, it should not be used alone + * for indexing. Ideally you want to index like this: + * [unique_id][module_name][version][flags] + * If any of the checks after unique_id fail, you default to the first + * possible choice. + */ + int32_t unique_id; + + /* Plugin version + * + * Unrelated to the minimum VST Version, but often the same. + */ + int32_t version; + + // There is no padding here if everything went right. + + /* Process the given number of single samples in inputs and outputs. + * + * @param pthis Pointer to the effect itself. + * @param inputs Pointer to an array of 'const float[samples]' with size numInputs. + * @param outputs Pointer to an array of 'float[samples]' with size numOutputs. + * @param samples Number of samples per channel in inputs. + */ + void(VST_FUNCTION_INTERFACE* process_float)(vst_effect* pthis, const float* const* inputs, float** outputs, + int32_t samples); + + /* Process the given number of double samples in inputs and outputs. + * + * Used only by 2.4 hosts and plugins, possibly restricted to said version. + * + * @param pthis Pointer to the effect itself. + * @param inputs Pointer to an array of 'const double[samples]' with size numInputs. + * @param outputs Pointer to an array of 'double[samples]' with size numOutputs. + * @param samples Number of samples per channel in inputs. + */ + void(VST_FUNCTION_INTERFACE* process_double)(vst_effect* pthis, const double* const* inputs, double** outputs, + int32_t samples); + + // Everything after this is unknown and was present in reacomp-standalone.dll. + uint8_t _unknown[56]; // 56-bytes of something. Could also just be 52-bytes. +}; + +struct vst_parameter_properties { + float step_f32; + float step_small_f32; + float step_large_f32; + + char name[VST_BUFFER_64]; + + uint32_t flags; + int32_t min_value_i32; + int32_t max_value_i32; + int32_t step_i32; + + char label[VST_BUFFER_8]; + + uint16_t index; + + uint16_t category; + uint16_t num_parameters_in_category; + uint16_t _unknown_00; + + char category_label[VST_BUFFER_24]; + + char _unknown_01[VST_BUFFER_16]; +}; + +struct vst_speaker_properties { + float _unknown_00; // 10.0 if LFE, otherwise random? Never exceeds -PI to PI range. + float _unknown_04; // 10.0 if LFE, otherwise random? Never exceeds -PI to PI range. + float _unknown_08; // 0.0 if LFE, otherwise 1.0. + float _unknown_0C; + char name[VST_BUFFER_64]; + VST_SPEAKER_TYPE type; + + uint8_t _unknown[28]; // Padding detected from testing. +}; + +struct vst_speaker_arrangement { + VST_ARRANGEMENT_TYPE type; // See VST_SPEAKER_ARRANGEMENT_TYPE + int32_t channels; // Number of channels in speakers. + vst_speaker_properties speakers[VST_MAX_CHANNELS]; // Array of speaker properties, actual size defined by channels. +}; + +/* Callback used by the plugin to interface with the host. + * + * @param opcode See VST_HOST_OPCODE + * @param p_str Zero terminated string or null on call. + * @return ? + */ +typedef intptr_t (*vst_host_callback)(vst_effect* plugin, VST_HOST_OPCODE opcode, int32_t p_int1, int64_t p_int2, + void* p_str, int32_t p_int3); + +/* Entry point for VST2.x plugins. + * + * @return A new instance of the VST2.x effect. + */ +#define VST_ENTRYPOINT vst_effect* VSTPluginMain(vst_host_callback callback) +#define VST_ENTRYPOINT_WINDOWS \ + vst_effect* MAIN(vst_host_callback callback) \ + { \ + return VSTPluginMain(callback); \ + } +#define VST_ENTRYPOINT_MACOS \ + vst_effect* main_macho(vst_host_callback callback) \ + { \ + return VSTPluginMain(callback); \ + } + +#ifdef __cplusplus +} +#endif + +// Variable size variant of vst_speaker_arrangement. +#ifdef __cplusplus +template +struct vst_speaker_arrangement_t { + VST_ARRANGEMENT_TYPE type; // See VST_SPEAKER_ARRANGEMENT_TYPE + int32_t channels; // Number of channels in speakers. + vst_speaker_properties speakers[T]; // Array of speaker properties, actual size defined by channels. +}; +#endif + +#pragma pack(pop) + +#endif