summaryrefslogtreecommitdiff
path: root/muse2/muse/vst_native.cpp
diff options
context:
space:
mode:
authorTim E. Real <termtech@rogers.com>2012-12-07 07:41:24 +0000
committerTim E. Real <termtech@rogers.com>2012-12-07 07:41:24 +0000
commit0a919a7b36ee4b58e5ce3628a2d8b97bf751d2c4 (patch)
tree335fccddc229d2d0299095dfe46daae614188c79 /muse2/muse/vst_native.cpp
parenta9cef6554f73892b6c7ff6a7a44d8e72f06aa16f (diff)
Feature: Native VST instruments support. PLEASE SEE ChangeLog.
Diffstat (limited to 'muse2/muse/vst_native.cpp')
-rw-r--r--muse2/muse/vst_native.cpp2628
1 files changed, 2628 insertions, 0 deletions
diff --git a/muse2/muse/vst_native.cpp b/muse2/muse/vst_native.cpp
new file mode 100644
index 00000000..8febe142
--- /dev/null
+++ b/muse2/muse/vst_native.cpp
@@ -0,0 +1,2628 @@
+//===================================================================
+// MusE
+// Linux Music Editor
+//
+// vst_native.cpp
+// (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net)
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; version 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+//===================================================================
+
+#include "config.h"
+
+#ifdef VST_NATIVE_SUPPORT
+
+#include <QDir>
+#include <QMenu>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <dlfcn.h>
+#include <cmath>
+#include <set>
+#include <jack/jack.h>
+
+#include "globals.h"
+#include "gconfig.h"
+#include "audio.h"
+#include "synth.h"
+#include "jackaudio.h"
+#include "midi.h"
+#include "xml.h"
+#include "plugin.h"
+#include "popupmenu.h"
+
+#include "vst_native.h"
+
+#define OLD_PLUGIN_ENTRY_POINT "main"
+#define NEW_PLUGIN_ENTRY_POINT "VSTPluginMain"
+
+// Enable debugging messages
+//#define VST_NATIVE_DEBUG
+//#define VST_NATIVE_DEBUG_PROCESS
+
+namespace MusECore {
+
+extern JackAudioDevice* jackAudio;
+
+//-----------------------------------------------------------------------------------------
+// vstHostCallback
+// This must be a function, it cannot be a class method so we dispatch to various objects from here.
+//-----------------------------------------------------------------------------------------
+
+VstIntPtr VSTCALLBACK vstNativeHostCallback(AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt)
+{
+ VSTPlugin* plugin;
+ if(effect && effect->user)
+ {
+ plugin = (VSTPlugin*)(effect->user);
+ return ((VstNativeSynthIF*)plugin)->hostCallback(opcode, index, value, ptr, opt);
+ }
+
+#ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "vstNativeHostCallback eff:%p opcode:%ld\n", effect, opcode);
+#endif
+
+ switch (opcode) {
+ case audioMasterAutomate:
+ // index, value, returns 0
+ return 0;
+
+ case audioMasterVersion:
+ // vst version, currently 2 (0 for older)
+ return 2300;
+
+ case audioMasterCurrentId:
+ // returns the unique id of a plug that's currently
+ // loading
+ return 0;
+
+ case audioMasterIdle:
+ // call application idle routine (this will
+ // call effEditIdle for all open editors too)
+ return 0;
+
+ case audioMasterPinConnected:
+ // inquire if an input or output is beeing connected;
+ // index enumerates input or output counting from zero:
+ // value is 0 for input and != 0 otherwise. note: the
+ // return value is 0 for <true> such that older versions
+ // will always return true.
+ return 1;
+
+ case audioMasterWantMidi:
+ // <value> is a filter which is currently ignored
+ return 0;
+
+ case audioMasterGetTime:
+ // returns const VstTimeInfo* (or 0 if not supported)
+ // <value> should contain a mask indicating which fields are required
+ // (see valid masks above), as some items may require extensive
+ // conversions
+ return 0;
+
+ case audioMasterProcessEvents:
+ // VstEvents* in <ptr>
+ return 0;
+
+ case audioMasterSetTime:
+ // VstTimenfo* in <ptr>, filter in <value>, not supported
+ return 0;
+
+ case audioMasterTempoAt:
+ // returns tempo (in bpm * 10000) at sample frame location passed in <value>
+ return 0;
+
+ case audioMasterGetNumAutomatableParameters:
+ return 0;
+
+ case audioMasterGetParameterQuantization:
+ // returns the integer value for +1.0 representation,
+ // or 1 if full single float precision is maintained
+ // in automation. parameter index in <value> (-1: all, any)
+ return 0;
+
+ case audioMasterIOChanged:
+ // numInputs and/or numOutputs has changed
+ return 0;
+
+ case audioMasterNeedIdle:
+ // plug needs idle calls (outside its editor window)
+ return 0;
+
+ case audioMasterSizeWindow:
+ // index: width, value: height
+ return 0;
+
+ case audioMasterGetSampleRate:
+ return MusEGlobal::sampleRate;
+
+ case audioMasterGetBlockSize:
+ return MusEGlobal::segmentSize;
+
+ case audioMasterGetInputLatency:
+ return 0;
+
+ case audioMasterGetOutputLatency:
+ return 0;
+
+ case audioMasterGetPreviousPlug:
+ // input pin in <value> (-1: first to come), returns cEffect*
+ return 0;
+
+ case audioMasterGetNextPlug:
+ // output pin in <value> (-1: first to come), returns cEffect*
+
+ case audioMasterWillReplaceOrAccumulate:
+ // returns: 0: not supported, 1: replace, 2: accumulate
+ return 0;
+
+ case audioMasterGetCurrentProcessLevel:
+ // returns: 0: not supported,
+ // 1: currently in user thread (gui)
+ // 2: currently in audio thread (where process is called)
+ // 3: currently in 'sequencer' thread (midi, timer etc)
+ // 4: currently offline processing and thus in user thread
+ // other: not defined, but probably pre-empting user thread.
+ return 0;
+
+ case audioMasterGetAutomationState:
+ // returns 0: not supported, 1: off, 2:read, 3:write, 4:read/write
+ // offline
+ return 0;
+
+ case audioMasterOfflineStart:
+ case audioMasterOfflineRead:
+ // ptr points to offline structure, see below. return 0: error, 1 ok
+ return 0;
+
+ case audioMasterOfflineWrite:
+ // same as read
+ return 0;
+
+ case audioMasterOfflineGetCurrentPass:
+ case audioMasterOfflineGetCurrentMetaPass:
+ return 0;
+
+ case audioMasterSetOutputSampleRate:
+ // for variable i/o, sample rate in <opt>
+ return 0;
+
+ case audioMasterGetSpeakerArrangement:
+ // (long)input in <value>, output in <ptr>
+ return 0;
+
+ case audioMasterGetVendorString:
+ // fills <ptr> with a string identifying the vendor (max 64 char)
+ strcpy ((char*) ptr, "MusE");
+ return 1;
+
+ case audioMasterGetProductString:
+ // fills <ptr> with a string with product name (max 64 char)
+ strcpy ((char*) ptr, "NativeVST");
+ return 1;
+
+ case audioMasterGetVendorVersion:
+ // returns vendor-specific version
+ return 2000;
+
+ case audioMasterVendorSpecific:
+ // no definition, vendor specific handling
+ return 0;
+
+ case audioMasterSetIcon:
+ // void* in <ptr>, format not defined yet
+ return 0;
+
+ case audioMasterCanDo:
+ // string in ptr, see below
+ return 0;
+
+ case audioMasterGetLanguage:
+ // see enum
+ return kVstLangEnglish;
+
+ case audioMasterOpenWindow:
+ // returns platform specific ptr
+ return 0;
+
+ case audioMasterCloseWindow:
+ // close window, platform specific handle in <ptr>
+ return 0;
+
+ case audioMasterGetDirectory:
+ // get plug directory, FSSpec on MAC, else char*
+ return 0;
+
+ case audioMasterUpdateDisplay:
+ // something has changed, update 'multi-fx' display
+ return 0;
+
+ case audioMasterBeginEdit:
+ // begin of automation session (when mouse down), parameter index in <index>
+ return 0;
+
+ case audioMasterEndEdit:
+ // end of automation session (when mouse up), parameter index in <index>
+ return 0;
+
+ case audioMasterOpenFileSelector:
+ // open a fileselector window with VstFileSelect* in <ptr>
+ return 0;
+
+ case audioMasterCloseFileSelector:
+ return 0;
+
+ default:
+ break;
+ }
+
+ if(MusEGlobal::debugMsg)
+ fprintf(stderr, " unknown opcode\n");
+
+ return 0;
+ }
+
+//---------------------------------------------------------
+// loadPluginLib
+//---------------------------------------------------------
+
+static void scanVstNativeLib(QFileInfo& fi)
+{
+ void* handle = dlopen(fi.filePath().toAscii().constData(), RTLD_NOW);
+ if (handle == NULL)
+ {
+ fprintf(stderr, "scanVstNativeLib: dlopen(%s) failed: %s\n", fi.filePath().toAscii().constData(), dlerror());
+ return;
+ }
+
+ char buffer[128];
+ QString effectName;
+ QString vendorString;
+ QString productString;
+ std::vector<Synth*>::iterator is;
+ int vst_version = 0;
+ VstNativeSynth* new_synth = NULL;
+
+ AEffect *(*getInstance)(audioMasterCallback);
+ getInstance = (AEffect*(*)(audioMasterCallback))dlsym(handle, NEW_PLUGIN_ENTRY_POINT);
+ if(!getInstance)
+ {
+ if(MusEGlobal::debugMsg)
+ {
+ fprintf(stderr, "VST 2.4 entrypoint \"" NEW_PLUGIN_ENTRY_POINT "\" not found in library %s, looking for \""
+ OLD_PLUGIN_ENTRY_POINT "\"\n", fi.filePath().toAscii().constData());
+ }
+
+ getInstance = (AEffect*(*)(audioMasterCallback))dlsym(handle, OLD_PLUGIN_ENTRY_POINT);
+ if(!getInstance)
+ {
+ fprintf(stderr, "ERROR: VST entrypoints \"" NEW_PLUGIN_ENTRY_POINT "\" or \""
+ OLD_PLUGIN_ENTRY_POINT "\" not found in library\n");
+ dlclose(handle);
+ return;
+ }
+ else if(MusEGlobal::debugMsg)
+ {
+ fprintf(stderr, "VST entrypoint \"" OLD_PLUGIN_ENTRY_POINT "\" found\n");
+ }
+ }
+ else if(MusEGlobal::debugMsg)
+ {
+ fprintf(stderr, "VST entrypoint \"" NEW_PLUGIN_ENTRY_POINT "\" found\n");
+ }
+
+ AEffect *plugin = getInstance(vstNativeHostCallback);
+ if(!plugin)
+ {
+ fprintf(stderr, "ERROR: Failed to instantiate plugin in VST library \"%s\"\n", fi.filePath().toAscii().constData());
+ dlclose(handle);
+ return;
+ }
+ else if(MusEGlobal::debugMsg)
+ fprintf(stderr, "plugin instantiated\n");
+
+ if(plugin->magic != kEffectMagic)
+ {
+ fprintf(stderr, "Not a VST plugin in library \"%s\"\n", fi.filePath().toAscii().constData());
+ dlclose(handle);
+ return;
+ }
+ else if(MusEGlobal::debugMsg)
+ fprintf(stderr, "plugin is a VST\n");
+
+ if(!(plugin->flags & effFlagsHasEditor))
+ {
+ if(MusEGlobal::debugMsg)
+ fprintf(stderr, "Plugin has no GUI\n");
+ }
+ else if(MusEGlobal::debugMsg)
+ fprintf(stderr, "Plugin has a GUI\n");
+
+ if(!(plugin->flags & effFlagsCanReplacing))
+ fprintf(stderr, "Plugin does not support processReplacing\n");
+ else if(MusEGlobal::debugMsg)
+ fprintf(stderr, "Plugin supports processReplacing\n");
+
+ plugin->dispatcher(plugin, effOpen, 0, 0, NULL, 0);
+
+ buffer[0] = 0;
+ plugin->dispatcher(plugin, effGetEffectName, 0, 0, buffer, 0);
+ if(buffer[0])
+ effectName = QString(buffer);
+
+ buffer[0] = 0;
+ plugin->dispatcher(plugin, effGetVendorString, 0, 0, buffer, 0);
+ if (buffer[0])
+ vendorString = QString(buffer);
+
+ buffer[0] = 0;
+ plugin->dispatcher(plugin, effGetProductString, 0, 0, buffer, 0);
+ if (buffer[0])
+ productString = QString(buffer);
+
+ // Make sure it doesn't already exist.
+ for(is = MusEGlobal::synthis.begin(); is != MusEGlobal::synthis.end(); ++is)
+ if((*is)->name() == effectName && (*is)->baseName() == fi.completeBaseName())
+ goto _ending;
+
+ // "2 = VST2.x, older versions return 0". Observed 2400 on all the ones tested so far.
+ vst_version = plugin->dispatcher(plugin, effGetVstVersion, 0, 0, NULL, 0.0f);
+ if(vst_version < 2 || !((plugin->flags & effFlagsIsSynth) || (plugin->dispatcher(plugin, effCanDo, 0, 0,(void*) "receiveVstEvents", 0.0f) > 0)))
+ {
+ if(MusEGlobal::debugMsg)
+ fprintf(stderr, "Plugin is not a synth\n");
+ goto _ending;
+ }
+
+ new_synth = new VstNativeSynth(fi, plugin, effectName, productString, vendorString, QString()); // TODO Version string?
+
+ if(MusEGlobal::debugMsg)
+ fprintf(stderr, "scanVstNativeLib: adding vst synth plugin:%s name:%s effectName:%s vendorString:%s productString:%s vstver:%d\n",
+ fi.filePath().toLatin1().constData(),
+ fi.completeBaseName().toLatin1().constData(),
+ effectName.toLatin1().constData(),
+ vendorString.toLatin1().constData(),
+ productString.toLatin1().constData(),
+ vst_version
+ );
+
+ MusEGlobal::synthis.push_back(new_synth);
+
+_ending: ;
+
+ //plugin->dispatcher(plugin, effMainsChanged, 0, 0, NULL, 0);
+ plugin->dispatcher(plugin, effClose, 0, 0, NULL, 0);
+ dlclose(handle);
+}
+
+//---------------------------------------------------------
+// scanVstDir
+//---------------------------------------------------------
+
+static void scanVstNativeDir(const QString& s)
+{
+ if (MusEGlobal::debugMsg)
+ fprintf(stderr, "scan vst native plugin dir <%s>\n", s.toLatin1().constData());
+ QDir pluginDir(s, QString("*.so"), QDir::Unsorted, QDir::Files);
+ if(!pluginDir.exists())
+ return;
+ QStringList list = pluginDir.entryList();
+ int count = list.count();
+ for(int i = 0; i < count; ++i)
+ {
+ if(MusEGlobal::debugMsg)
+ fprintf(stderr, "scanVstNativeDir: found %s\n", (s + QString("/") + list[i]).toLatin1().constData());
+
+ QFileInfo fi(s + QString("/") + list[i]);
+ scanVstNativeLib(fi);
+ }
+}
+
+//---------------------------------------------------------
+// initVST_Native
+//---------------------------------------------------------
+
+void initVST_Native()
+ {
+ const char* vstPath = getenv("VST_NATIVE_PATH"); // FIXME TODO: What's the right path and env var?
+ if (vstPath == 0)
+ vstPath = "/usr/lib/vst:/usr/local/lib/vst";
+
+ const char* p = vstPath;
+ while (*p != '\0') {
+ const char* pe = p;
+ while (*pe != ':' && *pe != '\0')
+ pe++;
+
+ int n = pe - p;
+ if (n) {
+ char* buffer = new char[n + 1];
+ strncpy(buffer, p, n);
+ buffer[n] = '\0';
+ scanVstNativeDir(QString(buffer));
+ delete[] buffer;
+ }
+ p = pe;
+ if (*p == ':')
+ p++;
+ }
+ }
+
+//---------------------------------------------------------
+// VstNativeSynth
+//---------------------------------------------------------
+
+VstNativeSynth::VstNativeSynth(const QFileInfo& fi, AEffect* plugin, const QString& label, const QString& desc, const QString& maker, const QString& ver)
+ : Synth(fi, label, desc, maker, ver)
+{
+ _handle = NULL;
+ _hasGui = plugin->flags & effFlagsHasEditor;
+ _inports = plugin->numInputs;
+ _outports = plugin->numOutputs;
+ _controlInPorts = plugin->numParams;
+ _inPlaceCapable = false; //(plugin->flags & effFlagsCanReplacing) && (_inports == _outports) && MusEGlobal::config.vstInPlace;
+#ifdef VST_SDK_SUPPORT
+ _hasChunks = plugin->flags & effFlagsProgramChunks;
+#else
+ _hasChunks = false;
+#endif
+
+ _flags = 0;
+ _vst_version = 0;
+ _vst_version = plugin->dispatcher(plugin, effGetVstVersion, 0, 0, NULL, 0.0f);
+ // "2 = VST2.x, older versions return 0". Observed 2400 on all the ones tested so far.
+ if(_vst_version >= 2)
+ {
+ if(plugin->dispatcher(plugin, effCanDo, 0, 0, (void*)"receiveVstEvents", 0.0f) > 0)
+ _flags |= canReceiveVstEvents;
+ if(plugin->dispatcher(plugin, effCanDo, 0, 0, (void*)"sendVstEvents", 0.0f) > 0)
+ _flags |= canSendVstEvents;
+ if(plugin->dispatcher(plugin, effCanDo, 0, 0, (void*)"sendVstMidiEvent", 0.0f) > 0)
+ _flags |= canSendVstMidiEvents;
+ if(plugin->dispatcher(plugin, effCanDo, 0, 0, (void*)"sendVstTimeInfo", 0.0f) > 0)
+ _flags |= canSendVstTimeInfo;
+ if(plugin->dispatcher(plugin, effCanDo, 0, 0, (void*)"receiveVstMidiEvent", 0.0f) > 0)
+ _flags |= canReceiveVstMidiEvents;
+ if(plugin->dispatcher(plugin, effCanDo, 0, 0, (void*)"receiveVstTimeInfo", 0.0f) > 0)
+ _flags |= canReceiveVstTimeInfo;
+ if(plugin->dispatcher(plugin, effCanDo, 0, 0, (void*)"offline", 0.0f) > 0)
+ _flags |=canProcessOffline;
+ if(plugin->dispatcher(plugin, effCanDo, 0, 0, (void*)"plugAsChannelInsert", 0.0f) > 0)
+ _flags |= canUseAsInsert;
+ if(plugin->dispatcher(plugin, effCanDo, 0, 0, (void*)"plugAsSend", 0.0f) > 0)
+ _flags |= canUseAsSend;
+ if(plugin->dispatcher(plugin, effCanDo, 0, 0, (void*)"mixDryWet", 0.0f) > 0)
+ _flags |= canMixDryWet;
+ if(plugin->dispatcher(plugin, effCanDo, 0, 0, (void*)"midiProgramNames", 0.0f) > 0)
+ _flags |= canMidiProgramNames;
+ }
+}
+
+//---------------------------------------------------------
+// incInstances
+//---------------------------------------------------------
+
+void VstNativeSynth::incInstances(int val)
+{
+ _instances += val;
+ if(_instances == 0)
+ {
+ if(_handle)
+ {
+ #ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "VstNativeSynth::incInstances no more instances, closing library\n");
+ #endif
+
+ dlclose(_handle);
+ }
+ _handle = NULL;
+ iIdx.clear();
+ oIdx.clear();
+ rpIdx.clear();
+ midiCtl2PortMap.clear();
+ port2MidiCtlMap.clear();
+ }
+}
+
+//---------------------------------------------------------
+// instantiate
+//---------------------------------------------------------
+
+AEffect* VstNativeSynth::instantiate()
+{
+ int inst_num = _instances;
+ inst_num++;
+ QString n;
+ n.setNum(inst_num);
+ QString instanceName = baseName() + "-" + n;
+ QByteArray ba = info.filePath().toLatin1();
+ const char* path = ba.constData();
+ void* hnd = _handle;
+ int vst_version;
+
+ if(hnd == NULL)
+ {
+ hnd = dlopen(path, RTLD_NOW);
+ if(hnd == NULL)
+ {
+ fprintf(stderr, "dlopen(%s) failed: %s\n", path, dlerror());
+ return NULL;
+ }
+ }
+
+ AEffect *(*getInstance)(audioMasterCallback);
+ getInstance = (AEffect*(*)(audioMasterCallback))dlsym(hnd, NEW_PLUGIN_ENTRY_POINT);
+ if(!getInstance)
+ {
+ if(MusEGlobal::debugMsg)
+ {
+ fprintf(stderr, "VST 2.4 entrypoint \"" NEW_PLUGIN_ENTRY_POINT "\" not found in library %s, looking for \""
+ OLD_PLUGIN_ENTRY_POINT "\"\n", path);
+ }
+
+ getInstance = (AEffect*(*)(audioMasterCallback))dlsym(hnd, OLD_PLUGIN_ENTRY_POINT);
+ if(!getInstance)
+ {
+ fprintf(stderr, "ERROR: VST entrypoints \"" NEW_PLUGIN_ENTRY_POINT "\" or \""
+ OLD_PLUGIN_ENTRY_POINT "\" not found in library\n");
+ dlclose(hnd);
+ return NULL;
+ }
+ else if(MusEGlobal::debugMsg)
+ {
+ fprintf(stderr, "VST entrypoint \"" OLD_PLUGIN_ENTRY_POINT "\" found\n");
+ }
+ }
+ else if(MusEGlobal::debugMsg)
+ {
+ fprintf(stderr, "VST entrypoint \"" NEW_PLUGIN_ENTRY_POINT "\" found\n");
+ }
+
+ AEffect *plugin = getInstance(vstNativeHostCallback);
+ if(!plugin)
+ {
+ fprintf(stderr, "ERROR: Failed to instantiate plugin in VST library \"%s\"\n", path);
+ dlclose(hnd);
+ return NULL;
+ }
+ else if(MusEGlobal::debugMsg)
+ fprintf(stderr, "plugin instantiated\n");
+
+ if(plugin->magic != kEffectMagic)
+ {
+ fprintf(stderr, "Not a VST plugin in library \"%s\"\n", path);
+ dlclose(hnd);
+ return NULL;
+ }
+ else if(MusEGlobal::debugMsg)
+ fprintf(stderr, "plugin is a VST\n");
+
+ if(!(plugin->flags & effFlagsHasEditor))
+ {
+ if(MusEGlobal::debugMsg)
+ fprintf(stderr, "Plugin has no GUI\n");
+ }
+ else if(MusEGlobal::debugMsg)
+ fprintf(stderr, "Plugin has a GUI\n");
+
+ if(!(plugin->flags & effFlagsCanReplacing))
+ fprintf(stderr, "Plugin does not support processReplacing\n");
+ else if(MusEGlobal::debugMsg)
+ fprintf(stderr, "Plugin supports processReplacing\n");
+
+ plugin->dispatcher(plugin, effOpen, 0, 0, NULL, 0);
+
+ // "2 = VST2.x, older versions return 0". Observed 2400 on all the ones tested so far.
+ vst_version = plugin->dispatcher(plugin, effGetVstVersion, 0, 0, NULL, 0.0f);
+ if(vst_version < 2 || !((plugin->flags & effFlagsIsSynth) || (plugin->dispatcher(plugin, effCanDo, 0, 0,(void*) "receiveVstEvents", 0.0f) > 0)))
+ {
+ if(MusEGlobal::debugMsg)
+ fprintf(stderr, "Plugin is not a synth\n");
+ goto _error;
+ }
+
+ ++_instances;
+ _handle = hnd;
+
+ plugin->dispatcher(plugin, effOpen, 0, 0, NULL, 0);
+ //plugin->dispatcher(plugin, effSetProgram, 0, 0, NULL, 0.0f); // REMOVE Tim. Or keep?
+ return plugin;
+
+_error:
+ //plugin->dispatcher(plugin, effMainsChanged, 0, 0, NULL, 0);
+ plugin->dispatcher(plugin, effClose, 0, 0, NULL, 0);
+ dlclose(hnd);
+ return NULL;
+}
+
+//---------------------------------------------------------
+// createSIF
+//---------------------------------------------------------
+
+SynthIF* VstNativeSynth::createSIF(SynthI* s)
+ {
+ VstNativeSynthIF* sif = new VstNativeSynthIF(s);
+ sif->init(this);
+ return sif;
+ }
+
+//---------------------------------------------------------
+// VstNativeSynthIF
+//---------------------------------------------------------
+
+VstNativeSynthIF::VstNativeSynthIF(SynthI* s) : SynthIF(s)
+{
+ _guiVisible = false;
+ _synth = NULL;
+ _plugin = NULL;
+ _editor = NULL;
+ _inProcess = false;
+ _controls = NULL;
+// controlsOut = 0;
+ _audioInBuffers = NULL;
+ _audioInSilenceBuf = NULL;
+ //_audioInSilenceBufs = NULL;
+ _audioOutBuffers = NULL;
+}
+
+VstNativeSynthIF::~VstNativeSynthIF()
+{
+ // Just in case it wasn't removed or deactivate3 wasn't called etc...
+ if(_plugin)
+ fprintf(stderr, "ERROR: ~VstNativeSynthIF: _plugin is not NULL!\n");
+
+ if(_audioOutBuffers)
+ {
+ unsigned long op = _synth->outPorts();
+ for(unsigned long i = 0; i < op; ++i)
+ {
+ if(_audioOutBuffers[i])
+ free(_audioOutBuffers[i]);
+ }
+ delete[] _audioOutBuffers;
+ }
+
+ if(_audioInBuffers)
+ {
+ unsigned long ip = _synth->inPorts();
+ for(unsigned long i = 0; i < ip; ++i)
+ {
+ if(_audioInBuffers[i])
+ free(_audioInBuffers[i]);
+ }
+ delete[] _audioInBuffers;
+ }
+
+ if(_audioInSilenceBuf)
+ free(_audioInSilenceBuf);
+
+// if(_audioInSilenceBufs)
+// {
+// for(unsigned long i = 0; i < _synth->inPorts(); ++i)
+// {
+// if(_audioInSilenceBufs[i])
+// free(_audioInSilenceBufs[i]);
+// }
+// delete[] _audioInSilenceBufs;
+// }
+
+ if(_controls)
+ delete[] _controls;
+}
+
+//---------------------------------------------------------
+// init
+//---------------------------------------------------------
+
+bool VstNativeSynthIF::init(Synth* s)
+ {
+ _synth = (VstNativeSynth*)s;
+ _plugin = _synth->instantiate();
+ if(!_plugin)
+ return false;
+ _plugin->user = this;
+
+ queryPrograms();
+
+ unsigned long outports = _synth->outPorts();
+ if(outports != 0)
+ {
+ _audioOutBuffers = new float*[outports];
+ for(unsigned long k = 0; k < outports; ++k)
+ {
+ posix_memalign((void**)&_audioOutBuffers[k], 16, sizeof(float) * MusEGlobal::segmentSize);
+ memset(_audioOutBuffers[k], 0, sizeof(float) * MusEGlobal::segmentSize);
+ }
+ }
+
+ unsigned long inports = _synth->inPorts();
+ if(inports != 0)
+ {
+ _audioInBuffers = new float*[inports];
+ //_audioInSilenceBufs = new float*[inports];
+ for(unsigned long k = 0; k < inports; ++k)
+ {
+ posix_memalign((void**)&_audioInBuffers[k], 16, sizeof(float) * MusEGlobal::segmentSize);
+ memset(_audioInBuffers[k], 0, sizeof(float) * MusEGlobal::segmentSize);
+ //posix_memalign((void**)&_audioInSilenceBufs[k], 16, sizeof(float) * MusEGlobal::segmentSize);
+ //memset(_audioInSilenceBufs[k], 0, sizeof(float) * MusEGlobal::segmentSize);
+ _iUsedIdx.push_back(false); // Start out with all false.
+ }
+
+ posix_memalign((void**)&_audioInSilenceBuf, 16, sizeof(float) * MusEGlobal::segmentSize);
+ memset(_audioInSilenceBuf, 0, sizeof(float) * MusEGlobal::segmentSize);
+ }
+
+ int controlPorts = _synth->inControls();
+ if(controlPorts != 0)
+ _controls = new Port[controlPorts];
+ else
+ _controls = NULL;
+
+ //_synth->midiCtl2PortMap.clear();
+ //_synth->port2MidiCtlMap.clear();
+
+ for(unsigned long i = 0; i < _synth->inControls(); ++i)
+ {
+ _controls[i].idx = i;
+ //float val; // TODO
+ //ladspaDefaultValue(ld, k, &val); // FIXME TODO
+ float val = _plugin->getParameter(_plugin, i); // TODO
+ _controls[i].val = val;
+ _controls[i].tmpVal = val;
+ _controls[i].enCtrl = true;
+ _controls[i].en2Ctrl = true;
+
+ // Support a special block for synth ladspa controllers.
+ // Put the ID at a special block after plugins (far after).
+ int id = genACnum(MAX_PLUGINS, i);
+ const char* param_name = paramName(i);
+
+ // TODO FIXME!
+ ///float min, max;
+ ///ladspaControlRange(ld, k, &min, &max);
+ float min = 0.0, max = 1.0;
+
+ CtrlList* cl;
+ CtrlListList* cll = ((MusECore::AudioTrack*)synti)->controller();
+ iCtrlList icl = cll->find(id);
+ if (icl == cll->end())
+ {
+ cl = new CtrlList(id);
+ cll->add(cl);
+ //cl->setCurVal(controls[cip].val);
+ cl->setCurVal(_plugin->getParameter(_plugin, i));
+ }
+ else
+ {
+ cl = icl->second;
+ ///controls[cip].val = cl->curVal();
+ //setParam(i, cl->curVal());
+ if(dispatch(effCanBeAutomated, i, 0, NULL, 0.0f) == 1)
+ _plugin->setParameter(_plugin, i, cl->curVal());
+#ifdef VST_NATIVE_DEBUG
+ else
+ fprintf(stderr, "VstNativeSynthIF::init %s parameter:%lu cannot be automated\n", name().toLatin1().constData(), i);
+#endif
+
+ }
+ cl->setRange(min, max);
+ cl->setName(QString(param_name));
+ //cl->setValueType(ladspaCtrlValueType(ld, k));
+ cl->setValueType(ctrlValueType(i));
+ //cl->setMode(ladspaCtrlMode(ld, k));
+ cl->setMode(ctrlMode(i));
+ }
+
+ activate();
+ doSelectProgram(synti->_curBankH, synti->_curBankL, synti->_curProgram);
+ //doSelectProgram(synti->_curProgram);
+
+ return true;
+ }
+
+//---------------------------------------------------------
+// resizeEditor
+//---------------------------------------------------------
+
+bool VstNativeSynthIF::resizeEditor(int w, int h)
+{
+ if(!_editor || w <= 0 || h <= 0)
+ return false;
+ _editor->resize(w, h);
+ return true;
+}
+
+//---------------------------------------------------------
+// hostCallback
+//---------------------------------------------------------
+
+VstIntPtr VstNativeSynthIF::hostCallback(VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt)
+ {
+ //static VstTimeInfo _timeInfo;
+ //jack_position_t jack_pos;
+ //jack_transport_state_t tstate;
+
+#ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "VstNativeSynthIF::hostCallback %s opcode:%ld\n", name().toLatin1().constData(), opcode);
+#endif
+
+ switch (opcode) {
+ case audioMasterAutomate:
+ // index, value, returns 0
+ ///_plugin->setParameter (_plugin, index, opt);
+ guiControlChanged(index, opt);
+ return 0;
+
+ case audioMasterVersion:
+ // vst version, currently 2 (0 for older)
+ return 2300;
+
+ case audioMasterCurrentId:
+ // returns the unique id of a plug that's currently
+ // loading
+ return 0;
+
+ case audioMasterIdle:
+ // call application idle routine (this will
+ // call effEditIdle for all open editors too)
+ //_plugin->updateParamValues(false);
+ //_plugin->dispatcher(_plugin, effEditIdle, 0, 0, NULL, 0.0f);
+ idleEditor();
+ return 0;
+
+ case audioMasterGetTime:
+ // returns const VstTimeInfo* (or 0 if not supported)
+ // <value> should contain a mask indicating which fields are required
+ // (see valid masks above), as some items may require extensive
+ // conversions
+
+// FIXME: TODO: Change this to MusE tempo and sig, and even if Jack is used,
+// the jackAudio pointer may be 0. Jack may not be running and dummy used instead.
+#if 1
+ return 0;
+#else
+ memset(&_timeInfo, 0, sizeof(_timeInfo));
+
+ if (_plugin) {
+ tstate = jackAudio->transportQuery(&jack_pos);
+
+ _timeInfo.samplePos = jack_pos.frame;
+ _timeInfo.sampleRate = jack_pos.frame_rate;
+ _timeInfo.flags = 0;
+
+ if ((value & (kVstBarsValid|kVstTempoValid)) && (jack_pos.valid & JackPositionBBT)) {
+ _timeInfo.tempo = jack_pos.beats_per_minute;
+ _timeInfo.timeSigNumerator = (long) floor (jack_pos.beats_per_bar);
+ _timeInfo.timeSigDenominator = (long) floor (jack_pos.beat_type);
+ _timeInfo.flags |= (kVstBarsValid|kVstTempoValid);
+ }
+ if (tstate == JackTransportRolling) {
+ _timeInfo.flags |= kVstTransportPlaying;
+ }
+ }
+ else {
+ _timeInfo.samplePos = 0;
+ _timeInfo.sampleRate = MusEGlobal::sampleRate;
+ }
+ return (long)&_timeInfo;
+#endif
+
+ case audioMasterProcessEvents:
+ // VstEvents* in <ptr>
+ return 0; // TODO:
+
+ case audioMasterIOChanged:
+ // numInputs and/or numOutputs has changed
+ return 0;
+
+ case audioMasterSizeWindow:
+ // index: width, value: height
+ if(resizeEditor(int(index), int(value)))
+ return 1; // supported.
+ return 0;
+
+ case audioMasterGetSampleRate:
+ //return 0;
+ return MusEGlobal::sampleRate;
+
+ case audioMasterGetBlockSize:
+ //return 0;
+ return MusEGlobal::segmentSize;
+
+ case audioMasterGetInputLatency:
+ return 0;
+
+ case audioMasterGetOutputLatency:
+ return 0;
+
+ case audioMasterGetCurrentProcessLevel:
+ // returns: 0: not supported,
+ // 1: currently in user thread (gui)
+ // 2: currently in audio thread (where process is called)
+ // 3: currently in 'sequencer' thread (midi, timer etc)
+ // 4: currently offline processing and thus in user thread
+ // other: not defined, but probably pre-empting user thread.
+ if(_inProcess)
+ return 2;
+ else
+ return 1;
+
+ case audioMasterGetAutomationState:
+ // returns 0: not supported, 1: off, 2:read, 3:write, 4:read/write
+ // offline
+ return 1; // TODO:
+
+ case audioMasterOfflineStart:
+ case audioMasterOfflineRead:
+ // ptr points to offline structure, see below. return 0: error, 1 ok
+ return 0;
+
+ case audioMasterOfflineWrite:
+ // same as read
+ return 0;
+
+ case audioMasterOfflineGetCurrentPass:
+ case audioMasterOfflineGetCurrentMetaPass:
+ return 0;
+
+ case audioMasterGetVendorString:
+ // fills <ptr> with a string identifying the vendor (max 64 char)
+ strcpy ((char*) ptr, "MusE");
+ return 1;
+
+ case audioMasterGetProductString:
+ // fills <ptr> with a string with product name (max 64 char)
+ strcpy ((char*) ptr, "MusE Sequencer");
+ return 1;
+
+ case audioMasterGetVendorVersion:
+ // returns vendor-specific version
+ return 2000;
+
+ case audioMasterVendorSpecific:
+ // no definition, vendor specific handling
+ return 0;
+
+ case audioMasterCanDo:
+ // string in ptr, see below
+ if(!strcmp((char*)ptr, "sendVstEvents") ||
+ !strcmp((char*)ptr, "receiveVstMidiEvent") ||
+ !strcmp((char*)ptr, "sendVstMidiEvent") ||
+ !strcmp((char*)ptr, "sendVstTimeInfo") ||
+ !strcmp((char*)ptr, "sizeWindow") ||
+ !strcmp((char*)ptr, "supplyIdle"))
+ return 1;
+
+#if 0 //ifdef VST_SDK_SUPPORT
+ else
+ if(!strcmp((char*)ptr, "openFileSelector") ||
+ !strcmp((char*)ptr, "closeFileSelector"))
+ return 1;
+#endif
+ return 0;
+
+ case audioMasterGetLanguage:
+ // see enum
+ //return 0;
+ return kVstLangEnglish;
+
+ case audioMasterGetDirectory:
+ // get plug directory, FSSpec on MAC, else char*
+ return 0;
+
+ case audioMasterUpdateDisplay:
+ // something has changed, update 'multi-fx' display
+
+ //_plugin->updateParamValues(false);
+ //QApplication::processEvents(); // REMOVE Tim. Or keep. Commented in QTractor.
+
+ _plugin->dispatcher(_plugin, effEditIdle, 0, 0, NULL, 0.0f); // ?
+
+ return 0;
+
+ case audioMasterBeginEdit:
+ // begin of automation session (when mouse down), parameter index in <index>
+ guiAutomationBegin(index);
+ return 1;
+
+ case audioMasterEndEdit:
+ // end of automation session (when mouse up), parameter index in <index>
+ guiAutomationEnd(index);
+ return 1;
+
+#if 0 //ifdef VST_SDK_SUPPORT
+ case audioMasterOpenFileSelector:
+ // open a fileselector window with VstFileSelect* in <ptr>
+ return 0;
+
+ case audioMasterCloseFileSelector:
+ return 0;
+#endif
+
+#ifdef VST_NATIVE_FORCE_DEPRECATED
+
+ case audioMasterGetSpeakerArrangement:
+ // (long)input in <value>, output in <ptr>
+ return 0;
+
+ case audioMasterPinConnected:
+ // inquire if an input or output is beeing connected;
+ // index enumerates input or output counting from zero:
+ // value is 0 for input and != 0 otherwise. note: the
+ // return value is 0 for <true> such that older versions
+ // will always return true.
+ //return 1;
+ return 0;
+
+ // VST 2.0 opcodes...
+ case audioMasterWantMidi:
+ // <value> is a filter which is currently ignored
+ return 0;
+
+ case audioMasterSetTime:
+ // VstTimenfo* in <ptr>, filter in <value>, not supported
+ return 0;
+
+ case audioMasterTempoAt:
+ // returns tempo (in bpm * 10000) at sample frame location passed in <value>
+ return 0; // TODO:
+
+ case audioMasterGetNumAutomatableParameters:
+ return 0;
+
+ case audioMasterGetParameterQuantization:
+ // returns the integer value for +1.0 representation,
+ // or 1 if full single float precision is maintained
+ // in automation. parameter index in <value> (-1: all, any)
+ //return 0;
+ return 1;
+
+ case audioMasterNeedIdle:
+ // plug needs idle calls (outside its editor window)
+ return 0;
+
+ case audioMasterGetPreviousPlug:
+ // input pin in <value> (-1: first to come), returns cEffect*
+ return 0;
+
+ case audioMasterGetNextPlug:
+ // output pin in <value> (-1: first to come), returns cEffect*
+ return 0;
+
+ case audioMasterWillReplaceOrAccumulate:
+ // returns: 0: not supported, 1: replace, 2: accumulate
+ //return 0;
+ return 1;
+
+ case audioMasterSetOutputSampleRate:
+ // for variable i/o, sample rate in <opt>
+ return 0;
+
+ case audioMasterSetIcon:
+ // void* in <ptr>, format not defined yet
+ return 0;
+
+ case audioMasterOpenWindow:
+ // returns platform specific ptr
+ return 0;
+
+ case audioMasterCloseWindow:
+ // close window, platform specific handle in <ptr>
+ return 0;
+#endif
+
+
+ default:
+ break;
+ }
+ return 0;
+ }
+
+//---------------------------------------------------------
+// idleEditor
+//---------------------------------------------------------
+
+void VstNativeSynthIF::idleEditor()
+{
+#ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "VstNativeSynthIF::idleEditor %p\n", this);
+#endif
+
+ _plugin->dispatcher(_plugin, effEditIdle, 0, 0, NULL, 0.0f);
+ if(_editor)
+ _editor->update();
+}
+
+//---------------------------------------------------------
+// nativeGuiVisible
+//---------------------------------------------------------
+
+bool VstNativeSynthIF::nativeGuiVisible() const
+ {
+ return _guiVisible;
+ }
+
+//---------------------------------------------------------
+// guiVisible
+//---------------------------------------------------------
+
+bool VstNativeSynthIF::guiVisible() const
+ {
+ return _gui && _gui->isVisible();
+ }
+
+//---------------------------------------------------------
+// showGui
+//---------------------------------------------------------
+
+void VstNativeSynthIF::showGui(bool v)
+{
+ if (v) {
+ if (_gui == 0)
+ makeGui();
+ _gui->show();
+ }
+ else {
+ if (_gui)
+ _gui->hide();
+ }
+}
+
+//---------------------------------------------------------
+// showGui
+//---------------------------------------------------------
+
+void VstNativeSynthIF::showNativeGui(bool v)
+ {
+ if(!(_plugin->flags & effFlagsHasEditor)) // || v == nativeGuiVisible())
+ return;
+ if(v)
+ {
+ if(_editor)
+ {
+ if(!_editor->isVisible())
+ _editor->show();
+ _editor->raise();
+ _editor->activateWindow();
+ }
+ else
+ {
+ Qt::WindowFlags wflags = Qt::Window
+ | Qt::CustomizeWindowHint
+ | Qt::WindowTitleHint
+ | Qt::WindowSystemMenuHint
+ | Qt::WindowMinMaxButtonsHint
+ | Qt::WindowCloseButtonHint;
+ _editor = new MusEGui::VstNativeEditor(NULL, wflags);
+ _editor->open(this);
+
+ // TODO TEST Test if these might be helpful, especially opaque event.
+ // _editor->setBackgroundRole(QPalette::NoRole);
+ // _editor->setAttribute(Qt::WA_NoSystemBackground);
+ // _editor->setAttribute(Qt::WA_StaticContents);
+ // // This is absolutely required for speed! Otherwise painfully slow because of full background
+ // // filling, even when requesting small udpdates! Background is drawn by us.
+ // _editor->setAttribute(Qt::WA_OpaquePaintEvent);
+ // //_editor->setFrameStyle(QFrame::Raised | QFrame::StyledPanel);
+ }
+ }
+ else
+ {
+ if(_editor)
+ {
+ delete _editor;
+ //_editor = NULL; // No - done in editorDeleted.
+ }
+ }
+ _guiVisible = v;
+}
+
+//---------------------------------------------------------
+// editorOpened
+//---------------------------------------------------------
+
+void VstNativeSynthIF::editorOpened()
+{
+ _guiVisible = true;
+}
+
+//---------------------------------------------------------
+// editorClosed
+//---------------------------------------------------------
+
+void VstNativeSynthIF::editorClosed()
+{
+ _guiVisible = false;
+}
+
+//---------------------------------------------------------
+// editorDeleted
+//---------------------------------------------------------
+
+void VstNativeSynthIF::editorDeleted()
+{
+ _editor = NULL;
+}
+
+//---------------------------------------------------------
+// receiveEvent
+//---------------------------------------------------------
+
+MidiPlayEvent VstNativeSynthIF::receiveEvent()
+ {
+ return MidiPlayEvent();
+ }
+
+//---------------------------------------------------------
+// hasGui
+//---------------------------------------------------------
+
+bool VstNativeSynthIF::hasNativeGui() const
+ {
+ return _plugin->flags & effFlagsHasEditor;
+ }
+
+//---------------------------------------------------------
+// channels
+//---------------------------------------------------------
+
+int VstNativeSynthIF::channels() const
+ {
+ //return _plugin->numOutputs;
+ return _plugin->numOutputs > MAX_CHANNELS ? MAX_CHANNELS : _plugin->numOutputs ;
+ }
+
+int VstNativeSynthIF::totalOutChannels() const
+ {
+ return _plugin->numOutputs;
+ }
+
+int VstNativeSynthIF::totalInChannels() const
+ {
+ return _plugin->numInputs;
+ }
+
+//---------------------------------------------------------
+// deactivate3
+//---------------------------------------------------------
+
+void VstNativeSynthIF::deactivate3()
+ {
+ if(_editor)
+ {
+ delete _editor;
+ _editor = NULL;
+ _guiVisible = false;
+ }
+
+ deactivate();
+ if (_plugin) {
+ _plugin->dispatcher (_plugin, effClose, 0, 0, NULL, 0);
+ _plugin = NULL;
+ }
+ }
+
+//---------------------------------------------------------
+// queryPrograms
+//---------------------------------------------------------
+
+void VstNativeSynthIF::queryPrograms()
+{
+ char buf[256];
+ programs.clear();
+ int num_progs = _plugin->numPrograms;
+ int iOldIndex = dispatch(effGetProgram, 0, 0, NULL, 0.0f);
+ bool need_restore = false;
+ for(int prog = 0; prog < num_progs; ++prog)
+ {
+ buf[0] = 0;
+
+//#ifdef VST_SDK_SUPPORT
+ // value = category. -1 = regular linear.
+ if(dispatch(effGetProgramNameIndexed, prog, -1, buf, 0.0f) == 0)
+ {
+//#endif
+ dispatch(effSetProgram, 0, prog, NULL, 0.0f);
+ dispatch(effGetProgramName, 0, 0, buf, 0.0f);
+ need_restore = true;
+//#ifdef VST_SDK_SUPPORT
+ }
+//#endif
+
+ VST_Program p;
+ p.name = QString(buf);
+ //p.program = prog & 0x7f;
+ //p.bank = prog << 7;
+ p.program = prog;
+ programs.push_back(p);
+ }
+
+ // Restore current program.
+ if(need_restore) // && num_progs > 0)
+ {
+ dispatch(effSetProgram, 0, iOldIndex, NULL, 0.0f);
+ fprintf(stderr, "FIXME: VstNativeSynthIF::queryPrograms(): effGetProgramNameIndexed returned 0. Used ugly effSetProgram/effGetProgramName instead\n");
+ }
+}
+
+//---------------------------------------------------------
+// doSelectProgram
+//---------------------------------------------------------
+
+void VstNativeSynthIF::doSelectProgram(int bankH, int bankL, int prog)
+{
+ if(!_plugin)
+ return;
+
+#ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "VstNativeSynthIF::doSelectProgram bankH:%d bankL:%d prog:%d\n", bankH, bankL, prog);
+#endif
+
+ if(bankH == 0xff)
+ bankH = 0;
+ if(bankL == 0xff)
+ bankL = 0;
+ if(prog == 0xff)
+ prog = 0;
+
+ int p = (bankH << 14) + (bankL << 7) + prog;
+
+ if(p >= _plugin->numPrograms)
+ {
+ fprintf(stderr, "VstNativeSynthIF::doSelectProgram program:%d out of range\n", p);
+ return;
+ }
+
+ //for (unsigned short i = 0; i < instances(); ++i)
+ //{
+ // "host calls this before a new program (effSetProgram) is loaded"
+ //if(dispatch(effBeginSetProgram, 0, 0, NULL, 0.0f) == 1) // TESTED: Usually it did not acknowledge. So IGNORE it.
+ dispatch(effBeginSetProgram, 0, 0, NULL, 0.0f);
+ //{
+ dispatch(effSetProgram, 0, p, NULL, 0.0f);
+ //dispatch(effSetProgram, 0, prog, NULL, 0.0f);
+ // "host calls this after the new program (effSetProgram) has been loaded"
+ dispatch(effEndSetProgram, 0, 0, NULL, 0.0f);
+ //}
+ //else
+ // fprintf(stderr, "VstNativeSynthIF::doSelectProgram bankH:%d bankL:%d prog:%d Effect did not acknowledge effBeginSetProgram\n", bankH, bankL, prog);
+ //}
+
+ // TODO: Is this true of VSTs? See the similar section in dssihost.cpp // REMOVE Tim.
+ // "A plugin is permitted to re-write the values of its input control ports when select_program is called.
+ // The host should re-read the input control port values and update its own records appropriately.
+ // (This is the only circumstance in which a DSSI plugin is allowed to modify its own input ports.)" From dssi.h
+ // Need to update the automation value, otherwise it overwrites later with the last automation value.
+ if(id() != -1)
+ {
+ for(unsigned long k = 0; k < _synth->inControls(); ++k)
+ {
+ // We're in the audio thread context: no need to send a message, just modify directly.
+ synti->setPluginCtrlVal(genACnum(id(), k), _plugin->getParameter(_plugin, k));
+ }
+ }
+
+// // Reset parameters default value... // TODO ?
+// AEffect *pVstEffect = vst_effect(0);
+// if (pVstEffect) {
+// const qtractorPlugin::Params& params = qtractorPlugin::params();
+// qtractorPlugin::Params::ConstIterator param = params.constBegin();
+// for ( ; param != params.constEnd(); ++param) {
+// qtractorPluginParam *pParam = param.value();
+// float *pfValue = pParam->subject()->data();
+// *pfValue = pVstEffect->getParameter(pVstEffect, pParam->index());
+// pParam->setDefaultValue(*pfValue);
+// }
+// }
+
+}
+
+//---------------------------------------------------------
+// getPatchName
+//---------------------------------------------------------
+
+const char* VstNativeSynthIF::getPatchName(int /*chan*/, int prog, bool /*drum*/)
+{
+ unsigned long program = prog & 0x7f;
+ unsigned long lbank = (prog >> 8) & 0xff;
+ unsigned long hbank = (prog >> 16) & 0xff;
+
+ if (lbank == 0xff)
+ lbank = 0;
+ if (hbank == 0xff)
+ hbank = 0;
+ prog = (hbank << 14) + (lbank << 7) + program;
+
+ if(prog < _plugin->numPrograms)
+ {
+ for(std::vector<VST_Program>::const_iterator i = programs.begin(); i != programs.end(); ++i)
+ {
+ if(i->program == program)
+ return i->name.toLatin1().constData();
+ }
+ }
+ return "?";
+}
+
+//---------------------------------------------------------
+// populatePatchPopup
+//---------------------------------------------------------
+
+void VstNativeSynthIF::populatePatchPopup(MusEGui::PopupMenu* menu, int /*chan*/, bool /*drum*/)
+{
+ // The plugin can change the programs, patches etc.
+ // So make sure we're up to date by calling queryPrograms.
+ queryPrograms();
+
+ menu->clear();
+
+ for (std::vector<VST_Program>::const_iterator i = programs.begin(); i != programs.end(); ++i)
+ {
+ //int bank = i->bank;
+ int prog = i->program;
+ //int id = (bank << 7) + prog;
+
+ QAction *act = menu->addAction(i->name);
+ //act->setData(id);
+ act->setData(prog);
+ }
+
+}
+
+//---------------------------------------------------------
+// getParameter
+//---------------------------------------------------------
+
+float VstNativeSynthIF::getParameter(unsigned long idx) const
+ {
+ if(idx >= _synth->inControls())
+ {
+ fprintf(stderr, "VstNativeSynthIF::getParameter param number %lu out of range of ports:%lu\n", idx, _synth->inControls());
+ return 0.0;
+ }
+
+ return _plugin->getParameter(_plugin, idx);
+ }
+
+//---------------------------------------------------------
+// setParameter
+//---------------------------------------------------------
+
+void VstNativeSynthIF::setParameter(unsigned long idx, float value)
+ {
+ //_plugin->setParameter(_plugin, idx, value);
+ addScheduledControlEvent(idx, value, MusEGlobal::audio->curFrame());
+ }
+
+//---------------------------------------------------------
+// guiAutomationBegin
+//---------------------------------------------------------
+
+void VstNativeSynthIF::guiAutomationBegin(unsigned long param_idx)
+{
+ AutomationType at = AUTO_OFF;
+ MusECore::AudioTrack* t = track();
+ if(t)
+ at = t->automationType();
+
+ // FIXME TODO: This stuff should probably be sent as control FIFO events.
+ // And probably not safe - accessing from gui and audio threads...
+
+ if (at == AUTO_READ || at == AUTO_TOUCH || at == AUTO_WRITE)
+ enableController(param_idx, false);
+
+ int plug_id = id();
+
+ if(plug_id == -1)
+ return;
+
+ plug_id = MusECore::genACnum(plug_id, param_idx);
+
+ //if(params[param].type == GuiParam::GUI_SLIDER)
+ //{
+ //double val = ((Slider*)params[param].actuator)->value();
+ float val = param(param_idx);
+ // FIXME TODO:
+ //if (LADSPA_IS_HINT_LOGARITHMIC(params[param].hint))
+ // val = pow(10.0, val/20.0);
+ //else if (LADSPA_IS_HINT_INTEGER(params[param].hint))
+ // val = rint(val);
+ //plugin->setParam(param, val);
+ //((DoubleLabel*)params[param].label)->setValue(val);
+
+ if(t)
+ {
+ t->setPluginCtrlVal(plug_id, val);
+ t->startAutoRecord(plug_id, val);
+ }
+ //}
+// else if(params[param].type == GuiParam::GUI_SWITCH)
+// {
+// float val = (float)((CheckBox*)params[param].actuator)->isChecked();
+// plugin->setParam(param, val);
+//
+// if(t)
+// {
+// t->setPluginCtrlVal(plug_id, val);
+// t->startAutoRecord(plug_id, val);
+// }
+// }
+}
+
+//---------------------------------------------------------
+// guiAutomationEnd
+//---------------------------------------------------------
+
+void VstNativeSynthIF::guiAutomationEnd(unsigned long param_idx)
+{
+ AutomationType at = AUTO_OFF;
+ MusECore::AudioTrack* t = track();
+ if(t)
+ at = t->automationType();
+
+ // FIXME TODO: This stuff should probably be sent as control FIFO events.
+ // And probably not safe - accessing from gui and audio threads...
+
+ // Special for switch - don't enable controller until transport stopped.
+ if ((at == AUTO_OFF) ||
+ (at == AUTO_READ) ||
+ (at == AUTO_TOUCH)) // && (params[param].type != GuiParam::GUI_SWITCH || // FIXME TODO
+ // !MusEGlobal::audio->isPlaying()) ) )
+ enableController(param_idx, true);
+
+ int plug_id = id();
+ if(!t || plug_id == -1)
+ return;
+ plug_id = MusECore::genACnum(plug_id, param_idx);
+
+ //if(params[param].type == GuiParam::GUI_SLIDER)
+ //{
+ //double val = ((Slider*)params[param].actuator)->value();
+ float val = param(param_idx);
+ // FIXME TODO:
+ //if (LADSPA_IS_HINT_LOGARITHMIC(params[param].hint))
+ // val = pow(10.0, val/20.0);
+ //else if (LADSPA_IS_HINT_INTEGER(params[param].hint))
+ // val = rint(val);
+ t->stopAutoRecord(plug_id, val);
+ //}
+}
+
+//---------------------------------------------------------
+// guiControlEventHandler
+//---------------------------------------------------------
+
+int VstNativeSynthIF::guiControlChanged(unsigned long param_idx, float value)
+{
+ #ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "VstNativeSynthIF::guiControlChanged received oscControl port:%lu val:%f\n", port, value);
+ #endif
+
+ if(param_idx >= _synth->inControls())
+ {
+ fprintf(stderr, "VstNativeSynthIF::guiControlChanged: port number:%lu is out of range of index list size:%lu\n", param_idx, _synth->inControls());
+ return 0;
+ }
+
+ ControlEvent ce;
+ ce.unique = false; // Not used for native vst.
+ ce.fromGui = true; // It came form the plugin's own GUI.
+ ce.idx = param_idx;
+ ce.value = value;
+ // don't use timestamp(), because it's circular, which is making it impossible to deal
+ // with 'modulo' events which slip in 'under the wire' before processing the ring buffers.
+ ce.frame = MusEGlobal::audio->curFrame();
+
+ if(_controlFifo.put(ce))
+ {
+ fprintf(stderr, "VstNativeSynthIF::guiControlChanged: fifo overflow: in control number:%lu\n", param_idx);
+ }
+
+ // FIXME TODO: This stuff should probably be sent as control FIFO events.
+ // And probably not safe - accessing from gui and audio threads...
+
+ // Record automation:
+ // Take care of this immediately rather than in the fifo processing.
+ if(id() != -1)
+ {
+ unsigned long pid = genACnum(id(), param_idx);
+ AutomationType at = synti->automationType();
+
+ if ((at == AUTO_WRITE) ||
+ (at == AUTO_TOUCH && MusEGlobal::audio->isPlaying()))
+ enableController(param_idx, false);
+
+ synti->recordAutomation(pid, value);
+ }
+
+ return 0;
+}
+
+//---------------------------------------------------------
+// write
+//---------------------------------------------------------
+
+void VstNativeSynthIF::write(int level, Xml& xml) const
+{
+#ifdef VST_SDK_SUPPORT
+ if(_synth->hasChunks())
+ {
+ //---------------------------------------------
+ // dump current state of synth
+ //---------------------------------------------
+ fprintf(stderr, "%s: commencing chunk data dump, plugin api version=%d\n", name().toLatin1().constData(), _synth->vstVersion());
+ unsigned long len = 0;
+ void* p = 0;
+ len = dispatch(effGetChunk, 0, 0, &p, 0.0); // index 0: is bank 1: is program
+ if (len)
+ {
+ xml.tag(level++, "midistate version=\"%d\"", SYNTH_MIDI_STATE_SAVE_VERSION);
+ xml.nput(level++, "<event type=\"%d\"", Sysex);
+ // 10 = 2 bytes header + "VSTSAVE" + 1 byte flags (compression etc)
+ xml.nput(" datalen=\"%d\">\n", len+10);
+ xml.nput(level, "");
+ xml.nput("%02x %02x ", (char)MUSE_SYNTH_SYSEX_MFG_ID, (char)VST_NATIVE_SYNTH_UNIQUE_ID); // Wrap in a proper header
+ xml.nput("56 53 54 53 41 56 45 "); // embed a save marker "string 'VSTSAVE'
+ xml.nput("%02x ", (char)0); // No flags yet, only uncompressed supported for now. TODO
+ for (unsigned long int i = 0; i < len; ++i)
+ {
+ if (i && (((i+10) % 16) == 0))
+ {
+ xml.nput("\n");
+ xml.nput(level, "");
+ }
+ xml.nput("%02x ", ((char*)(p))[i] & 0xff);
+ }
+ xml.nput("\n");
+ xml.tag(level--, "/event");
+ xml.etag(level--, "midistate");
+ }
+ }
+#else
+ fprintf(stderr, "support for vst chunks not compiled in!\n");
+#endif
+
+ //---------------------------------------------
+ // dump current state of synth
+ //---------------------------------------------
+
+ int params = _plugin->numParams;
+ for (int i = 0; i < params; ++i)
+ {
+ float f = _plugin->getParameter(_plugin, i);
+ xml.floatTag(level, "param", f);
+ }
+}
+
+//---------------------------------------------------------
+// getData
+//---------------------------------------------------------
+
+void VstNativeSynthIF::setVstEvent(VstMidiEvent* event, int a, int b, int c, int d)
+{
+ event->type = kVstMidiType;
+ event->byteSize = 24;
+ event->deltaFrames = 0;
+ event->flags = 0;
+ event->detune = 0;
+ event->noteLength = 0;
+ event->noteOffset = 0;
+ event->reserved1 = 0;
+ event->reserved2 = 0;
+ event->noteOffVelocity = 0;
+ event->midiData[0] = a;
+ event->midiData[1] = b;
+ event->midiData[2] = c;
+ event->midiData[3] = d;
+}
+
+//---------------------------------------------------------
+// getData
+//---------------------------------------------------------
+
+bool VstNativeSynthIF::processEvent(const MusECore::MidiPlayEvent& e, VstMidiEvent* event)
+{
+ int type = e.type();
+ int chn = e.channel();
+ int a = e.dataA();
+ int b = e.dataB();
+
+ int len = e.len();
+ char ca[len + 2];
+
+ ca[0] = 0xF0;
+ memcpy(ca + 1, (const char*)e.data(), len);
+ ca[len + 1] = 0xF7;
+
+ len += 2;
+
+ #ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "VstNativeSynthIF::processEvent midi event type:%d chn:%d a:%d b:%d\n", type, chn, a, b);
+ #endif
+
+ switch(type)
+ {
+ case MusECore::ME_NOTEON:
+ #ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "VstNativeSynthIF::processEvent midi event is MusECore::ME_NOTEON\n");
+ #endif
+ setVstEvent(event, (type | chn) & 0xff, a & 0x7f, b & 0x7f);
+ break;
+ case MusECore::ME_NOTEOFF:
+ #ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "VstNativeSynthIF::processEvent midi event is MusECore::ME_NOTEOFF\n");
+ #endif
+ setVstEvent(event, (type | chn) & 0xff, a & 0x7f, 0);
+ break;
+ case MusECore::ME_PROGRAM:
+ {
+ #ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "VstNativeSynthIF::processEvent midi event is MusECore::ME_PROGRAM\n");
+ #endif
+
+ int bankH = (a >> 16) & 0xff;
+ int bankL = (a >> 8) & 0xff;
+ int prog = a & 0xff;
+ synti->_curBankH = bankH;
+ synti->_curBankL = bankL;
+ synti->_curProgram = prog;
+ doSelectProgram(bankH, bankL, prog);
+ return false; // Event pointer not filled. Return false.
+ }
+ break;
+ case MusECore::ME_CONTROLLER:
+ {
+ #ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "VstNativeSynthIF::processEvent midi event is MusECore::ME_CONTROLLER\n");
+ #endif
+
+ if((a == 0) || (a == 32))
+ return false;
+
+ if(a == MusECore::CTRL_PROGRAM)
+ {
+ #ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "VstNativeSynthIF::processEvent midi event is MusECore::ME_CONTROLLER, dataA is MusECore::CTRL_PROGRAM\n");
+ #endif
+
+ int bankH = (b >> 16) & 0xff;
+ int bankL = (b >> 8) & 0xff;
+ int prog = b & 0xff;
+
+ synti->_curBankH = bankH;
+ synti->_curBankL = bankL;
+ synti->_curProgram = prog;
+ doSelectProgram(bankH, bankL, prog);
+ return false; // Event pointer not filled. Return false.
+ }
+
+ if(a == MusECore::CTRL_PITCH)
+ {
+ #ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "VstNativeSynthIF::processEvent midi event is MusECore::ME_CONTROLLER, dataA is MusECore::CTRL_PITCH\n");
+ #endif
+ int v = b + 8192;
+ setVstEvent(event, (type | chn) & 0xff, v & 0x7f, (v >> 7) & 0x7f);
+ return true;
+ }
+
+ if(a == MusECore::CTRL_AFTERTOUCH)
+ {
+ #ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "VstNativeSynthIF::processEvent midi event is MusECore::ME_CONTROLLER, dataA is MusECore::CTRL_AFTERTOUCH\n");
+ #endif
+ setVstEvent(event, (type | chn) & 0xff, b & 0x7f);
+ return true;
+ }
+
+ if((a | 0xff) == MusECore::CTRL_POLYAFTER)
+ {
+ #ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "VstNativeSynthIF::processEvent midi event is MusECore::ME_CONTROLLER, dataA is MusECore::CTRL_POLYAFTER\n");
+ #endif
+ setVstEvent(event, (type | chn) & 0xff, a & 0x7f, b & 0x7f);
+ return true;
+ }
+
+ #ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "VstNativeSynthIF::processEvent midi event is MusECore::ME_CONTROLLER, dataA is:%d\n", a);
+ #endif
+
+ // Regular controller. Pass it on.
+ setVstEvent(event, (type | chn) & 0xff, a & 0x7f, b & 0x7f);
+
+ return true;
+
+// REMOVE Tim. Or keep. TODO For native vsts? Or not...
+//
+// const LADSPA_Descriptor* ld = dssi->LADSPA_Plugin;
+//
+// MusECore::ciMidiCtl2LadspaPort ip = synth->midiCtl2PortMap.find(a);
+// // Is it just a regular midi controller, not mapped to a LADSPA port (either by the plugin or by us)?
+// // NOTE: There's no way to tell which of these controllers is supported by the plugin.
+// // For example sustain footpedal or pitch bend may be supported, but not mapped to any LADSPA port.
+// if(ip == synth->midiCtl2PortMap.end())
+// {
+// int ctlnum = a;
+// if(MusECore::midiControllerType(a) != MusECore::MidiController::Controller7)
+// return false; // Event pointer not filled. Return false.
+// else
+// {
+// #ifdef VST_NATIVE_DEBUG
+// fprintf(stderr, "VstNativeSynthIF::processEvent non-ladspa midi event is Controller7. Current dataA:%d\n", a);
+// #endif
+// a &= 0x7f;
+// ctlnum = DSSI_CC_NUMBER(ctlnum);
+// }
+//
+// // Fill the event.
+// #ifdef VST_NATIVE_DEBUG
+// fprintf(stderr, "VstNativeSynthIF::processEvent non-ladspa filling midi event chn:%d dataA:%d dataB:%d\n", chn, a, b);
+// #endif
+// snd_seq_ev_clear(event);
+// event->queue = SND_SEQ_QUEUE_DIRECT;
+// snd_seq_ev_set_controller(event, chn, a, b);
+// return true;
+// }
+//
+// unsigned long k = ip->second;
+// unsigned long i = controls[k].idx;
+// int ctlnum = DSSI_NONE;
+// if(dssi->get_midi_controller_for_port)
+// ctlnum = dssi->get_midi_controller_for_port(handle, i);
+//
+// // No midi controller for the ladspa port? Send to ladspa control.
+// if(ctlnum == DSSI_NONE)
+// {
+// // Sanity check.
+// if(k > synth->_controlInPorts)
+// return false;
+//
+// // Simple but flawed solution: Start them at 0x60000 + 0x2000 = 0x62000. Max NRPN number is 0x3fff.
+// ctlnum = k + (MusECore::CTRL_NRPN14_OFFSET + 0x2000);
+// }
+// else
+// {
+// #ifdef VST_NATIVE_DEBUG
+// fprintf(stderr, "VstNativeSynthIF::processEvent plugin requests DSSI-style ctlnum:%x(h) %d(d) be mapped to control port:%lu...\n", ctlnum, ctlnum, i);
+// #endif
+//
+// int c = ctlnum;
+// // Can be both CC and NRPN! Prefer CC over NRPN.
+// if(DSSI_IS_CC(ctlnum))
+// {
+// ctlnum = DSSI_CC_NUMBER(c);
+//
+// #ifdef VST_NATIVE_DEBUG
+// fprintf(stderr, "VstNativeSynthIF::processEvent is CC ctlnum:%d\n", ctlnum);
+// #endif
+//
+// #ifdef VST_NATIVE_DEBUG
+// if(DSSI_IS_NRPN(ctlnum))
+// fprintf(stderr, "VstNativeSynthIF::processEvent is also NRPN control. Using CC.\n");
+// #endif
+// }
+// else
+// if(DSSI_IS_NRPN(ctlnum))
+// {
+// ctlnum = DSSI_NRPN_NUMBER(c) + MusECore::CTRL_NRPN14_OFFSET;
+//
+// #ifdef VST_NATIVE_DEBUG
+// fprintf(stderr, "VstNativeSynthIF::processEvent is NRPN ctlnum:%x(h) %d(d)\n", ctlnum, ctlnum);
+// #endif
+// }
+//
+// }
+//
+// float val = midi2LadspaValue(ld, i, ctlnum, b);
+//
+// #ifdef VST_NATIVE_DEBUG
+// fprintf(stderr, "VstNativeSynthIF::processEvent control port:%lu port:%lu dataA:%d Converting val from:%d to ladspa:%f\n", i, k, a, b, val);
+// #endif
+//
+// // Set the ladspa port value.
+// controls[k].val = val;
+//
+// // Need to update the automation value, otherwise it overwrites later with the last automation value.
+// if(id() != -1)
+// // We're in the audio thread context: no need to send a message, just modify directly.
+// synti->setPluginCtrlVal(genACnum(id(), k), val);
+//
+// // Since we absorbed the message as a ladspa control change, return false - the event is not filled.
+// return false;
+ }
+ break;
+ case MusECore::ME_PITCHBEND:
+ {
+ int v = a + 8192;
+ setVstEvent(event, (type | chn) & 0xff, v & 0x7f, (v >> 7) & 0x7f);
+ }
+ break;
+ case MusECore::ME_AFTERTOUCH:
+ setVstEvent(event, (type | chn) & 0xff, a & 0x7f);
+ break;
+ case MusECore::ME_POLYAFTER:
+ setVstEvent(event, (type | chn) & 0xff, a & 0x7f, b & 0x7f);
+ break;
+ case MusECore::ME_SYSEX:
+ {
+ #ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "VstNativeSynthIF::processEvent midi event is MusECore::ME_SYSEX\n");
+ #endif
+
+ const unsigned char* data = e.data();
+ if(e.len() >= 2)
+ {
+ if(data[0] == MUSE_SYNTH_SYSEX_MFG_ID)
+ {
+ if(data[1] == VST_NATIVE_SYNTH_UNIQUE_ID)
+ {
+ //if(e.len() >= 9)
+ if(e.len() >= 10)
+ {
+ if (QString((const char*)(data + 2)).startsWith("VSTSAVE"))
+ {
+ if(_synth->hasChunks())
+ {
+#ifdef VST_SDK_SUPPORT
+ int chunk_flags = data[9];
+ if(chunk_flags & VST_NATIVE_CHUNK_FLAG_COMPRESSED)
+ fprintf(stderr, "chunk flags:%x compressed chunks not supported yet.\n", chunk_flags);
+ else
+ {
+ fprintf(stderr, "%s: loading chunk from sysex!\n", name().toLatin1().constData());
+ // 10 = 2 bytes header + "VSTSAVE" + 1 byte flags (compression etc)
+ len = dispatch(effSetChunk, 0, e.len()-10, (void*)(data+10), 0.0); // index 0: is bank 1: is program
+ }
+#else
+ fprintf(stderr, "support for vst chunks not compiled in!\n");
+#endif
+ }
+ // Event not filled.
+ return false;
+ }
+ }
+ }
+ }
+ }
+
+ // DELETETHIS, 50 clean it up or fix it?
+ /*
+ // p3.3.39 Read the state of current bank and program and all input control values.
+ // TODO: Needs to be better. See write().
+ //else
+ if (QString((const char*)e.data()).startsWith("PARAMSAVE"))
+ {
+ #ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "VstNativeSynthIF::processEvent midi event is MusECore::ME_SYSEX PARAMSAVE\n");
+ #endif
+
+ unsigned long dlen = e.len() - 9; // Minus "PARAMSAVE"
+ if(dlen > 0)
+ {
+ //if(dlen < 2 * sizeof(unsigned long))
+ if(dlen < (2 + 2 * sizeof(unsigned long))) // Version major and minor bytes, bank and program.
+ fprintf(stderr, "VstNativeSynthIF::processEvent Error: PARAMSAVE data length does not include at least version major and minor, bank and program!\n");
+ else
+ {
+ // Not required, yet.
+ //char vmaj = *((char*)(e.data() + 9)); // After "PARAMSAVE"
+ //char vmin = *((char*)(e.data() + 10));
+
+ unsigned long* const ulp = (unsigned long*)(e.data() + 11); // After "PARAMSAVE" + version major and minor.
+ // TODO: TODO: Set plugin bank and program.
+ _curBank = ulp[0];
+ _curProgram = ulp[1];
+
+ dlen -= (2 + 2 * sizeof(unsigned long)); // After the version major and minor, bank and program.
+
+ if(dlen > 0)
+ {
+ if((dlen % sizeof(float)) != 0)
+ fprintf(stderr, "VstNativeSynthIF::processEvent Error: PARAMSAVE float data length not integral multiple of float size!\n");
+ else
+ {
+ const unsigned long n = dlen / sizeof(float);
+ if(n != synth->_controlInPorts)
+ fprintf(stderr, "VstNativeSynthIF::processEvent Warning: PARAMSAVE number of floats:%lu != number of controls:%lu\n", n, synth->_controlInPorts);
+
+ // Point to location after "PARAMSAVE", version major and minor, bank and progam.
+ float* const fp = (float*)(e.data() + 9 + 2 + 2 * sizeof(unsigned long));
+
+ for(unsigned long i = 0; i < synth->_controlInPorts && i < n; ++i)
+ {
+ const float v = fp[i];
+ controls[i].val = v;
+ }
+ }
+ }
+ }
+ }
+ // Event not filled.
+ return false;
+ }
+ */
+ //else
+ {
+ // FIXME TODO: Sysex support.
+ return false;
+
+ // NOTE: There is a limit on the size of a sysex. Got this:
+ // "VstNativeSynthIF::processEvent midi event is MusECore::ME_SYSEX"
+ // "WARNING: MIDI event of type ? decoded to 367 bytes, discarding"
+ // That might be ALSA doing that.
+// snd_seq_ev_clear(event);
+// event->queue = SND_SEQ_QUEUE_DIRECT;
+// snd_seq_ev_set_sysex(event, len,
+// (unsigned char*)ca);
+ }
+ }
+ break;
+ default:
+ if(MusEGlobal::debugMsg)
+ fprintf(stderr, "VstNativeSynthIF::processEvent midi event unknown type:%d\n", e.type());
+ // Event not filled.
+ return false;
+ break;
+ }
+
+ return true;
+}
+
+//---------------------------------------------------------
+// getData
+//---------------------------------------------------------
+
+iMPEvent VstNativeSynthIF::getData(MidiPort* /*mp*/, MPEventList* el, iMPEvent start_event, unsigned pos, int ports, unsigned nframes, float** buffer)
+{
+ // We may not be using nevents all at once - this will be just the maximum.
+ unsigned long nevents = el->size() + synti->eventFifo.getSize();
+ VstMidiEvent events[nevents];
+ char evbuf[sizeof(VstMidiEvent*) * nevents + sizeof(VstEvents)];
+ VstEvents *vst_events = (VstEvents*)evbuf;
+ vst_events->numEvents = 0;
+ vst_events->reserved = 0;
+
+ int frameOffset = MusEGlobal::audio->getFrameOffset();
+ unsigned long syncFrame = MusEGlobal::audio->curSyncFrame();
+
+ #ifdef VST_NATIVE_DEBUG_PROCESS
+ fprintf(stderr, "VstNativeSynthIF::getData: pos:%u ports:%d nframes:%u syncFrame:%lu nevents:%lu\n", pos, ports, nframes, syncFrame, nevents);
+ #endif
+
+ unsigned long nop, k;
+ nop = ((unsigned long) ports) > _synth->outPorts() ? _synth->outPorts() : ((unsigned long) ports);
+ unsigned long sample = 0;
+
+ // I read that some plugins do not like changing sample run length (compressors etc). Some are OK with it.
+ // TODO: In order to support this effectively, must be user selectable, per-plugin. ENABLED for now.
+ const bool usefixedrate = false;
+ unsigned long fixedsize = nframes;
+
+ // For now, the fixed size is clamped to the MusEGlobal::audio buffer size.
+ // TODO: We could later add slower processing over several cycles -
+ // so that users can select a small MusEGlobal::audio period but a larger control period.
+ if(fixedsize > nframes)
+ fixedsize = nframes;
+
+ unsigned long min_per = MusEGlobal::config.minControlProcessPeriod; // Must be power of 2 !
+ if(min_per > nframes)
+ min_per = nframes;
+
+ // Inform the host callback we are in the audio thread.
+ _inProcess = true;
+
+ #ifdef VST_NATIVE_DEBUG_PROCESS
+ fprintf(stderr, "VstNativeSynthIF::getData: Handling inputs...\n");
+ #endif
+
+ // Handle inputs...
+ if(!((MusECore::AudioTrack*)synti)->noInRoute())
+ {
+ RouteList* irl = ((MusECore::AudioTrack*)synti)->inRoutes();
+ iRoute i = irl->begin();
+ if(!i->track->isMidiTrack())
+ {
+ int ch = i->channel == -1 ? 0 : i->channel;
+ int remch = i->remoteChannel == -1 ? 0 : i->remoteChannel;
+ int chs = i->channels == -1 ? 0 : i->channels;
+
+ if((unsigned)ch < _synth->inPorts() && (unsigned)(ch + chs) <= _synth->inPorts())
+ {
+ int h = remch + chs;
+ for(int j = remch; j < h; ++j)
+ _iUsedIdx[j] = true;
+
+ ((MusECore::AudioTrack*)i->track)->copyData(pos, chs, ch, -1, nframes, &_audioInBuffers[remch]);
+ }
+ }
+
+ ++i;
+ for(; i != irl->end(); ++i)
+ {
+ if(i->track->isMidiTrack())
+ continue;
+
+ int ch = i->channel == -1 ? 0 : i->channel;
+ int remch = i->remoteChannel == -1 ? 0 : i->remoteChannel;
+ int chs = i->channels == -1 ? 0 : i->channels;
+
+ if((unsigned)ch < _synth->inPorts() && (unsigned)(ch + chs) <= _synth->inPorts())
+ {
+ bool u1 = _iUsedIdx[remch];
+ if(chs >= 2)
+ {
+ bool u2 = _iUsedIdx[remch + 1];
+ if(u1 && u2)
+ ((MusECore::AudioTrack*)i->track)->addData(pos, chs, ch, -1, nframes, &_audioInBuffers[remch]);
+ else
+ if(!u1 && !u2)
+ ((MusECore::AudioTrack*)i->track)->copyData(pos, chs, ch, -1, nframes, &_audioInBuffers[remch]);
+ else
+ {
+ if(u1)
+ ((MusECore::AudioTrack*)i->track)->addData(pos, 1, ch, 1, nframes, &_audioInBuffers[remch]);
+ else
+ ((MusECore::AudioTrack*)i->track)->copyData(pos, 1, ch, 1, nframes, &_audioInBuffers[remch]);
+
+ if(u2)
+ ((MusECore::AudioTrack*)i->track)->addData(pos, 1, ch + 1, 1, nframes, &_audioInBuffers[remch + 1]);
+ else
+ ((MusECore::AudioTrack*)i->track)->copyData(pos, 1, ch + 1, 1, nframes, &_audioInBuffers[remch + 1]);
+ }
+ }
+ else
+ {
+ if(u1)
+ ((MusECore::AudioTrack*)i->track)->addData(pos, 1, ch, -1, nframes, &_audioInBuffers[remch]);
+ else
+ ((MusECore::AudioTrack*)i->track)->copyData(pos, 1, ch, -1, nframes, &_audioInBuffers[remch]);
+ }
+
+ int h = remch + chs;
+ for(int j = remch; j < h; ++j)
+ _iUsedIdx[j] = true;
+ }
+ }
+ }
+
+ #ifdef VST_NATIVE_DEBUG_PROCESS
+ fprintf(stderr, "VstNativeSynthIF::getData: Processing automation control values...\n");
+ #endif
+
+ while(sample < nframes)
+ {
+ unsigned long nsamp = usefixedrate ? fixedsize : nframes - sample;
+
+ //
+ // Process automation control values, while also determining the maximum acceptable
+ // size of this run. Further processing, from FIFOs for example, can lower the size
+ // from there, but this section determines where the next highest maximum frame
+ // absolutely needs to be for smooth playback of the controller value stream...
+ //
+ if(id() != -1)
+ {
+ unsigned long frame = pos + sample;
+ AutomationType at = AUTO_OFF;
+ at = synti->automationType();
+ bool no_auto = !MusEGlobal::automation || at == AUTO_OFF;
+ AudioTrack* track = (static_cast<AudioTrack*>(synti));
+ int nextFrame;
+ const unsigned long in_ctrls = _synth->inControls();
+ for(unsigned long k = 0; k < in_ctrls; ++k)
+ {
+ //_controls[k].val = track->controller()->value(genACnum(id(), k), frame,
+ // no_auto || !_controls[k].enCtrl || !_controls[k].en2Ctrl,
+ // &nextFrame);
+ if(dispatch(effCanBeAutomated, k, 0, NULL, 0.0f) == 1)
+ _plugin->setParameter(_plugin, k, track->controller()->value(genACnum(id(), k), frame,
+ no_auto || !_controls[k].enCtrl || !_controls[k].en2Ctrl,
+ &nextFrame));
+#ifdef VST_NATIVE_DEBUG
+ else
+ fprintf(stderr, "VstNativeSynthIF::getData %s parameter:%lu cannot be automated\n", name().toLatin1().constData(), k);
+#endif
+
+#ifdef VST_NATIVE_DEBUG_PROCESS
+ fprintf(stderr, "VstNativeSynthIF::getData k:%lu sample:%lu frame:%lu nextFrame:%d nsamp:%lu \n", k, sample, frame, nextFrame, nsamp);
+#endif
+ if(MusEGlobal::audio->isPlaying() && !usefixedrate && nextFrame != -1)
+ {
+ // Returned value of nextFrame can be zero meaning caller replaces with some (constant) value.
+ unsigned long samps = (unsigned long)nextFrame;
+ if(samps > frame + min_per)
+ {
+ unsigned long diff = samps - frame;
+ unsigned long mask = min_per-1; // min_per must be power of 2
+ samps = diff & ~mask;
+ if((diff & mask) != 0)
+ samps += min_per;
+ }
+ else
+ samps = min_per;
+
+ if(samps < nsamp)
+ nsamp = samps;
+ }
+ }
+#ifdef VST_NATIVE_DEBUG_PROCESS
+ fprintf(stderr, "VstNativeSynthIF::getData sample:%lu nsamp:%lu\n", sample, nsamp);
+#endif
+ }
+
+ bool found = false;
+ unsigned long frame = 0;
+ unsigned long index = 0;
+ unsigned long evframe;
+ // Get all control ring buffer items valid for this time period...
+ while(!_controlFifo.isEmpty())
+ {
+ ControlEvent v = _controlFifo.peek();
+ // The events happened in the last period or even before that. Shift into this period with + n. This will sync with audio.
+ // If the events happened even before current frame - n, make sure they are counted immediately as zero-frame.
+ evframe = (syncFrame > v.frame + nframes) ? 0 : v.frame - syncFrame + nframes;
+
+ #ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "VstNativeSynthIF::getData found:%d evframe:%lu frame:%lu event frame:%lu idx:%lu val:%f unique:%d\n",
+ found, evframe, frame, v.frame, v.idx, v.value, v.unique);
+ #endif
+
+ // Protection. Observed this condition. Why? Supposed to be linear timestamps.
+ if(found && evframe < frame)
+ {
+ fprintf(stderr, "VstNativeSynthIF::getData *** Error: evframe:%lu < frame:%lu event: frame:%lu idx:%lu val:%f unique:%d\n",
+ evframe, frame, v.frame, v.idx, v.value, v.unique);
+
+ // No choice but to ignore it.
+ _controlFifo.remove(); // Done with the ring buffer's item. Remove it.
+ continue;
+ }
+
+ if(evframe >= nframes // Next events are for a later period.
+ || (!usefixedrate && !found && !v.unique && (evframe - sample >= nsamp)) // Next events are for a later run in this period. (Autom took prio.)
+ || (found && !v.unique && (evframe - sample >= min_per)) // Eat up events within minimum slice - they're too close.
+ || (usefixedrate && found && v.unique && v.idx == index)) // Special for dssi-vst: Fixed rate and must reply to all.
+ break;
+ _controlFifo.remove(); // Done with the ring buffer's item. Remove it.
+
+ if(v.idx >= _synth->inControls()) // Sanity check.
+ break;
+ found = true;
+ frame = evframe;
+ index = v.idx;
+ // Set the ladspa control port value.
+ //controls[v.idx].val = v.value;
+ if(dispatch(effCanBeAutomated, v.idx, 0, NULL, 0.0f) == 1)
+ _plugin->setParameter(_plugin, v.idx, v.value);
+#ifdef VST_NATIVE_DEBUG
+ else
+ fprintf(stderr, "VstNativeSynthIF::getData %s parameter:%lu cannot be automated\n", name().toLatin1().constData(), v.idx);
+#endif
+
+ // Need to update the automation value, otherwise it overwrites later with the last automation value.
+ if(id() != -1)
+ synti->setPluginCtrlVal(genACnum(id(), v.idx), v.value);
+ }
+
+ if(found && !usefixedrate) // If a control FIFO item was found, takes priority over automation controller stream.
+ nsamp = frame - sample;
+
+ if(sample + nsamp >= nframes) // Safety check.
+ nsamp = nframes - sample;
+
+ // TODO: Don't allow zero-length runs. This could/should be checked in the control loop instead.
+ // Note this means it is still possible to get stuck in the top loop (at least for a while).
+ if(nsamp == 0)
+ continue;
+
+ nevents = 0;
+ // Process event list events...
+ for(; start_event != el->end(); ++start_event)
+ {
+ #ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "VstNativeSynthIF::getData eventlist event time:%d pos:%u sample:%lu nsamp:%lu frameOffset:%d\n", start_event->time(), pos, sample, nsamp, frameOffset);
+ #endif
+
+ if(start_event->time() >= (pos + sample + nsamp + frameOffset)) // frameOffset? Test again...
+ {
+ #ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, " event is for future:%lu, breaking loop now\n", start_event->time() - frameOffset - pos - sample);
+ #endif
+ break;
+ }
+
+ // Update hardware state so knobs and boxes are updated. Optimize to avoid re-setting existing values.
+ // Same code as in MidiPort::sendEvent()
+ if(synti->midiPort() != -1)
+ {
+ MusECore::MidiPort* mp = &MusEGlobal::midiPorts[synti->midiPort()];
+ if(start_event->type() == MusECore::ME_CONTROLLER)
+ {
+ int da = start_event->dataA();
+ int db = start_event->dataB();
+ db = mp->limitValToInstrCtlRange(da, db);
+ if(!mp->setHwCtrlState(start_event->channel(), da, db))
+ continue;
+ }
+ else if(start_event->type() == MusECore::ME_PITCHBEND)
+ {
+ int da = mp->limitValToInstrCtlRange(MusECore::CTRL_PITCH, start_event->dataA());
+ if(!mp->setHwCtrlState(start_event->channel(), MusECore::CTRL_PITCH, da))
+ continue;
+ }
+ else if(start_event->type() == MusECore::ME_AFTERTOUCH)
+ {
+ int da = mp->limitValToInstrCtlRange(MusECore::CTRL_AFTERTOUCH, start_event->dataA());
+ if(!mp->setHwCtrlState(start_event->channel(), MusECore::CTRL_AFTERTOUCH, da))
+ continue;
+ }
+ else if(start_event->type() == MusECore::ME_POLYAFTER)
+ {
+ int ctl = (MusECore::CTRL_POLYAFTER & ~0xff) | (start_event->dataA() & 0x7f);
+ int db = mp->limitValToInstrCtlRange(ctl, start_event->dataB());
+ if(!mp->setHwCtrlState(start_event->channel(), ctl , db))
+ continue;
+ }
+ else if(start_event->type() == MusECore::ME_PROGRAM)
+ {
+ if(!mp->setHwCtrlState(start_event->channel(), MusECore::CTRL_PROGRAM, start_event->dataA()))
+ continue;
+ }
+ }
+
+ // Returns false if the event was not filled. It was handled, but some other way.
+ if(processEvent(*start_event, &events[nevents]))
+ {
+ // Time-stamp the event.
+ int ft = start_event->time() - frameOffset - pos - sample;
+ if(ft < 0)
+ ft = 0;
+
+ if (ft >= int(nsamp))
+ {
+ fprintf(stderr, "VstNativeSynthIF::getData: eventlist event time:%d out of range. pos:%d offset:%d ft:%d sample:%lu nsamp:%lu\n", start_event->time(), pos, frameOffset, ft, sample, nsamp);
+ ft = nsamp - 1;
+ }
+
+ #ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "VstNativeSynthIF::getData eventlist: ft:%d current nevents:%lu\n", ft, nevents);
+ #endif
+
+ vst_events->events[nevents] = (VstEvent*)&events[nevents];
+ events[nevents].deltaFrames = ft;
+ ++nevents;
+ }
+ }
+
+ // Now process putEvent events...
+ while(!synti->eventFifo.isEmpty())
+ {
+ MusECore::MidiPlayEvent e = synti->eventFifo.peek();
+
+ #ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "VstNativeSynthIF::getData eventFifo event time:%d\n", e.time());
+ #endif
+
+ if(e.time() >= (pos + sample + nsamp + frameOffset))
+ break;
+
+ synti->eventFifo.remove(); // Done with ring buffer's event. Remove it.
+ // Returns false if the event was not filled. It was handled, but some other way.
+ if(processEvent(e, &events[nevents]))
+ {
+ // Time-stamp the event.
+ int ft = e.time() - frameOffset - pos - sample;
+ if(ft < 0)
+ ft = 0;
+ if (ft >= int(nsamp))
+ {
+ fprintf(stderr, "VstNativeSynthIF::getData: eventFifo event time:%d out of range. pos:%d offset:%d ft:%d sample:%lu nsamp:%lu\n", e.time(), pos, frameOffset, ft, sample, nsamp);
+ ft = nsamp - 1;
+ }
+ vst_events->events[nevents] = (VstEvent*)&events[nevents];
+ events[nevents].deltaFrames = ft;
+
+ ++nevents;
+ }
+ }
+
+ #ifdef VST_NATIVE_DEBUG_PROCESS
+ fprintf(stderr, "VstNativeSynthIF::getData: Connecting and running. sample:%lu nsamp:%lu nevents:%lu\n", sample, nsamp, nevents);
+ #endif
+
+ // Set the events pointer.
+ if(nevents > 0)
+ {
+ vst_events->numEvents = nevents;
+ dispatch(effProcessEvents, 0, 0, vst_events, 0.0f);
+ }
+
+ float* in_bufs[_synth->inPorts()];
+ float* out_bufs[_synth->outPorts()];
+
+ k = 0;
+ // Connect the given buffers directly to the ports, up to a max of synth ports.
+ for(; k < nop; ++k)
+ out_bufs[k] = buffer[k] + sample;
+ // Connect the remaining ports to some local buffers (not used yet).
+ const unsigned long op =_synth->outPorts();
+ for(; k < op; ++k)
+ out_bufs[k] = _audioOutBuffers[k] + sample;
+ // Connect all inputs either to some local buffers, or a silence buffer.
+ const unsigned long ip =_synth->inPorts();
+ for(k = 0; k < ip; ++k)
+ {
+ if(_iUsedIdx[k])
+ {
+ _iUsedIdx[k] = false; // Reset
+ in_bufs[k] = _audioInBuffers[k] + sample;
+ }
+ else
+ in_bufs[k] = _audioInSilenceBuf + sample;
+ }
+
+ // Run the synth for a period of time. This processes events and gets/fills our local buffers...
+ if((_plugin->flags & effFlagsCanReplacing) && _plugin->processReplacing)
+ {
+ _plugin->processReplacing(_plugin, in_bufs, out_bufs, nsamp);
+ }
+#if 0 //ifdef VST_NATIVE_FORCE_DEPRECATED
+ else
+ {
+ // TODO: This is not right, we don't accumulate data here. Depricated now, and frowned upon anyway...
+ if(_plugin->process)
+ _plugin->process(_plugin, in_bufs, out_bufs, nsamp);
+ }
+#endif
+
+ sample += nsamp;
+ }
+
+ // Inform the host callback we will be no longer in the audio thread.
+ _inProcess = true;
+
+ return start_event;
+}
+
+//---------------------------------------------------------
+// putEvent
+//---------------------------------------------------------
+
+bool VstNativeSynthIF::putEvent(const MidiPlayEvent& ev)
+ {
+ #ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "VstNativeSynthIF::putEvent midi event time:%d chn:%d a:%d b:%d\n", ev.time(), ev.channel(), ev.dataA(), ev.dataB());
+ #endif
+
+ if (MusEGlobal::midiOutputTrace)
+ ev.dump();
+ return synti->eventFifo.put(ev);
+ }
+
+
+//--------------------------------
+// Methods for PluginIBase:
+//--------------------------------
+
+unsigned long VstNativeSynthIF::pluginID() { return (_plugin) ? _plugin->uniqueID : 0; }
+int VstNativeSynthIF::id() { return MAX_PLUGINS; } // Set for special block reserved for synth.
+QString VstNativeSynthIF::pluginLabel() const { return _synth ? QString(_synth->name()) : QString(); } // FIXME Maybe wrong
+QString VstNativeSynthIF::lib() const { return _synth ? _synth->completeBaseName() : QString(); }
+QString VstNativeSynthIF::dirPath() const { return _synth ? _synth->absolutePath() : QString(); }
+QString VstNativeSynthIF::fileName() const { return _synth ? _synth->fileName() : QString(); }
+void VstNativeSynthIF::enableController(unsigned long i, bool v) { _controls[i].enCtrl = v; }
+bool VstNativeSynthIF::controllerEnabled(unsigned long i) const { return _controls[i].enCtrl;}
+void VstNativeSynthIF::enable2Controller(unsigned long i, bool v) { _controls[i].en2Ctrl = v; }
+bool VstNativeSynthIF::controllerEnabled2(unsigned long i) const { return _controls[i].en2Ctrl; }
+void VstNativeSynthIF::enableAllControllers(bool v)
+{
+ if(!_synth)
+ return;
+ for(unsigned long i = 0; i < _synth->inControls(); ++i)
+ _controls[i].enCtrl = v;
+}
+void VstNativeSynthIF::enable2AllControllers(bool v)
+{
+ if(!_synth)
+ return;
+ for(unsigned long i = 0; i < _synth->inControls(); ++i)
+ _controls[i].en2Ctrl = v;
+}
+void VstNativeSynthIF::updateControllers() { }
+void VstNativeSynthIF::activate()
+{
+ //for (unsigned short i = 0; i < instances(); ++i) {
+ // dispatch(i, effMainsChanged, 0, 1, NULL, 0.0f);
+ dispatch(effMainsChanged, 0, 1, NULL, 0.0f);
+//#ifdef VST_SDK_SUPPORT
+ //dispatch(i, effStartProcess, 0, 0, NULL, 0.0f);
+ dispatch(effStartProcess, 0, 0, NULL, 0.0f);
+//#endif
+ //}
+
+// REMOVE Tim. Or keep? From PluginI::activate().
+// if (initControlValues) {
+// for (unsigned long i = 0; i < controlPorts; ++i) {
+// controls[i].val = controls[i].tmpVal;
+// }
+// }
+// else {
+// // get initial control values from plugin
+// for (unsigned long i = 0; i < controlPorts; ++i) {
+// controls[i].tmpVal = controls[i].val;
+// }
+// }
+}
+void VstNativeSynthIF::deactivate()
+{
+ //for (unsigned short i = 0; i < instances(); ++i) {
+//#ifdef VST_SDK_SUPPORT
+ //dispatch(i, effStopProcess, 0, 0, NULL, 0.0f);
+ dispatch(effStopProcess, 0, 0, NULL, 0.0f);
+//#endif
+ //dispatch(i, effMainsChanged, 0, 0, NULL, 0.0f);
+ dispatch(effMainsChanged, 0, 0, NULL, 0.0f);
+ //}
+}
+
+unsigned long VstNativeSynthIF::parameters() const { return _synth ? _synth->inControls() : 0; }
+unsigned long VstNativeSynthIF::parametersOut() const { return 0; }
+void VstNativeSynthIF::setParam(unsigned long i, float val) { setParameter(i, val); }
+float VstNativeSynthIF::param(unsigned long i) const { return getParameter(i); }
+float VstNativeSynthIF::paramOut(unsigned long) const { return 0.0; }
+const char* VstNativeSynthIF::paramName(unsigned long i)
+{
+ if(!_plugin)
+ return 0;
+ static char buf[256];
+ buf[0] = 0;
+ dispatch(effGetParamName, i, 0, buf, 0);
+ return buf;
+}
+
+const char* VstNativeSynthIF::paramOutName(unsigned long) { return 0; }
+LADSPA_PortRangeHint VstNativeSynthIF::range(unsigned long /*i*/)
+{
+ LADSPA_PortRangeHint h;
+ // FIXME TODO:
+ h.HintDescriptor = 0;
+ h.LowerBound = 0.0;
+ h.UpperBound = 1.0;
+ return h;
+}
+LADSPA_PortRangeHint VstNativeSynthIF::rangeOut(unsigned long)
+{
+ // There are no output controls.
+ LADSPA_PortRangeHint h;
+ h.HintDescriptor = 0;
+ h.LowerBound = 0.0;
+ h.UpperBound = 1.0;
+ return h;
+}
+// FIXME TODO:
+CtrlValueType VstNativeSynthIF::ctrlValueType(unsigned long /*i*/) const { return VAL_LINEAR; }
+CtrlList::Mode VstNativeSynthIF::ctrlMode(unsigned long /*i*/) const { return CtrlList::INTERPOLATE; };
+
+} // namespace MusECore
+
+#else // VST_NATIVE_SUPPORT
+namespace MusECore {
+void initVST_Native() {}
+} // namespace MusECore
+#endif
+