Browse Source

Add distrho VST export code

tags/1.9.4
falkTX 12 years ago
parent
commit
2724bc3d1e
1 changed files with 752 additions and 0 deletions
  1. +752
    -0
      source/libs/distrho/src/DistrhoPluginVST.cpp

+ 752
- 0
source/libs/distrho/src/DistrhoPluginVST.cpp View File

@@ -0,0 +1,752 @@
/*
* DISTRHO Plugin Toolkit (DPT)
* Copyright (C) 2012-2013 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation.
*
* 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 Lesser General Public License for more details.
*
* For a full copy of the license see the LGPL.txt file
*/

#include "DistrhoPluginInternal.hpp"

#if DISTRHO_PLUGIN_HAS_UI
# include "DistrhoUIInternal.hpp"
# ifdef DISTRHO_UI_QT4
# include <QtGui/QApplication>
# endif
#endif

#ifndef __cdecl
# define __cdecl
#endif

#define VST_FORCE_DEPRECATED 0
#include <pluginterfaces/vst2.x/aeffectx.h>

START_NAMESPACE_DISTRHO

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

#if DISTRHO_PLUGIN_HAS_UI

# ifdef DISTRHO_UI_QT4
static int qargc = 0;
static char** qargv = nullptr;

class QStaticScopedAppInit
{
public:
QStaticScopedAppInit()
{
}

static void idle()
{
if (sApp != nullptr)
sApp->processEvents();
}

static void addOne()
{
if (sCount++ != 0)
return;

sApp = qApp ? qApp : new QApplication(qargc, qargv, true);
sApp->setQuitOnLastWindowClosed(false);
}

static void removeOne()
{
if (sCount-- != 1)
return;

assert(sApp != nullptr);

sApp->processEvents();
//sApp->quit();
//sApp->processEvents();
//delete sApp;
//sApp = nullptr;
}

private:
static QApplication* sApp;
static uint32_t sCount;
};

QApplication* QStaticScopedAppInit::sApp = nullptr;
uint32_t QStaticScopedAppInit::sCount = 0;
# endif

class UIVst
{
public:
UIVst(audioMasterCallback audioMaster, AEffect* effect, PluginInternal* plugin, intptr_t winId)
: kAudioMaster(audioMaster),
kEffect(effect),
kPlugin(plugin),
fUi(this, winId, editParameterCallback, setParameterCallback, setStateCallback, sendNoteCallback, uiResizeCallback),
fParameterChecks(nullptr),
fParameterValues(nullptr)
{
uint32_t paramCount = plugin->parameterCount();

if (paramCount > 0)
{
fParameterChecks = new bool[paramCount];
fParameterValues = new float[paramCount];

for (uint32_t i=0; i < paramCount; i++)
{
fParameterChecks[i] = false;
fParameterValues[i] = 0.0f;
}
}

#if DISTRHO_PLUGIN_WANT_PROGRAMS
fNextProgram = -1;
#endif

#if DISTRHO_PLUGIN_IS_SYNTH
fMidiEventCount = 0;
#endif
}

~UIVst()
{
if (fParameterChecks != nullptr)
delete[] fParameterChecks;

if (fParameterValues != nullptr)
delete[] fParameterValues;
}

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

void idle()
{
#if DISTRHO_PLUGIN_WANT_PROGRAMS
if (fNextProgram != -1)
{
fUi.programChanged(fNextProgram);
fNextProgram = -1;
}
#endif

for (uint32_t i=0, count = kPlugin->parameterCount(); i < count; i++)
{
if (fParameterChecks[i])
{
fParameterChecks[i] = false;

fUi.parameterChanged(i, fParameterValues[i]);
}
}

#if DISTRHO_PLUGIN_IS_SYNTH
// TODO - notes
#endif

fUi.idle();
}

int16_t getWidth()
{
return fUi.width();
}

int16_t getHeight()
{
return fUi.height();
}

// ---------------------------------------------
// functions called from the plugin side, no block

void setParameterValueFromPlugin(uint32_t index, float perValue)
{
fParameterChecks[index] = true;
fParameterValues[index] = perValue;
}

#if DISTRHO_PLUGIN_WANT_PROGRAMS
void setProgramFromPlugin(uint32_t index)
{
fNextProgram = index;

// set previous parameters invalid
for (uint32_t i=0, count = kPlugin->parameterCount(); i < count; i++)
fParameterChecks[i] = false;
}
#endif

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

protected:
intptr_t hostCallback(int32_t opcode, int32_t index, intptr_t value, void* ptr, float opt)
{
return kAudioMaster(kEffect, opcode, index, value, ptr, opt);
}

void editParameter(uint32_t index, bool started)
{
if (started)
hostCallback(audioMasterBeginEdit, index, 0, nullptr, 0.0f);
else
hostCallback(audioMasterEndEdit, index, 0, nullptr, 0.0f);
}

void setParameterValue(uint32_t index, float realValue)
{
const ParameterRanges& ranges = kPlugin->parameterRanges(index);
float perValue = ranges.normalizeValue(realValue);

kPlugin->setParameterValue(index, realValue);
hostCallback(audioMasterAutomate, index, 0, nullptr, perValue);
}

void setState(const char* key, const char* value)
{
#if DISTRHO_PLUGIN_WANT_STATE
kPlugin->setState(key, value);
#endif
}

void sendNote(bool onOff, uint8_t channel, uint8_t note, uint8_t velocity)
{
#if DISTRHO_PLUGIN_IS_SYNTH
// TODO
#endif
}

void uiResize(unsigned int width, unsigned int height)
{
hostCallback(audioMasterSizeWindow, width, height, nullptr, 0.0f);
}

private:
// Vst stuff
audioMasterCallback const kAudioMaster;
AEffect* const kEffect;
PluginInternal* const kPlugin;

// Plugin UI
UIInternal fUi;

// Temporary data
bool* fParameterChecks;
float* fParameterValues;

#if DISTRHO_PLUGIN_WANT_PROGRAMS
int32_t fNextProgram;
#endif
#if DISTRHO_PLUGIN_IS_SYNTH
uint32_t fMidiEventCount;
MidiEvent fMidiEvents[MAX_MIDI_EVENTS];
#endif

// ---------------------------------------------
// Callbacks

#define handlePtr ((UIVst*)ptr)

static void editParameterCallback(void* ptr, uint32_t index, bool started)
{
handlePtr->editParameter(index, started);
}

static void setParameterCallback(void* ptr, uint32_t rindex, float value)
{
handlePtr->setParameterValue(rindex, value);
}

static void setStateCallback(void* ptr, const char* key, const char* value)
{
handlePtr->setState(key, value);
}

static void sendNoteCallback(void* ptr, bool onOff, uint8_t channel, uint8_t note, uint8_t velocity)
{
handlePtr->sendNote(onOff, channel, note, velocity);
}

static void uiResizeCallback(void* ptr, unsigned int width, unsigned int height)
{
handlePtr->uiResize(width, height);
}

#undef handlePtr
};
#endif

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

class PluginVst
{
public:
PluginVst(audioMasterCallback audioMaster, AEffect* effect)
: kAudioMaster(audioMaster),
kEffect(effect)
{
#if DISTRHO_PLUGIN_HAS_UI
fVstUi = nullptr;
fVstRect.top = 0;
fVstRect.left = 0;
fVstRect.bottom = 0;
fVstRect.right = 0;
#endif

#if DISTRHO_PLUGIN_WANT_PROGRAMS
fCurProgram = -1;
#endif

#if DISTRHO_PLUGIN_IS_SYNTH
fMidiEventCount = 0;
#endif
}

~PluginVst()
{
}

intptr_t vst_dispatcher(int32_t opcode, int32_t index, intptr_t value, void* ptr, float opt)
{
int32_t ret = 0;

switch (opcode)
{
#if DISTRHO_PLUGIN_WANT_PROGRAMS
case effSetProgram:
if (value >= 0 && value < static_cast<intptr_t>(fPlugin.programCount()))
{
fCurProgram = value;
fPlugin.setProgram(fCurProgram);

if (fVstUi != nullptr)
fVstUi->setProgramFromPlugin(fCurProgram);

ret = 1;
}
break;

case effGetProgram:
ret = fCurProgram;
break;

case effSetProgramName:
// unsupported
break;

case effGetProgramName:
if (ptr != nullptr && fCurProgram >= 0 && fCurProgram < static_cast<int32_t>(fPlugin.programCount()))
{
std::strncpy((char*)ptr, fPlugin.programName(fCurProgram), kVstMaxProgNameLen);
ret = 1;
}
break;
#endif

case effGetParamDisplay:
if (ptr != nullptr && index < static_cast<int32_t>(fPlugin.parameterCount()))
{
snprintf((char*)ptr, kVstMaxParamStrLen, "%f", fPlugin.parameterValue(index));
ret = 1;
}
break;

case effSetSampleRate:
fPlugin.setSampleRate(opt, true);
break;

case effSetBlockSize:
fPlugin.setBufferSize(value, true);
break;

case effMainsChanged:
if (value == 0)
fPlugin.deactivate();
else
fPlugin.activate();
break;

#if DISTRHO_PLUGIN_HAS_UI
case effEditGetRect:
createUiIfNeeded(0);
*(ERect**)ptr = &fVstRect;
ret = 1;
break;

case effEditOpen:
createUiIfNeeded((intptr_t)ptr);
ret = 1;
break;

case effEditClose:
if (fVstUi != nullptr)
{
delete fVstUi;
fVstUi = nullptr;
# ifdef DISTRHO_UI_QT4
QStaticScopedAppInit::idle();
# endif
ret = 1;
}
break;

case effEditIdle:
if (fVstUi != nullptr)
fVstUi->idle();
# ifdef DISTRHO_UI_QT4
QStaticScopedAppInit::idle();
# endif
break;
#endif

#if DISTRHO_PLUGIN_WANT_STATE
case effGetChunk:
case effSetChunk:
// TODO
break;
#endif

#if DISTRHO_PLUGIN_IS_SYNTH
case effProcessEvents:
if (ptr)
{
//VstEvents* events = (VstEvents*)ptr;
// TODO
}
break;
#endif

case effCanBeAutomated:
if (index < static_cast<int32_t>(fPlugin.parameterCount()))
{
uint32_t hints = fPlugin.parameterHints(index);

// must be automable, and not output
if ((hints & PARAMETER_IS_AUTOMABLE) != 0 && (hints & PARAMETER_IS_OUTPUT) == 0)
ret = 1;
}
break;

case effCanDo:
// TODO
break;

case effStartProcess:
case effStopProcess:
break;
}

return ret;
}

float vst_getParameter(int32_t index)
{
const ParameterRanges& ranges = fPlugin.parameterRanges(index);
return ranges.normalizeValue(fPlugin.parameterValue(index));
}

void vst_setParameter(int32_t index, float value)
{
const ParameterRanges& ranges = fPlugin.parameterRanges(index);
float realValue = ranges.unnormalizeValue(value);
fPlugin.setParameterValue(index, realValue);

#if DISTRHO_PLUGIN_HAS_UI
if (fVstUi != nullptr)
fVstUi->setParameterValueFromPlugin(index, realValue);
#endif
}

void vst_processReplacing(float** inputs, float** outputs, int32_t sampleFrames)
{
#if DISTRHO_PLUGIN_IS_SYNTH
fPlugin.run(inputs, outputs, sampleFrames, fMidiEventCount, fMidiEvents);

// TODO - send notes to UI
#else
fPlugin.run(inputs, outputs, sampleFrames, 0, nullptr);
#endif
}

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

private:
// VST stuff
audioMasterCallback const kAudioMaster;
AEffect* const kEffect;

// Plugin
PluginInternal fPlugin;

#if DISTRHO_PLUGIN_HAS_UI
// UI
UIVst* fVstUi;
ERect fVstRect;
#endif

#if DISTRHO_PLUGIN_WANT_PROGRAMS
int32_t fCurProgram;
#endif
#if DISTRHO_PLUGIN_IS_SYNTH
uint32_t fMidiEventCount;
MidiEvent fMidiEvents[MAX_MIDI_EVENTS];
#endif

#if DISTRHO_PLUGIN_HAS_UI
void createUiIfNeeded(intptr_t ptr)
{
if (fVstUi != nullptr)
return;

d_lastUiSampleRate = kAudioMaster(kEffect, audioMasterGetSampleRate, 0, 0, nullptr, 0.0f);

if (ptr == 0)
{
// fake temporary window, just to set ui size
UIVst tmpUi(kAudioMaster, kEffect, &fPlugin, ptr);
fVstRect.right = tmpUi.getWidth();
fVstRect.bottom = tmpUi.getHeight();
}
else
{
// real parented ui
fVstUi = new UIVst(kAudioMaster, kEffect, &fPlugin, ptr);
fVstRect.right = fVstUi->getWidth();
fVstRect.bottom = fVstUi->getHeight();

# if DISTRHO_PLUGIN_WANT_PROGRAMS
if (fCurProgram >= 0)
fVstUi->setProgramFromPlugin(fCurProgram);
# endif

for (uint32_t i=0, count = fPlugin.parameterCount(); i < count; i++)
fVstUi->setParameterValueFromPlugin(i, fPlugin.parameterValue(i));
}
}
#endif
};

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

#define handlePtr ((PluginVst*)effect->object)

static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t index, intptr_t value, void* ptr, float opt)
{
// first internal init
bool doInternalInit = (opcode == -1 && index == 0xdead && value == 0xf00d);

if (doInternalInit)
{
// set valid but dummy values
d_lastBufferSize = 512;
d_lastSampleRate = 44100.0;
}

// Create dummy plugin to get data from
static PluginInternal plugin;

if (doInternalInit)
{
// unset
d_lastBufferSize = 0;
d_lastSampleRate = 0.0;

*(PluginInternal**)ptr = &plugin;
return 0;
}

// handle base opcodes
switch (opcode)
{
case effOpen:
if (effect != nullptr && effect->object == nullptr)
{
audioMasterCallback audioMaster = (audioMasterCallback)effect->user;
d_lastBufferSize = audioMaster(effect, audioMasterGetBlockSize, 0, 0, nullptr, 0.0f);
d_lastSampleRate = audioMaster(effect, audioMasterGetSampleRate, 0, 0, nullptr, 0.0f);
effect->object = new PluginVst(audioMaster, effect);
#ifdef DISTRHO_UI_QT4
QStaticScopedAppInit::addOne();
#endif
return 1;
}
return 0;

case effClose:
if (effect != nullptr && effect->object != nullptr)
{
delete (PluginVst*)effect->object;
effect->object = nullptr;
delete effect;
#ifdef DISTRHO_UI_QT4
QStaticScopedAppInit::removeOne();
#endif
return 1;
}
return 0;

case effGetParamLabel:
if (ptr != nullptr && index < static_cast<int32_t>(plugin.parameterCount()))
{
std::strncpy((char*)ptr, plugin.parameterUnit(index), kVstMaxParamStrLen);
return 1;
}
return 0;

case effGetParamName:
if (ptr != nullptr && index < static_cast<int32_t>(plugin.parameterCount()))
{
std::strncpy((char*)ptr, plugin.parameterName(index), kVstMaxParamStrLen);
return 1;
}
return 0;

#if DISTRHO_PLUGIN_WANT_PROGRAMS
case effGetProgramNameIndexed:
if (ptr != nullptr && index < static_cast<int32_t>(plugin.parameterCount()))
{
std::strncpy((char*)ptr, plugin.programName(index), kVstMaxProgNameLen);
return 1;
}
return 0;
#endif

case effGetPlugCategory:
#if DISTRHO_PLUGIN_IS_SYNTH
return kPlugCategSynth;
#else
return kPlugCategUnknown;
#endif

case effGetEffectName:
if (ptr != nullptr)
{
std::strncpy((char*)ptr, plugin.name(), kVstMaxProductStrLen);
return 1;
}
return 0;

case effGetVendorString:
if (ptr != nullptr)
{
std::strncpy((char*)ptr, plugin.maker(), kVstMaxVendorStrLen);
return 1;
}
return 0;

case effGetProductString:
if (ptr != nullptr)
{
std::strncpy((char*)ptr, plugin.label(), kVstMaxEffectNameLen);
return 1;
}
return 0;

case effGetVendorVersion:
return plugin.version();

case effGetVstVersion:
return kVstVersion;
};

// handle object opcodes
if (effect != nullptr && effect->object != nullptr)
return handlePtr->vst_dispatcher(opcode, index, value, ptr, opt);

return 0;
}

static float vst_getParameterCallback(AEffect* effect, int32_t index)
{
if (effect != nullptr && effect->object != nullptr)
return handlePtr->vst_getParameter(index);
return 0.0f;
}

static void vst_setParameterCallback(AEffect* effect, int32_t index, float value)
{
if (effect != nullptr && effect->object != nullptr)
handlePtr->vst_setParameter(index, value);
}

static void vst_processCallback(AEffect* effect, float** inputs, float** outputs, int32_t sampleFrames)
{
if (effect != nullptr && effect->object != nullptr)
handlePtr->vst_processReplacing(inputs, outputs, sampleFrames);
}

static void vst_processReplacingCallback(AEffect* effect, float** inputs, float** outputs, int32_t sampleFrames)
{
if (effect != nullptr && effect->object != nullptr)
handlePtr->vst_processReplacing(inputs, outputs, sampleFrames);
}

#undef handlePtr

END_NAMESPACE_DISTRHO

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

DISTRHO_PLUGIN_EXPORT
const AEffect* VSTPluginMain(audioMasterCallback audioMaster)
{
USE_NAMESPACE_DISTRHO

// old version
if (audioMaster(nullptr, audioMasterVersion, 0, 0, nullptr, 0.0f) == 0)
return nullptr;

PluginInternal* plugin = nullptr;
vst_dispatcherCallback(nullptr, -1, 0xdead, 0xf00d, &plugin, 0.0f);

AEffect* effect = new AEffect;
memset(effect, 0, sizeof(AEffect));

// vst fields
effect->magic = kEffectMagic;
effect->uniqueID = plugin->uniqueId();
effect->version = plugin->version();

// plugin fields
effect->numParams = plugin->parameterCount();
#if DISTRHO_PLUGIN_WANT_PROGRAMS
effect->numPrograms = plugin->programCount();
#endif
effect->numInputs = DISTRHO_PLUGIN_NUM_INPUTS;
effect->numOutputs = DISTRHO_PLUGIN_NUM_OUTPUTS;

// static calls
effect->dispatcher = vst_dispatcherCallback;
effect->process = vst_processCallback;
effect->getParameter = vst_getParameterCallback;
effect->setParameter = vst_setParameterCallback;
effect->processReplacing = vst_processReplacingCallback;
effect->processDoubleReplacing = nullptr;

// plugin flags
effect->flags |= effFlagsCanReplacing;
#if DISTRHO_PLUGIN_HAS_UI
effect->flags |= effFlagsHasEditor;
#endif
#if DISTRHO_PLUGIN_WANT_STATE
effect->flags |= effFlagsProgramChunks;
#endif

// pointers
effect->object = nullptr;
effect->user = (void*)audioMaster;

return effect;
}

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

Loading…
Cancel
Save