Browse Source

Replace vestige with a more liberally-licensed version

Signed-off-by: falkTX <falktx@falktx.com>
pull/409/head
falkTX 2 years ago
parent
commit
f9924e0e81
Signed by: falkTX <falktx@falktx.com> GPG Key ID: CDBAA37ABC74FBA0
6 changed files with 1234 additions and 177 deletions
  1. +5
    -9
      LICENSING.md
  2. +0
    -9
      distrho/DistrhoInfo.hpp
  3. +189
    -159
      distrho/src/DistrhoPluginVST2.cpp
  4. +11
    -0
      distrho/src/xaymar-vst2/LICENSE
  5. +21
    -0
      distrho/src/xaymar-vst2/README.md
  6. +1008
    -0
      distrho/src/xaymar-vst2/vst.h

+ 5
- 9
LICENSING.md View File

@@ -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;<br/> **ALSA**: 1998-2001 Jaroslav Kysela, Abramo Bagnara, Takashi Iwai |
| LV2 | ISC | Copyright attribution | 2006-2020 Steve Harris, David Robillard;<br/> 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



+ 0
- 9
distrho/DistrhoInfo.hpp View File

@@ -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

/** @} */

/* ------------------------------------------------------------------------------------------------------------


+ 189
- 159
distrho/src/DistrhoPluginVST2.cpp View File

@@ -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 <clocale>
#include <map>
#include <string>
#include <vector>

#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<const String, String> StringMap;

static constexpr const int kVstMidiEventSize = static_cast<int>(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<int32_t>(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<uint32_t>(hostCallback(audioMasterGetBlockSize));
const double sampleRate = static_cast<double>(hostCallback(audioMasterGetSampleRate));
const uint32_t bufferSize = static_cast<uint32_t>(hostCallback(VST_HOST_OPCODE_11));
const double sampleRate = static_cast<double>(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<int32_t>(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<int32_t>(sizeof(VstMidiEvent));;
vstMidiEvent.deltaFrames = midiEvent.frame;

for (uint8_t i=0; i<midiEvent.size; ++i)
vstMidiEvent.midiData[i] = midiEvent.data[i];

return hostCallback(audioMasterProcessEvents, 0, 0, &vstEvents) == 1;
return hostCallback(VST_HOST_OPCODE_08, 0, 0, &vstEvents) == 1;
}

static bool writeMidiCallback(void* ptr, const MidiEvent& midiEvent)
@@ -1276,10 +1298,10 @@ private:

// --------------------------------------------------------------------------------------------------------------------

struct ExtendedAEffect : AEffect {
struct ExtendedAEffect : vst_effect {
char _padding[63];
char valid;
audioMasterCallback audioMaster;
vst_host_callback audioMaster;
PluginVst* pluginPtr;
};

@@ -1304,7 +1326,7 @@ static struct Cleanup {
// --------------------------------------------------------------------------------------------------------------------

static inline
ExtendedAEffect* getExtendedEffect(AEffect* const effect)
ExtendedAEffect* getExtendedEffect(vst_effect* const effect)
{
if (effect == nullptr)
return nullptr;
@@ -1317,7 +1339,7 @@ ExtendedAEffect* getExtendedEffect(AEffect* const effect)
}

static inline
PluginVst* getEffectPlugin(AEffect* const effect)
PluginVst* getEffectPlugin(vst_effect* const effect)
{
if (effect == nullptr)
return nullptr;
@@ -1331,22 +1353,22 @@ PluginVst* getEffectPlugin(AEffect* const effect)

// --------------------------------------------------------------------------------------------------------------------

static intptr_t vst_dispatcherCallback(AEffect* const effect, const int32_t opcode, const int32_t index, const intptr_t value, void* const ptr, const float opt)
static intptr_t vst_dispatcherCallback(vst_effect* const effect, const VST_EFFECT_OPCODE opcode, const int32_t index, const intptr_t value, void* const ptr, const float opt)
{
// handle base opcodes
switch (opcode)
{
case effOpen:
case VST_EFFECT_OPCODE_CREATE:
if (ExtendedAEffect* const exteffect = getExtendedEffect(effect))
{
// some hosts call effOpen twice
// some hosts call open/create twice
if (exteffect->pluginPtr != 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<int32_t>(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<int32_t>(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<int32_t>(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<int32_t>(ranges.min);
properties->maxInteger = static_cast<int32_t>(ranges.max);
properties->flags |= VST_PARAMETER_FLAGS_INTEGER_LIMITS;
properties->min_value_i32 = static_cast<int32_t>(ranges.min);
properties->max_value_i32 = static_cast<int32_t>(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<const float**>(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<const float**>(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;


+ 11
- 0
distrho/src/xaymar-vst2/LICENSE View File

@@ -0,0 +1,11 @@
Copyright 2020 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>

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.

+ 21
- 0
distrho/src/xaymar-vst2/README.md View File

@@ -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.

+ 1008
- 0
distrho/src/xaymar-vst2/vst.h
File diff suppressed because it is too large
View File


Loading…
Cancel
Save