diff options
Diffstat (limited to 'attic/muse2-oom/muse2/muse/dssihost.cpp')
-rw-r--r-- | attic/muse2-oom/muse2/muse/dssihost.cpp | 3059 |
1 files changed, 3059 insertions, 0 deletions
diff --git a/attic/muse2-oom/muse2/muse/dssihost.cpp b/attic/muse2-oom/muse2/muse/dssihost.cpp new file mode 100644 index 00000000..986abea1 --- /dev/null +++ b/attic/muse2-oom/muse2/muse/dssihost.cpp @@ -0,0 +1,3059 @@ +//============================================================================= +// MusE +// Linux Music Editor +// $Id: dssihost.cpp,v 1.15.2.16 2009/12/15 03:39:58 terminator356 Exp $ +// +// Copyright (C) 2002-2006 by Werner Schweer and others +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2. +// +// 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., 675 Mass Ave, Cambridge, MA 02139, USA. +//============================================================================= + +#include "config.h" +#ifdef DSSI_SUPPORT + +// Turn on debugging messages +//#define DSSI_DEBUG + +// Support vst state saving/loading with vst chunks. Requires patches to DSSI and DSSI-vst! +//#define DSSI_VST_CHUNK_SUPPORT + +#include <string.h> +#include <signal.h> +#include <dlfcn.h> +#include <stdlib.h> +#include <sys/stat.h> +//#include <dssi.h> +//#include <alsa/asoundlib.h> + +#include <QDir> +#include <QFileInfo> +#include <QMenu> + +#include "dssihost.h" +#include "synth.h" +#include "audio.h" +#include "jackaudio.h" +//#include "../driver/jackaudio.h" // p4.0.2 +#include "midi.h" +#include "midiport.h" +#include "stringparam.h" +#include "plugin.h" +//#include "al/al.h" +//#include "al/xml.h" +#include "xml.h" +#include "song.h" +//#include "midictrl.h" +//#include "ladspaplugin.h" + +#include "app.h" +#include "globals.h" +#include "globaldefs.h" +//#include "al/dsp.h" +#include "gconfig.h" + +/* +static lo_server_thread serverThread; +static char osc_path_tmp[1024]; +static char* url; + +//--------------------------------------------------------- +// oscError +//--------------------------------------------------------- + +static void oscError(int num, const char *msg, const char *path) + { + fprintf(stderr, "MusE: liblo server error %d in path %s: %s\n", + num, path, msg); + } + +//--------------------------------------------------------- +// oscDebugHandler +//--------------------------------------------------------- + +static int oscDebugHandler(const char* path, const char* types, lo_arg** argv, + int argc, void*, void*) + { + printf("MusE: got unhandled OSC message:\n path: <%s>\n", path); + for (int i = 0; i < argc; i++) { + printf(" arg %d '%c' ", i, types[i]); + lo_arg_pp(lo_type(types[i]), argv[i]); + printf("\n"); + } + return 1; + } + +//--------------------------------------------------------- +// oscUpdate +//--------------------------------------------------------- + +int DssiSynthIF::oscUpdate(lo_arg **argv) + { + const char *url = (char *)&argv[0]->s; + + if (uiTarget) + lo_address_free(uiTarget); + char* host = lo_url_get_hostname(url); + char* port = lo_url_get_port(url); + uiTarget = lo_address_new(host, port); + free(host); + free(port); + + if (uiOscPath) + free(uiOscPath); + uiOscPath = lo_url_get_path(url); + int pl = strlen(uiOscPath); + + if (uiOscControlPath) + free(uiOscControlPath); + uiOscControlPath = (char *)malloc(pl + 10); + sprintf(uiOscControlPath, "%s/control", uiOscPath); + + if (uiOscConfigurePath) + free(uiOscConfigurePath); + uiOscConfigurePath = (char *)malloc(pl + 12); + sprintf(uiOscConfigurePath, "%s/configure", uiOscPath); + + if (uiOscProgramPath) + free(uiOscProgramPath); + uiOscProgramPath = (char *)malloc(pl + 10); + sprintf(uiOscProgramPath, "%s/program", uiOscPath); + + if (uiOscShowPath) + free(uiOscShowPath); + uiOscShowPath = (char *)malloc(pl + 10); + sprintf(uiOscShowPath, "%s/show", uiOscPath); + + // At this point a more substantial host might also call + // configure() on the UI to set any state that it had remembered + // for the plugin instance. But we don't remember state for + // plugin instances (see our own configure() implementation in + // osc_configure_handler), and so we have nothing to send except + // the optional project directory. + + + #ifdef DSSI_DEBUG + printf("DssiSynthIF::oscUpdate synth name:%s url:%s uiTarget:%p uiOscPath:%s uiOscConfigurePath:%s museProject:%s\n", synti->name().ascii(), url, uiTarget, uiOscPath, uiOscConfigurePath, museProject.ascii()); + #endif + + //lo_send(uiTarget, uiOscConfigurePath, "ss", + //DSSI_PROJECT_DIRECTORY_KEY, song->projectPath().toAscii().data()); + lo_send(uiTarget, uiOscConfigurePath, "ss", + DSSI_PROJECT_DIRECTORY_KEY, museProject.ascii()); + +#if 0 + // Send current bank/program (-FIX- another race...) + if (instance->pendingProgramChange < 0) { + unsigned long bank = instance->currentBank; + unsigned long program = instance->currentProgram; + instance->uiNeedsProgramUpdate = 0; + if (instance->uiTarget) { + lo_send(instance->uiTarget, instance->ui_osc_program_path, "ii", bank, program); + } + } + + // Send control ports + for (i = 0; i < instance->plugin->controlIns; i++) { + int in = i + instance->firstControlIn; + int port = pluginControlInPortNumbers[in]; + lo_send(instance->uiTarget, instance->ui_osc_control_path, "if", port, + pluginControlIns[in]); + // Avoid overloading the GUI if there are lots and lots of ports + if ((i+1) % 50 == 0) + usleep(300000); + } +#endif + return 0; + } + +//--------------------------------------------------------- +// oscMessageHandler +//--------------------------------------------------------- + +int oscMessageHandler(const char* path, const char* types, lo_arg** argv, + int argc, void* data, void* user_data) + { + const char* p = path; + + #ifdef DSSI_DEBUG + if(argc) + { + printf("oscMessageHandler: path:%s argc:%d\n", path, argc); + for(int i = 0; i < argc; ++i) + { + printf(" "); + lo_arg_pp((lo_type)types[i], argv[i]); + } + printf("\n"); + } + else + { + printf("%s\n", path); + printf("oscMessageHandler: no args, path:%s\n", path); + } + #endif + + if (strncmp(p, "/dssi/", 6)) + return oscDebugHandler(path, types, argv, argc, data, user_data); + + p += 6; + //p = strrchr(p, "/"); + + SynthIList* sl = song->syntis(); + DssiSynthIF* instance = 0; + SynthI* synti = 0; + + #ifdef DSSI_DEBUG + fprintf(stderr, "oscMessageHandler: song->syntis() size:%d\n", sl->size()); + #endif + + for(int retry = 0; retry < 5; ++retry) + { + #ifdef DSSI_DEBUG + fprintf(stderr, "oscMessageHandler: search retry number:%d ...\n", retry); + #endif + + //if(uiOscPath) + // break; + + for(iSynthI si = sl->begin(); si != sl->end(); ++si) + { + #ifdef DSSI_DEBUG + fprintf(stderr, "oscMessageHandler: searching for synth p:%s: checking instances:%s\n", p, (*si)->name().ascii()); + #endif + + //int l = strlen((*si)->name().toAscii().data()); + //if (!strncmp(p, (*si)->name().toAscii().data(), l)) { + //int l = strlen((*si)->name().ascii()); + const char* sub = strstr(p, (*si)->name().ascii()); + + //if(!strncmp(p, (*si)->name().ascii(), l)) + if(sub != NULL) + { + synti = *si; + instance = (DssiSynthIF*)(synti->sif()); + + //p += l; + p = sub + strlen((*si)->name().ascii()); + + break; + } + } + if(instance) + break; + + sleep(1); + } + + if(!instance) + { + fprintf(stderr, "oscMessageHandler: error: no instance\n"); + return oscDebugHandler(path, types, argv, argc, data, user_data); + } + + if (*p != '/' || *(p + 1) == 0) + { + fprintf(stderr, "oscMessageHandler: error: end or no /\n"); + return oscDebugHandler(path, types, argv, argc, data, user_data); + } + + ++p; + + #ifdef DSSI_DEBUG + fprintf(stderr, "oscMessageHandler: method:%s\n", p); + #endif + + if (!strcmp(p, "configure") && argc == 2 && !strcmp(types, "ss")) + return instance->oscConfigure(argv); + else if (!strcmp(p, "control") && argc == 2 && !strcmp(types, "if")) + return instance->oscControl(argv); + else if (!strcmp(p, "midi") && argc == 1 && !strcmp(types, "m")) + return instance->oscMidi(argv); + else if (!strcmp(p, "program") && argc == 2 && !strcmp(types, "ii")) + return instance->oscProgram(argv); + else if (!strcmp(p, "update") && argc == 1 && !strcmp(types, "s")) + return instance->oscUpdate(argv); + else if (!strcmp(p, "exiting") && argc == 0) + return instance->oscExiting(argv); + return oscDebugHandler(path, types, argv, argc, data, user_data); + } +*/ + +//--------------------------------------------------------- +// scanDSSILib +//--------------------------------------------------------- + +static void scanDSSILib(QFileInfo& fi) // ddskrjo removed const for argument + { + //void* handle = dlopen(fi.filePath().toAscii().data(), RTLD_NOW); + void* handle = dlopen(fi.filePath().toLatin1().constData(), RTLD_NOW); + //void* handle = dlopen(fi.absFilePath().toLatin1().constData(), RTLD_NOW); + + if (handle == 0) { + fprintf(stderr, "scanDSSILib: dlopen(%s) failed: %s\n", + //fi.filePath().toAscii().data(), dlerror()); + fi.filePath().toLatin1().constData(), dlerror()); + //fi.absFilePath().toLatin1().constData(), dlerror()); + + return; + } + DSSI_Descriptor_Function dssi = (DSSI_Descriptor_Function)dlsym(handle, "dssi_descriptor"); + + if (!dssi) + { + /* + const char *txt = dlerror(); + if (txt) + { + fprintf(stderr, + "Unable to find dssi_descriptor() function in plugin " + "library file \"%s\": %s.\n" + "Are you sure this is a DSSI plugin file?\n", + //fi.filePath().toAscii().data(), + fi.filePath().ascii(), + //fi.absFilePath().toLatin1().constData(), + + txt); + dlclose(handle); + exit(1); + } + */ + dlclose(handle); + return; + } + else + { + //const DSSI_Descriptor* descr; + for (int i = 0;; ++i) + { + const DSSI_Descriptor* descr; + + // CRAPPY PLUGIN ALERT: + // Out of many plugins, with several, Valgrind says something in here is allocated with new. + descr = dssi(i); + if (descr == 0) + break; + + #ifdef DSSI_DEBUG + fprintf(stderr, "scanDSSILib: name:%s inPlaceBroken:%d\n", descr->LADSPA_Plugin->Name, LADSPA_IS_INPLACE_BROKEN(descr->LADSPA_Plugin->Properties)); + #endif + + // Listing synths only while excluding effect plugins: + // Do the exact opposite of what dssi-vst.cpp does for listing ladspa plugins. + // That way we cover all bases - effect plugins and synths. + // Non-synths will show up in the ladspa effect dialog, while synths will show up here... + // There should be nothing left out... + if(descr->run_synth || + descr->run_synth_adding || + descr->run_multiple_synths || + descr->run_multiple_synths_adding) + + { + const QString label(descr->LADSPA_Plugin->Label); + + // Make sure it doesn't already exist. + std::vector<Synth*>::iterator is; + for(is = synthis.begin(); is != synthis.end(); ++is) + { + Synth* s = *is; + //#ifdef DSSI_DEBUG + // fprintf(stderr, "scanDSSILib: name:%s listname:%s lib:%s listlib:%s\n", + // label.toLatin1().constData(), s->name().toLatin1().constData(), fi.baseName(true).toLatin1().constData(), s->baseName().toLatin1().constData()); + //#endif + + if(s->name() == label && s->baseName() == fi.completeBaseName()) + break; + } + if(is != synthis.end()) + continue; + + DssiSynth* s = new DssiSynth(fi, descr); + + if(debugMsg) + { + fprintf(stderr, "scanDSSILib: name:%s listname:%s lib:%s listlib:%s\n", + label.toLatin1().constData(), s->name().toLatin1().constData(), fi.completeBaseName().toLatin1().constData(), s->baseName().toLatin1().constData()); + int ai = 0, ao = 0, ci = 0, co = 0; + for(unsigned long pt = 0; pt < descr->LADSPA_Plugin->PortCount; ++pt) + { + LADSPA_PortDescriptor pd = descr->LADSPA_Plugin->PortDescriptors[pt]; + if(LADSPA_IS_PORT_INPUT(pd) && LADSPA_IS_PORT_AUDIO(pd)) + ai++; + else + if(LADSPA_IS_PORT_OUTPUT(pd) && LADSPA_IS_PORT_AUDIO(pd)) + ao++; + else + if(LADSPA_IS_PORT_INPUT(pd) && LADSPA_IS_PORT_CONTROL(pd)) + ci++; + else + if(LADSPA_IS_PORT_OUTPUT(pd) && LADSPA_IS_PORT_CONTROL(pd)) + co++; + } + fprintf(stderr, "audio ins:%d outs:%d control ins:%d outs:%d\n", ai, ao, ci, co); + } + + synthis.push_back(s); + } + else + { + // NOTE: Just a test + //QFileInfo ffi(fi); + //plugins.add(&ffi, LADSPA_Descriptor_Function(NULL), descr->LADSPA_Plugin, false); + //plugins.add(&ffi, descr, false); + } + } + } + dlclose(handle); + } + +//--------------------------------------------------------- +// scanVstDir +//--------------------------------------------------------- + +static void scanDSSIDir(QString& s) // ddskrjo removed const for argument +{ + if(debugMsg) + //printf("scan DSSI plugin dir <%s>\n", s.toAscii().data()); + printf("scanDSSIDir: scan DSSI plugin dir <%s>\n", s.toLatin1().constData()); + +#ifdef __APPLE__ + QDir pluginDir(s, QString("*.dylib"), QDir::Unsorted, QDir::Files); +#else + QDir pluginDir(s, QString("*.so"), QDir::Unsorted, QDir::Files); +#endif + if(!pluginDir.exists()) + return; + + //const QFileInfoList list = pluginDir.entryInfoList(); + //for (int i = 0; i < list.size(); ++i) { + //QFileInfo fi = list.at(i); + //scanDSSILib(fi); + //} + + QStringList list = pluginDir.entryList(); + for(int i = 0; i < list.count(); ++i) + { + if(debugMsg) + printf("scanDSSIDir: found %s\n", (s + QString("/") + list[i]).toLatin1().constData()); + + QFileInfo fi(s + QString("/") + list[i]); + scanDSSILib(fi); + } +} + +//--------------------------------------------------------- +// initDSSI +//--------------------------------------------------------- + +void initDSSI() + { + const char* dssiPath = getenv("DSSI_PATH"); + if (dssiPath == 0) + dssiPath = "/usr/local/lib64/dssi:/usr/lib64/dssi:/usr/local/lib/dssi:/usr/lib/dssi"; + + //const char* ladspaPath = getenv("LADSPA_PATH"); + //if (ladspaPath == 0) + // ladspaPath = "/usr/local/lib64/ladspa:/usr/lib64/ladspa:/usr/local/lib/ladspa:/usr/lib/ladspa"; + + const char* p = dssiPath; + //QString pth = QString(dssiPath) + QString(":") + QString(ladspaPath); + //const char* p = pth.toLatin1().constData(); + 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'; + QString tmpStr(buffer); + scanDSSIDir(tmpStr); + delete[] buffer; + } + p = pe; + if (*p == ':') + p++; + } + + // Create OSC thread + //serverThread = lo_server_thread_new(0, oscError); + //snprintf(osc_path_tmp, 31, "/dssi"); + //char* tmp = lo_server_thread_get_url(serverThread); + //url = (char *)malloc(strlen(tmp) + strlen(osc_path_tmp)); + //sprintf(url, "%s%s", tmp, osc_path_tmp + 1); + //free(tmp); + //lo_server_thread_add_method(serverThread, 0, 0, oscMessageHandler, 0); + //lo_server_thread_start(serverThread); + } + +//--------------------------------------------------------- +// DssiSynth +// Synth.label = plug.Label +// Synth.descr = plug.Name +// Synth.maker = plug.maker +// Synth.version = nil (no such field in ladspa, maybe try copyright instead) +//--------------------------------------------------------- + +DssiSynth::DssiSynth(QFileInfo& fi, const DSSI_Descriptor* d) : // ddskrjo removed const from QFileInfo + //Synth(fi, label, descr, maker, ver) + Synth(fi, QString(d->LADSPA_Plugin->Label), QString(d->LADSPA_Plugin->Name), QString(d->LADSPA_Plugin->Maker), QString()) +{ + df = 0; + handle = 0; + dssi = 0; + _hasGui = false; + + const LADSPA_Descriptor* descr = d->LADSPA_Plugin; + + _portCount = descr->PortCount; + //_portDescriptors = 0; + //if(_portCount) + // _portDescriptors = new LADSPA_PortDescriptor[_portCount]; + + _inports = 0; + _outports = 0; + _controlInPorts = 0; + _controlOutPorts = 0; + for(unsigned long k = 0; k < _portCount; ++k) + { + LADSPA_PortDescriptor pd = descr->PortDescriptors[k]; + //_portDescriptors[k] = pd; + if(pd & LADSPA_PORT_AUDIO) + { + if(pd & LADSPA_PORT_INPUT) + ++_inports; + else + if(pd & LADSPA_PORT_OUTPUT) + ++_outports; + } + else + if(pd & LADSPA_PORT_CONTROL) + { + if(pd & LADSPA_PORT_INPUT) + ++_controlInPorts; + else + if(pd & LADSPA_PORT_OUTPUT) + ++_controlOutPorts; + } + } + + _inPlaceCapable = !LADSPA_IS_INPLACE_BROKEN(descr->Properties); + + // Blacklist vst plugins in-place configurable for now. + if ((_inports != _outports) || (fi.completeBaseName() == QString("dssi-vst") && !config.vstInPlace)) + _inPlaceCapable = false; +} + +DssiSynth::~DssiSynth() +{ + +} + +//--------------------------------------------------------- +// createSIF +//--------------------------------------------------------- + +SynthIF* DssiSynth::createSIF(SynthI* synti) +{ + if (_instances == 0) + { + //handle = dlopen(info.filePath().toAscii().data(), RTLD_NOW); + handle = dlopen(info.filePath().toLatin1().constData(), RTLD_NOW); + //handle = dlopen(info.absFilePath().toLatin1().constData(), RTLD_NOW); + + if (handle == 0) + { + fprintf(stderr, "DssiSynth::createSIF dlopen(%s) failed: %s\n", + //info.filePath().toAscii().data(), dlerror()); + info.filePath().toLatin1().constData(), dlerror()); + //info.absFilePath().toLatin1().constData(), dlerror()); + + return 0; + } + df = (DSSI_Descriptor_Function)dlsym(handle, "dssi_descriptor"); + + if (!df) { + const char *txt = dlerror(); + fprintf(stderr, + "Unable to find dssi_descriptor() function in plugin " + "library file \"%s\": %s.\n" + "Are you sure this is a DSSI plugin file?\n", + //info.filePath().toAscii().data(), + info.filePath().toLatin1().constData(), + //info.absFilePath().toLatin1().constData(), + + txt ? txt : "?"); + dlclose(handle); + handle = 0; + return 0; + } + for (int i = 0;; ++i) + { + dssi = df(i); + if (dssi == 0) + break; + QString label(dssi->LADSPA_Plugin->Label); + if (label == _name) + break; + } + + if(dssi != 0) + { + _inports = 0; + _outports = 0; + _controlInPorts = 0; + _controlOutPorts = 0; + + pIdx.clear(); + opIdx.clear(); + iIdx.clear(); + oIdx.clear(); + rpIdx.clear(); + iUsedIdx.clear(); + midiCtl2PortMap.clear(); + port2MidiCtlMap.clear(); + //synti->_guiUpdateControls.clear(); + + const LADSPA_Descriptor* descr = dssi->LADSPA_Plugin; + //#ifdef DSSI_DEBUG + // printf("DssiSynth::createSIF ladspa plugin PortCount:%lu\n", d->PortCount); + //#endif + + _portCount = descr->PortCount; + + for (unsigned long k = 0; k < _portCount; ++k) + { + LADSPA_PortDescriptor pd = descr->PortDescriptors[k]; + + #ifdef DSSI_DEBUG + printf("DssiSynth::createSIF ladspa plugin Port:%ld Name:%s descriptor:%x\n", k, descr->PortNames[k], pd); + #endif + + if (LADSPA_IS_PORT_AUDIO(pd)) + { + if (LADSPA_IS_PORT_INPUT(pd)) + { + ++_inports; + iIdx.push_back(k); + iUsedIdx.push_back(false); // Start out with all false. + } + else if (LADSPA_IS_PORT_OUTPUT(pd)) + { + ++_outports; + oIdx.push_back(k); + } + + rpIdx.push_back((unsigned long)-1); + } + else if (LADSPA_IS_PORT_CONTROL(pd)) + { + if (LADSPA_IS_PORT_INPUT(pd)) + { + rpIdx.push_back(_controlInPorts); + ++_controlInPorts; + pIdx.push_back(k); + // Set to false at first. + //synti->_guiUpdateControls.push_back(false); + } + else if (LADSPA_IS_PORT_OUTPUT(pd)) + { + rpIdx.push_back((unsigned long)-1); + ++_controlOutPorts; + opIdx.push_back(k); + } + } + } + + _inPlaceCapable = !LADSPA_IS_INPLACE_BROKEN(descr->Properties); + // Blacklist vst plugins in-place configurable for now. + if((_inports != _outports) || (info.completeBaseName() == QString("dssi-vst") && !config.vstInPlace)) + _inPlaceCapable = false; + } + } + + if (dssi == 0) + { + //fprintf(stderr, "cannot found DSSI synti %s\n", _name.toAscii().data()); + fprintf(stderr, "cannot find DSSI synti %s\n", _name.toLatin1().constData()); + dlclose(handle); + handle = 0; + df = 0; + return 0; + } + + DssiSynthIF* sif = new DssiSynthIF(synti); + ++_instances; + sif->init(this); + + //_plugin->incInstances(1); + + + +// static char oscUrl[1024]; + //snprintf(oscUrl, 1024, "%s/%s", url, synti->name().toAscii().data()); + //snprintf(oscUrl, 1024, "%s/%s", url, synti->name().toLatin1().constData()); +// snprintf(oscUrl, 1024, "%s/%s/%s", url, info.baseName().toLatin1().constData(), synti->name().toLatin1().constData()); + //QString guiPath(info.path() + "/" + info.baseName()); + QString guiPath(info.path() + "/" + info.baseName()); + QDir guiDir(guiPath, "*", QDir::Unsorted, QDir::Files); + _hasGui = guiDir.exists(); + + //sif->initGui(); + + return sif; +} + +//--------------------------------------------------------- +// guiVisible +//--------------------------------------------------------- + +bool DssiSynthIF::guiVisible() const + { + //return _guiVisible; + #ifdef OSC_SUPPORT + return _oscif.oscGuiVisible(); + #endif + return false; + } + +//--------------------------------------------------------- +// showGui +//--------------------------------------------------------- + +void DssiSynthIF::showGui(bool v) + { + #ifdef OSC_SUPPORT + + #ifdef DSSI_DEBUG + printf("DssiSynthIF::showGui(): v:%d visible:%d\n", v, guiVisible()); + #endif + + _oscif.oscShowGui(v); + + #endif // OSC_SUPPORT + + /* + if (v == guiVisible()) + return; + + //if(guiPid == -1) + if((guiQProc == 0) || (!guiQProc->isRunning())) + { + // We need an indicator that update was called - update must have been called to get new path etc... + // If the process is not running this path is invalid, right? + if(uiOscPath) + free(uiOscPath); + uiOscPath = 0; + + #ifdef DSSI_DEBUG + printf("DssiSynthIF::showGui(): No QProcess or process not running. Starting gui...\n"); + #endif + + initGui(); + } + + //for (int i = 0; i < 5; ++i) { + for (int i = 0; i < 10; ++i) { // Give it a wee bit more time? + if (uiOscPath) + break; + sleep(1); + } + if (uiOscPath == 0) { + printf("DssiSynthIF::showGui(): no uiOscPath. Error: Timeout - synth gui did not start within 10 seconds.\n"); + return; + } + + char uiOscGuiPath[strlen(uiOscPath)+6]; + sprintf(uiOscGuiPath, "%s/%s", uiOscPath, v ? "show" : "hide"); + #ifdef DSSI_DEBUG + printf("DssiSynthIF::showGui(): Sending show/hide uiOscGuiPath:%s\n", uiOscGuiPath); + #endif + + lo_send(uiTarget, uiOscGuiPath, ""); + _guiVisible = v; + */ + } + +//--------------------------------------------------------- +// receiveEvent +//--------------------------------------------------------- + +//MidiEvent DssiSynthIF::receiveEvent() +// { +// return MidiEvent(); +// } +MidiPlayEvent DssiSynthIF::receiveEvent() + { + return MidiPlayEvent(); + } + +//--------------------------------------------------------- +// init +//--------------------------------------------------------- + +bool DssiSynthIF::init(DssiSynth* s) + { + #ifdef DSSI_DEBUG + printf("DssiSynthIF::init\n"); + #endif + + synth = s; + const DSSI_Descriptor* dssi = synth->dssi; + const LADSPA_Descriptor* ld = dssi->LADSPA_Plugin; + handle = ld->instantiate(ld, sampleRate); + + #ifdef OSC_SUPPORT + _oscif.oscSetSynthIF(this); + #endif + + queryPrograms(); + + int inports = synth->_inports; + if(inports != 0) + { + audioInBuffers = new float*[inports]; + for(int k = 0; k < inports; ++k) + { + //audioInBuffers[k] = new LADSPA_Data[segmentSize]; + //posix_memalign((void**)(audioInBuffers + k), 16, sizeof(float) * segmentSize); + posix_memalign((void**)&audioInBuffers[k], 16, sizeof(float) * segmentSize); + memset(audioInBuffers[k], 0, sizeof(float) * segmentSize); + ld->connect_port(handle, synth->iIdx[k], audioInBuffers[k]); + } + } + + int outports = synth->_outports; + if(outports != 0) + { + audioOutBuffers = new float*[outports]; + for(int k = 0; k < outports; ++k) + { + //audioOutBuffers[k] = new LADSPA_Data[segmentSize]; + //posix_memalign((void**)(audioOutBuffers + k), 16, sizeof(float) * segmentSize); + posix_memalign((void**)&audioOutBuffers[k], 16, sizeof(float) * segmentSize); + memset(audioOutBuffers[k], 0, sizeof(float) * segmentSize); + ld->connect_port(handle, synth->oIdx[k], audioOutBuffers[k]); + //printf("DssiSynthIF::init output port name: %s\n", ld->PortNames[synth->oIdx[k]]); // out1, out2, out3 etc + } + } + + int controlPorts = synth->_controlInPorts; + int controlOutPorts = synth->_controlOutPorts; + + if(controlPorts != 0) + controls = new Port[controlPorts]; + else + controls = 0; + + if(controlOutPorts != 0) + controlsOut = new Port[controlOutPorts]; + else + controlsOut = 0; + + synth->midiCtl2PortMap.clear(); + synth->port2MidiCtlMap.clear(); + synti->_guiUpdateControls.clear(); + synti->_guiUpdateProgram = false; + + for (int k = 0; k < controlPorts; ++k) { + int i = synth->pIdx[k]; + //controls[k].val = ladspaDefaultValue(ld, i); + ladspaDefaultValue(ld, i, &controls[k].val); + + // Set to false at first. + synti->_guiUpdateControls.push_back(false); + + #ifdef DSSI_DEBUG + printf("DssiSynthIF::init control port:%d port idx:%d name:%s\n", k, i, ld->PortNames[i]); + #endif + + // This code is duplicated in ::getControllerInfo() + // + + int ctlnum = DSSI_NONE; + if(dssi->get_midi_controller_for_port) + ctlnum = dssi->get_midi_controller_for_port(handle, i); + + // No controller number? Try to give it a unique one... + if(ctlnum == DSSI_NONE) + { + // FIXME: Be more careful. Must make sure to pick numbers not already chosen or which WILL BE chosen. + // Simple but flawed solution: Start them at 0x60000 + 0x2000 = 0x62000. Max NRPN number is 0x3fff. + // TODO: Update: Actually we want to try to use CC Controller7 controllers if possible (or a choice) because what if + // the user's controller hardware doesn't support RPN? + // If CC Controller7 is chosen we must make sure to use only non-common numbers. An already limited range + // of 127 now becomes narrower. See the cool document midi-controllers.txt in the DSSI source for a + // nice roundup of numbers and how to choose them and how they relate to synths and DSSI synths etc. ! + ctlnum = CTRL_NRPN14_OFFSET + 0x2000 + k; + } + else + { + int c = ctlnum; + // Can be both CC and NRPN! Prefer CC over NRPN. + if(DSSI_IS_CC(ctlnum)) + { + #ifdef DSSI_DEBUG + printf("DssiSynthIF::init is CC control\n"); + #endif + + ctlnum = DSSI_CC_NUMBER(c); + #ifdef DSSI_DEBUG + if(DSSI_IS_NRPN(ctlnum)) + printf("DssiSynthIF::init is also NRPN control. Using CC.\n"); + #endif + } + else + if(DSSI_IS_NRPN(ctlnum)) + { + #ifdef DSSI_DEBUG + printf("DssiSynthIF::init is NRPN control\n"); + #endif + + ctlnum = DSSI_NRPN_NUMBER(c) + CTRL_NRPN14_OFFSET; + } + + } + + #ifdef DSSI_DEBUG + printf("DssiSynthIF::init inserting to midiCtl2PortMap: ctlnum:%d k:%d\n", ctlnum, k); + #endif + + // We have a controller number! Insert it and the DSSI port number into both maps. + synth->midiCtl2PortMap.insert(std::pair<int, int>(ctlnum, k)); + synth->port2MidiCtlMap.insert(std::pair<int, int>(k, ctlnum)); + ld->connect_port(handle, i, &controls[k].val); + } + + for (int k = 0; k < controlOutPorts; ++k) { + int i = synth->opIdx[k]; + + #ifdef DSSI_DEBUG + printf("DssiSynthIF::init control output port:%d port idx:%d name:%s\n", k, i, ld->PortNames[i]); + #endif + + // p3.3.39 Removed. + /* + + //controls[k].val = ladspaDefaultValue(ld, i); + ladspaDefaultValue(ld, i, &controlsOut[k].val); + + // This code is duplicated in ::getControllerInfo() + // + + int ctlnum = DSSI_NONE; + if(dssi->get_midi_controller_for_port) + ctlnum = dssi->get_midi_controller_for_port(handle, i); + + // No controller number? Try to give it a unique one... + if(ctlnum == DSSI_NONE) + { + // FIXME: Be more careful. Must make sure to pick numbers not already chosen or which WILL BE chosen. + // Simple but flawed solution: Start them at 0x60000 + 0x3000 = 0x63000. Max NRPN number is 0x3fff. + // TODO: CC etc. etc. + ctlnum = CTRL_NRPN14_OFFSET + 0x3000 + k; + } + else + { + int c = ctlnum; + // Can be both CC and NRPN! Prefer CC over NRPN. + if(DSSI_IS_CC(ctlnum)) + { + #ifdef DSSI_DEBUG + printf("DssiSynthIF::init is CC control\n"); + #endif + + ctlnum = DSSI_CC_NUMBER(c); + + #ifdef DSSI_DEBUG + if(DSSI_IS_NRPN(ctlnum)) + printf("DssiSynthIF::init is also NRPN control. Using CC.\n"); + #endif + } + else + if(DSSI_IS_NRPN(ctlnum)) + { + #ifdef DSSI_DEBUG + printf("DssiSynthIF::init is NRPN control\n"); + #endif + + ctlnum = DSSI_NRPN_NUMBER(c) + CTRL_NRPN14_OFFSET; + } + + } + + #ifdef DSSI_DEBUG + printf("DssiSynthIF::init inserting to midiCtl2PortMap: ctlnum:%d k:%d\n", ctlnum, k); + #endif + + // We have a controller number! Insert it and the DSSI port number into the map. + // p3.3.39 Removed. Doesn't say whether it's in or out! Don't need this for now. + //synth->midiCtl2PortMap.insert(std::pair<int, int>(ctlnum, k)); + + */ + + // - Control outs are not handled but still must be connected to something. + ld->connect_port(handle, i, &controlsOut[k].val); + } + + // Set the latency to zero. + //controls[controlPorts].val = 0.0; + // Insert a controller for latency and the DSSI port number into the map. + //synth->midiCtl2PortMap.insert(std::pair<int, int>(CTRL_NRPN14_OFFSET + 0x2000, controlPorts)); + // Connect the port. + //ld->connect_port(handle, controlPorts, &controls[controlPorts].val); + + // Just a test. It works! We can instantiate a ladspa plugin for the synth. But it needs more work... + //plugins.add(&synth->info, LADSPA_Descriptor_Function(NULL), ld, false); + + if (ld->activate) + ld->activate(handle); + + // Set current configuration values. + if(dssi->configure) + { + char *rv = dssi->configure(handle, DSSI_PROJECT_DIRECTORY_KEY, + museProject.toLatin1().constData()); //song->projectPath() + + if(rv) + { + fprintf(stderr, "MusE: Warning: plugin doesn't like project directory: \"%s\"\n", rv); + free(rv); + } + + for(ciStringParamMap r = synti->_stringParamMap.begin(); r != synti->_stringParamMap.end(); ++r) + { + rv = 0; + rv = dssi->configure(handle, r->first.c_str(), r->second.c_str()); + if(rv) + { + fprintf(stderr, "MusE: Warning: plugin config key: %s value: %s \"%s\"\n", r->first.c_str(), r->second.c_str(), rv); + free(rv); + } + } + } + + // Set current program. + if(dssi->select_program) + dssi->select_program(handle, synti->_curBankL, synti->_curProgram); + + // + // For stored initial control values, let SynthI::initInstance() take care of that via ::setParameter(). + // + + return true; + } + +//--------------------------------------------------------- +// DssiSynthIF +//--------------------------------------------------------- + +DssiSynthIF::DssiSynthIF(SynthI* s) + : SynthIF(s) + { + #ifdef DSSI_DEBUG + printf("DssiSynthIF::DssiSynthIF\n"); + #endif + + synth = 0; + handle = NULL; + controls = 0; + controlsOut = 0; + + //_curBank = 0; + //_curProgram = 0; + + //#ifdef OSC_SUPPORT + //_oscif.setSynthIF(this); + //#endif + + //_guiVisible = false; + //uiTarget = 0; + //uiOscShowPath = 0; + //uiOscControlPath = 0; + //uiOscConfigurePath = 0; + //uiOscProgramPath = 0; + //uiOscPath = 0; + //guiPid = -1; + //guiQProc = 0; + + audioInBuffers = 0; + audioOutBuffers = 0; + } + +//--------------------------------------------------------- +// ~DssiSynthIF +//--------------------------------------------------------- + +DssiSynthIF::~DssiSynthIF() +{ + #ifdef DSSI_DEBUG + printf("DssiSynthIF::~DssiSynthIF\n"); + #endif + + if(synth) + { + #ifdef DSSI_DEBUG + printf("DssiSynthIF::~DssiSynthIF synth:%p\n", synth); + #endif + + if(synth->dssi) + { + #ifdef DSSI_DEBUG + printf("DssiSynthIF::~DssiSynthIF synth->dssi:%p\n", synth->dssi); + #endif + + if(synth->dssi->LADSPA_Plugin) + { + #ifdef DSSI_DEBUG + printf("DssiSynthIF::~DssiSynthIFsynth->dssi->LADSPA_Plugin:%p\n", synth->dssi->LADSPA_Plugin); + #endif + } + } + } + + if(synth && synth->dssi && synth->dssi->LADSPA_Plugin) + { + const DSSI_Descriptor* dssi = synth->dssi; + const LADSPA_Descriptor* descr = dssi->LADSPA_Plugin; + + #ifdef DSSI_DEBUG + printf("DssiSynthIF::~DssiSynthIF checking cleanup function exists\n"); + #endif + + if(descr->cleanup) + { + #ifdef DSSI_DEBUG + printf("DssiSynthIF::~DssiSynthIF calling cleanup function\n"); + #endif + + descr->cleanup(handle); + } + } + + /* + //if (guiPid != -1) + // kill(guiPid, SIGHUP); + if(guiQProc) + { + if(guiQProc->isRunning()) + { + #ifdef DSSI_DEBUG + printf("DssiSynthIF::~DssiSynthIF killing guiQProc\n"); + #endif + + guiQProc->kill(); + } + + //delete guiQProc; + } + + if(uiOscShowPath) + free(uiOscShowPath); + if(uiOscControlPath) + free(uiOscControlPath); + if(uiOscConfigurePath) + free(uiOscConfigurePath); + if(uiOscProgramPath) + free(uiOscProgramPath); + if(uiOscPath) + free(uiOscPath); + if(uiTarget) + lo_address_free(uiTarget); + */ + + if(audioInBuffers) + { + //for(int i = 0; i < synth->_inports; ++i) + //{ + // if(audioInBuffers[i]) + // delete[] audioInBuffers[i]; + //} + for(unsigned long i = 0; i < synth->_inports; ++i) + { + if(audioInBuffers[i]) + free(audioInBuffers[i]); + } + delete[] audioInBuffers; + } + + if(audioOutBuffers) + { + //for(int i = 0; i < synth->_outports; ++i) + //{ + // if(audioOutBuffers[i]) + // delete[] audioOutBuffers[i]; + //} + for(unsigned long i = 0; i < synth->_outports; ++i) + { + if(audioOutBuffers[i]) + free(audioOutBuffers[i]); + } + delete[] audioOutBuffers; + } + + if(controls) + delete[] controls; + + if(controlsOut) + delete[] controlsOut; +} + +//--------------------------------------------------------- +// getParameter +//--------------------------------------------------------- + +float DssiSynthIF::getParameter(unsigned long n) const +{ + if(n >= synth->_controlInPorts) + { + printf("DssiSynthIF::getParameter param number %ld out of range of ports:%ld\n", n, synth->_controlInPorts); + return 0.0; + } + + if(!controls) + return 0.0; + + return controls[n].val; +} + +//--------------------------------------------------------- +// setParameter +//--------------------------------------------------------- + +void DssiSynthIF::setParameter(unsigned long n, float v) +{ + if(n >= synth->_controlInPorts) + { + printf("DssiSynthIF::setParameter param number %ld out of range of ports:%ld\n", n, synth->_controlInPorts); + return; + } + + if(!controls) + return; + + controls[n].val = v; + + // Notify that changes are to be sent upon heartbeat. + // TODO: No, at least not for now. So far, setParameter is only called during loading of stored params, + // and we don't want this interfering with oscUpdate which also sends the values. + //synti->_guiUpdateControls[n] = true; +} + +//--------------------------------------------------------- +// write +//--------------------------------------------------------- + +//void DssiSynthIF::write(Xml&) const +void DssiSynthIF::write(int level, Xml& xml) const +{ + //bool vstsaved = false; + +#ifdef DSSI_VST_CHUNK_SUPPORT + //--------------------------------------------- + // dump current state of synth + //--------------------------------------------- + printf("dumping DSSI custom data! %d\n", synth->dssi->getCustomData); + + // this is only needed and supported if + // we are talking to a VST plugin at the other end. + std::string name = synth->dssi->LADSPA_Plugin->Name; + if ((name.length()> 4) && name.substr(name.length() - 4) == " VST") + { + printf("is vst plugin, commencing data dump, apiversion=%d!\n", synth->dssi->DSSI_API_Version); + unsigned long len = 0; + void* p = 0; + synth->dssi->getCustomData(handle,&p, &len); + if (len) { + xml.tag(level++, "midistate"); + xml.nput(level++, "<event type=\"%d\"", Sysex); + xml.nput(" datalen=\"%d\">\n", len+7 /*VSTSAVE*/); + xml.nput(level, ""); + xml.nput("56 53 54 53 41 56 45 "); // embed a save marker "string 'VSTSAVE' + for (long unsigned int i = 0; i < len; ++i) { + if (i && (((i+7) % 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"); + //vstsaved = true; + } + } +#else + printf("support for vst chunks not compiled in!\n"); +#endif + + /* + // p3.3.39 Store the state of current program and bank and all input control values, but only if VSTSAVE above didn't do it already! + // TODO: Not quite good enough, we would want to store all controls for EACH program, not just the current one. + // Need to modify controls array to be inside a program array and act as a cache when the user changes a control on a particular program. + if(!vstsaved) + { + if(synth->_controlInPorts) + { + // TODO: Hmm, what if these sizes change (platform etc.)? Hard code? Not good - need to store complete value. + const int fs = sizeof(float); + const int uls = sizeof(unsigned long); + + // Data length: Version major and minor bytes, bank + program, and controllers. + const unsigned long len = 2 + 2 * uls + synth->_controlInPorts * fs; + + unsigned long prog = _curBank; + unsigned long bnk = _curProgram; + + xml.tag(level++, "midistate"); + xml.nput(level++, "<event type=\"%d\"", Sysex); + xml.nput(" datalen=\"%d\">\n", len+9); // "PARAMSAVE" length + data length. + xml.nput(level, ""); + xml.nput("50 41 52 41 4d 53 41 56 45 "); // Embed a save marker string "PARAMSAVE". + + unsigned long i = 9; + + // Store PARAMSAVE version major... + char uc = DSSI_PARAMSAVE_VERSION_MAJOR; + if(i && ((i % 16) == 0)) + { + xml.nput("\n"); + xml.nput(level, ""); + } + xml.nput("%02x ", uc & 0xff); + ++i; + + // Store PARAMSAVE version minor... + uc = DSSI_PARAMSAVE_VERSION_MINOR; + if(i && ((i % 16) == 0)) + { + xml.nput("\n"); + xml.nput(level, ""); + } + xml.nput("%02x ", uc & 0xff); + ++i; + + // Store bank... + void* p = &bnk; + for(int j = 0; j < uls; ++j) + { + if(i && ((i % 16) == 0)) + { + xml.nput("\n"); + xml.nput(level, ""); + } + xml.nput("%02x ", ((char*)(p))[j] & 0xff); + ++i; + } + + // Store program... + p = &prog; + for(int j = 0; j < uls; ++j) + { + if(i && ((i % 16) == 0)) + { + xml.nput("\n"); + xml.nput(level, ""); + } + xml.nput("%02x ", ((char*)(p))[j] & 0xff); + ++i; + } + + // Store controls... + for(unsigned long c = 0; c < synth->_controlInPorts; ++c) + { + float v = controls[c].val; + p = &v; + for(int j = 0; j < fs; ++j) + { + if(i && ((i % 16) == 0)) + { + xml.nput("\n"); + xml.nput(level, ""); + } + xml.nput("%02x ", ((char*)(p))[j] & 0xff); + ++i; + } + } + xml.nput("\n"); + xml.tag(level--, "/event"); + xml.etag(level--, "midistate"); + } + } + */ + + // Store controls as parameters... + for(unsigned long c = 0; c < synth->_controlInPorts; ++c) + { + float f = controls[c].val; + xml.floatTag(level, "param", f); + //xml.tag(level, "param name=\"%s\" val=\"%s\"/", name, r->first.c_str(), r->second.c_str()); + } +} + +//--------------------------------------------------------- +// preProcessAlways +//--------------------------------------------------------- + +void DssiSynthIF::preProcessAlways() +{ + +} + +//--------------------------------------------------------- +// processEvent +// Return true if event pointer filled. +//-------------------------------------------------------- + +bool DssiSynthIF::processEvent(const MidiPlayEvent& e, snd_seq_event_t* event) +{ + const DSSI_Descriptor* dssi = synth->dssi; + + int chn = e.channel(); + int a = e.dataA(); + int b = e.dataB(); + //for sysex + //QByteArray ba = QByteArray((const char*)e.data(), e.len()); + //we must had 0xF0 at the beginning and 0xF7 at the end of e.data() + //ba.push_front(0xF0); + //ba.push_back(0xF7); + + //QByteArray ba(); + ////ba.assign((const char*)e.data(), e.len()); + ////ba.duplicate((const char*)e.data(), e.len()); + ////ba.setRawData((const char*)e.data(), e.len()); + //int len = e.len() + 2; + + 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; + + //snd_seq_event_t* event = &events[nevents]; + event->queue = SND_SEQ_QUEUE_DIRECT; + + #ifdef DSSI_DEBUG + fprintf(stderr, "DssiSynthIF::processEvent midi event type:%d chn:%d a:%d b:%d\n", e.type(), chn, a, b); + #endif + + switch(e.type()) + { + case ME_NOTEON: + #ifdef DSSI_DEBUG + fprintf(stderr, "DssiSynthIF::processEvent midi event is ME_NOTEON\n"); + #endif + + if(b) + snd_seq_ev_set_noteon(event, chn, a, b); + else + snd_seq_ev_set_noteoff(event, chn, a, 0); + break; + case ME_NOTEOFF: + snd_seq_ev_set_noteoff(event, chn, a, 0); + break; + case ME_PROGRAM: + { + #ifdef DSSI_DEBUG + fprintf(stderr, "DssiSynthIF::processEvent midi event is ME_PROGRAM\n"); + #endif + + int bank = (a >> 8) & 0xff; + int prog = a & 0xff; + //_curBank = bank; + //_curProgram = prog; + synti->_curBankH = 0; + synti->_curBankL = bank; + synti->_curProgram = prog; + + if(dssi->select_program) + { + dssi->select_program(handle, bank, prog); + // Notify that changes are to be sent upon heartbeat. + synti->_guiUpdateProgram = true; + } + // Event pointer not filled. Return false. + return false; + } + break; + case ME_CONTROLLER: + { + #ifdef DSSI_DEBUG + fprintf(stderr, "DssiSynthIF::processEvent midi event is ME_CONTROLLER\n"); + #endif + + if((a == 0) || (a == 32)) + return false; + + if(a == CTRL_PROGRAM) + { + #ifdef DSSI_DEBUG + fprintf(stderr, "DssiSynthIF::processEvent midi event is ME_CONTROLLER, dataA is CTRL_PROGRAM\n"); + #endif + + int bank = (b >> 8) & 0xff; + int prog = b & 0xff; + + //_curBank = bank; + //_curProgram = prog; + synti->_curBankH = 0; + synti->_curBankL = bank; + synti->_curProgram = prog; + + if(dssi->select_program) + { + dssi->select_program(handle, bank, prog); + // Notify that changes are to be sent upon heartbeat. + synti->_guiUpdateProgram = true; + } + // Event pointer not filled. Return false. + return false; + } + + if(a == CTRL_PITCH) + { + #ifdef DSSI_DEBUG + fprintf(stderr, "DssiSynthIF::processEvent midi event is ME_CONTROLLER, dataA is CTRL_PITCH\n"); + #endif + + b &= 0x3fff; + snd_seq_ev_set_pitchbend(event, chn, b); + // Event pointer filled. Return true. + return true; + } + + const LADSPA_Descriptor* ld = dssi->LADSPA_Plugin; + + 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()) + { + // p3.3.39 Changed to return false because of crashes with unknown controllers when switching a midi track + // among different dssi synths and regular synths etc. For example high RPN offset numbers (set by another + // device selected into the midi port before selecting this synth) were passing through here when in fact + // the particular synth had no such midi controllers. + // ========================== No, that leaves out regular controllers like footpedal + //#ifdef DSSI_DEBUG + //fprintf(stderr, "DssiSynthIF::processEvent dataA:%d not found in map (not a ladspa controller). Ignoring.\n", a); + //#endif + //return false; + + //#ifdef DSSI_DEBUG + //fprintf(stderr, "DssiSynthIF::processEvent dataA:%d not found in map (not a ladspa controller). Filling event as regular controller.\n", a); + //#endif + //snd_seq_ev_set_controller(event, chn, a, b); + //return true; + + int ctlnum = a; + //switch(midiControllerType(a)) + if(midiControllerType(a) != MidiController::Controller7) + return false; + else + { + /* + case MidiController::NRPN14: + case MidiController::Controller14: + case MidiController::Pitch: + case MidiController::Program: + case MidiController::RPN: + case MidiController::RPN14: + case MidiController::NRPN: + #ifdef DSSI_DEBUG + fprintf(stderr, "DssiSynthIF::processEvent non-ladspa midi event controller unsupported. DataA:%d\n", a); + #endif + return false; + */ + + //case MidiController::Controller7: + #ifdef DSSI_DEBUG + //fprintf(stderr, "DssiSynthIF::processEvent midi event is Controller7. Changing to DSSI_CC type. Current dataA:%d\n", a); + fprintf(stderr, "DssiSynthIF::processEvent non-ladspa midi event is Controller7. Current dataA:%d\n", a); + #endif + //a = DSSI_CC(a); + a &= 0x7f; + ctlnum = DSSI_CC_NUMBER(ctlnum); + // break; + + /* + case MidiController::NRPN14: + #ifdef DSSI_DEBUG + // fprintf(stderr, "DssiSynthIF::processEvent midi event is NRPN. Changing to DSSI_NRPN type. Current dataA:%d\n", a); + fprintf(stderr, "DssiSynthIF::processEvent non-ladspa midi event is NRPN. Current dataA:%d\n", a); + #endif + //a = DSSI_NRPN(a - CTRL_NRPN14_OFFSET); + a &= 0x3fff; + ctlnum = DSSI_NRPN_NUMBER(ctlnum); + break; + case MidiController::Controller14: + a &= 0x7f; + break; + case MidiController::Pitch: + // Should be caught above! + #ifdef DSSI_DEBUG + //fprintf(stderr, "DssiSynthIF::processEvent non-ladspa midi event is Pitch. DataA:%d\n", a); + fprintf(stderr, "DssiSynthIF::processEvent Error! non-ladspa midi event is Pitch. Should have been caught already! DataA:%d\n", a); + #endif + //a &= 0x3fff; + //snd_seq_ev_set_pitchbend(event, chn, b); + // Event pointer filled. Return true. + //return true; + // Event pointer not filled. Return false. + return false; + case MidiController::Program: + // Should be caught above! + #ifdef DSSI_DEBUG + fprintf(stderr, "DssiSynthIF::processEvent Error! non-ladspa midi event is Program. Should have been caught already! DataA:%d\n", a); + #endif + return false; + case MidiController::RPN: + case MidiController::RPN14: + case MidiController::NRPN: + default: + #ifdef DSSI_DEBUG + fprintf(stderr, "DssiSynthIF::processEvent non-ladspa midi event is RPN, RPN14, or NRPN type. DataA:%d\n", a); + #endif + break; + */ + } + + // Verify it's the same number. + //if(ctlnum != a) + //{ + // #ifdef DSSI_DEBUG + // printf("DssiSynthIF::processEvent Error! non-ladspa midi ctlnum:%d != event dataA:%d\n", ctlnum, a); + // #endif + // Event not filled. Return false. + + // TEMP: TODO: Turn on later + //return false; + //} + + // Fill the event. + #ifdef DSSI_DEBUG + printf("DssiSynthIF::processEvent non-ladspa filling midi event chn:%d dataA:%d dataB:%d\n", chn, a, b); + #endif + snd_seq_ev_set_controller(event, chn, a, b); + return true; + } + + //int num = ip->first; + unsigned long k = ip->second; + + unsigned long i = synth->pIdx[k]; + + 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; + + // TODO: If necessary... choose non-existing numbers... + //for(int k = 0; k < controlPorts; ++k) + //{ + // int i = synth->pIdx[k]; + //} + + // Simple but flawed solution: Start them at 0x60000 + 0x2000 = 0x62000. Max NRPN number is 0x3fff. + ctlnum = k + (CTRL_NRPN14_OFFSET + 0x2000); + } + // p3.3.39 + else + { + #ifdef DSSI_DEBUG + printf("DssiSynthIF::processEvent plugin requests DSSI-style ctlnum:%x(h) %d(d) be mapped to control port:%ld...\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 DSSI_DEBUG + printf("DssiSynthIF::processEvent is CC ctlnum:%d\n", ctlnum); + #endif + + #ifdef DSSI_DEBUG + if(DSSI_IS_NRPN(ctlnum)) + printf("DssiSynthIF::processEvent is also NRPN control. Using CC.\n"); + #endif + } + else + if(DSSI_IS_NRPN(ctlnum)) + { + ctlnum = DSSI_NRPN_NUMBER(c) + CTRL_NRPN14_OFFSET; + + #ifdef DSSI_DEBUG + printf("DssiSynthIF::processEvent is NRPN ctlnum:%x(h) %d(d)\n", ctlnum, ctlnum); + #endif + } + + } + + //{ + float val = midi2LadspaValue(ld, i, ctlnum, b); + + #ifdef DSSI_DEBUG + //fprintf(stderr, "DssiSynthIF::processEvent No midi controller for control port:%d port:%d dataA:%d Converting val from:%d to ladspa:%f\n", i, k, a, b, val); + fprintf(stderr, "DssiSynthIF::processEvent control port:%ld port:%ld 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; + // FIXME: Testing - Works but is this safe in a RT process callback? Try hooking into gui heartbeat timer instead... + //lo_send(uiTarget, uiOscControlPath, "if", i, val); + // Notify that changes are to be sent upon heartbeat. + synti->_guiUpdateControls[k] = true; + + // Since we absorbed the message as a ladspa control change, return false - the event is not filled. + return false; + //} + + // p3.3.39 Removed. + // "Hosts should not deliver through run_synth any MIDI controller events that have already + // been mapped to control port values." + // D'oh! My mistake, did not understand that the mapping is only a *request* that the app map MIDI + // controller events to a LADSPA port, and must do the conversion, not to actually *send* them via MIDI... + /* + else + { + switch(midiControllerType(a)) + { + case MidiController::Controller7: + #ifdef DSSI_DEBUG + //fprintf(stderr, "DssiSynthIF::processEvent midi event is Controller7. Changing to DSSI_CC type. Current dataA:%d\n", a); + fprintf(stderr, "DssiSynthIF::processEvent midi event is Controller7. Current dataA:%d\n", a); + #endif + //a = DSSI_CC(a); + a &= 0x7f; + ctlnum = DSSI_CC_NUMBER(ctlnum); + break; + case MidiController::NRPN14: + #ifdef DSSI_DEBUG + // fprintf(stderr, "DssiSynthIF::processEvent midi event is NRPN. Changing to DSSI_NRPN type. Current dataA:%d\n", a); + fprintf(stderr, "DssiSynthIF::processEvent midi event is NRPN. Current dataA:%d\n", a); + #endif + //a = DSSI_NRPN(a - CTRL_NRPN14_OFFSET); + a &= 0x3fff; + ctlnum = DSSI_NRPN_NUMBER(ctlnum); + break; + case MidiController::Controller14: + a &= 0x7f; + break; + case MidiController::Pitch: + #ifdef DSSI_DEBUG + fprintf(stderr, "DssiSynthIF::processEvent midi event is Pitch. DataA:%d\n", a); + #endif + a &= 0x3fff; + break; + case MidiController::Program: + #ifdef DSSI_DEBUG + fprintf(stderr, "DssiSynthIF::processEvent midi event is Program. DataA:%d\n", a); + #endif + a &= 0x3fff; + break; + case MidiController::RPN: + case MidiController::RPN14: + case MidiController::NRPN: + default: + #ifdef DSSI_DEBUG + fprintf(stderr, "DssiSynthIF::processEvent midi event is RPN, RPN14, or NRPN type. DataA:%d\n", a); + #endif + break; + } + + // Verify it's the same number. + if(ctlnum != a) + { + #ifdef DSSI_DEBUG + printf("DssiSynthIF::processEvent Error! ctlnum:%d != event dataA:%d\n", ctlnum, a); + #endif + // Event not filled. Return false. + + // TEMP: TODO: Turn on later + //return false; + } + + // Fill the event. + // FIXME: Darn! We get to this point, but no change in sound (later). Nothing happens, at least with LTS - + // which is the only one I found so far with midi controllers. + // Tried with/without converting to DSSI_CC and DSSI_NRPN. What could be wrong here? + #ifdef DSSI_DEBUG + printf("DssiSynthIF::processEvent filling event chn:%d dataA:%d dataB:%d\n", chn, a, b); + #endif + snd_seq_ev_set_controller(event, chn, a, b); + } + */ + + } + break; + case ME_PITCHBEND: + snd_seq_ev_set_pitchbend(event, chn, a); + break; + case ME_AFTERTOUCH: + snd_seq_ev_set_chanpress(event, chn, a); + break; + case ME_SYSEX: + #ifdef DSSI_DEBUG + fprintf(stderr, "DssiSynthIF::processEvent midi event is ME_SYSEX\n"); + #endif + + if (QString((const char*)e.data()).startsWith("VSTSAVE")) { +#ifdef DSSI_VST_CHUNK_SUPPORT + printf("loading chunk from sysex %s!\n", e.data()+7); + dssi->setCustomData(handle, e.data()+7 /* len of str*/,e.len()-7); +#else + printf("support for vst chunks not compiled in!\n"); +#endif + // Event not filled. + return false; + } + /* + // 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 DSSI_DEBUG + fprintf(stderr, "DssiSynthIF::processEvent midi event is 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. + printf("DssiSynthIF::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) + printf("DssiSynthIF::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) + printf("DssiSynthIF::processEvent Warning: PARAMSAVE number of floats:%ld != number of controls:%ld\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 + { + // NOTE: There is a limit on the size of a sysex. Got this: + // "DssiSynthIF::processEvent midi event is ME_SYSEX" + // "WARNING: MIDI event of type ? decoded to 367 bytes, discarding" + // That might be ALSA doing that. + snd_seq_ev_set_sysex(event, len, + //(unsigned char*)ba.data()); + (unsigned char*)ca); + } + break; + default: + if(debugMsg) + fprintf(stderr, "DssiSynthIF::processEvent midi event unknown type:%d\n", e.type()); + // Event not filled. + return false; + break; + } + + return true; +} + +//--------------------------------------------------------- +// getData +//--------------------------------------------------------- + +//void DssiSynthIF::getData(MidiEventList* el, unsigned pos, int ch, unsigned samples, float** data) +iMPEvent DssiSynthIF::getData(MidiPort* /*mp*/, MPEventList* el, iMPEvent /*i*/, unsigned pos, int ports, unsigned n, float** buffer) +{ + //#ifdef DSSI_DEBUG + // fprintf(stderr, "DssiSynthIF::getData elsize:%d pos:%d ports:%d samples:%d processed already?:%d\n", el->size(), pos, ports, n, synti->processed()); + //#endif + + //BEGIN: Process midi events + + // FIXME: Add 10(?) for good luck in case volatile size changes (increments) while we're processing. + //unsigned long nevents = el->size(); + unsigned long nevents = el->size() + synti->putFifo.getSize() + 10; + + /* + while (!synti->putFifo.isEmpty()) { + MidiEvent event = synti->putFifo.get(); + printf("Dssi: FIFO\n"); + } + */ + + snd_seq_event_t events[nevents]; + memset(events, 0, sizeof(events)); + nevents = 0; + + //int curPos = pos; + //unsigned endPos = pos + samples; + unsigned endPos = pos + n; + //int off = pos; + int frameOffset = audio->getFrameOffset(); + + //iMidiEvent i = el->begin(); + iMPEvent i = el->begin(); + + // Process event list events... + for(; i != el->end(); ++i) + { + //if(i->time() >= endPos) // Doesn't work, at least here in muse-1. The event times are all + // just slightly after the endPos, EVEN IF transport is stopped. + // So it misses all the notes. + if(i->time() >= (endPos + frameOffset)) // NOTE: frameOffset? Tested, examined printouts of times: Seems OK for playback. + break; + + #ifdef DSSI_DEBUG + fprintf(stderr, "DssiSynthIF::getData eventlist event time:%d\n", i->time()); + #endif + + // p3.3.39 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) + { + MidiPort* mp = &midiPorts[synti->midiPort()]; + if(i->type() == ME_CONTROLLER) + { + int da = i->dataA(); + int db = i->dataB(); + db = mp->limitValToInstrCtlRange(da, db); + if(!mp->setHwCtrlState(i->channel(), da, db)) + continue; + //mp->setHwCtrlState(i->channel(), da, db); + } + else + if(i->type() == ME_PITCHBEND) + { + int da = mp->limitValToInstrCtlRange(CTRL_PITCH, i->dataA()); + if(!mp->setHwCtrlState(i->channel(), CTRL_PITCH, da)) + continue; + //mp->setHwCtrlState(i->channel(), CTRL_PITCH, da); + } + else + if(i->type() == ME_PROGRAM) + { + if(!mp->setHwCtrlState(i->channel(), CTRL_PROGRAM, i->dataA())) + continue; + //mp->setHwCtrlState(i->channel(), CTRL_PROGRAM, i->dataA()); + } + } + + if(processEvent(*i, &events[nevents])) + ++nevents; + } + + // Now process putEvent events... + while(!synti->putFifo.isEmpty()) + { + MidiPlayEvent e = synti->putFifo.get(); + + #ifdef DSSI_DEBUG + fprintf(stderr, "DssiSynthIF::getData putFifo event time:%d\n", e.time()); + #endif + + // Set to the current time. + // FIXME: FIXME: Wrong - we should be setting some kind of linear realtime wallclock here, not song pos. + e.setTime(pos); + if(processEvent(e, &events[nevents])) + ++nevents; + } + + // Now process OSC gui input control fifo events. + // It is probably more important that these are processed last so that they take precedence over all other + // events because OSC + DSSI/DSSI-VST are fussy about receiving feedback via these control ports, from GUI changes. + #ifdef OSC_SUPPORT + unsigned long ctls = synth->_controlInPorts; + for(unsigned long k = 0; k < ctls; ++k) + { + OscControlFifo* cfifo = _oscif.oscFifo(k); + if(!cfifo) + continue; + + // If there are 'events' in the fifo, get exactly one 'event' per control per process cycle... + if(!cfifo->isEmpty()) + { + OscControlValue v = cfifo->get(); + + #ifdef DSSI_DEBUG + fprintf(stderr, "DssiSynthIF::getData OscControlFifo event input control number:%ld value:%f\n", k, v.value); + #endif + + // Set the ladspa control port value. + controls[k].val = v.value; + + // TODO: (From plugin module, adapt for synth if/when our own plugin gui is added to synths). + // Need to update the automation value, otherwise the block above overwrites with the last automation value. + ///if(_track) + ///{ + // Since we are now in the audio thread context, there's no need to send a message, + // just modify directly. + //audio->msgSetPluginCtrlVal(this, genACnum(_id, i), controls[i].val); + /// _track->setPluginCtrlVal(k, v.value) + ///} + } + } + #endif + +/* // This is from MESS... Tried this here, didn't work, need to re-adapt, try again. + int evTime = i->time(); + if(evTime == 0) + { + printf("DssiSynthIF::getData - time is 0!\n"); + //continue; + evTime=frameOffset; // will cause frame to be zero, problem? + } + + int frame = evTime - frameOffset; + + if(frame >= endPos) + { + printf("DssiSynthIF::getData frame > endPos!! frame = %d >= endPos %d, i->time() %d, frameOffset %d curPos=%d\n", frame, endPos, i->time(), frameOffset,curPos); + continue; + } + + if(frame > curPos) + { + if(frame < pos) + printf("DssiSynthIF::getData should not happen: missed event %d\n", pos -frame); + else + { +*/ + +/* + } + curPos = frame; + } +*/ +// } + + el->erase(el->begin(), i); + //END: Process midi events + + //BEGIN: Run the synth + // All ports must be connected to something! + + // First, copy the given input buffers to our local input buffers. + unsigned long np, k; + //np = portsin > synth->_inports ? synth->_inports : portsin; + //for(k = 0; k < np; ++k) + // memcpy(audioInBuffers[k], inbuffer[k], sizeof(float) * n); + //for(; k < portsin; ++k) + // memset(audioInBuffers[k], 0, sizeof(float) * n); + + // Watch our limits. + np = ports > synth->_outports ? synth->_outports : ports; + + const DSSI_Descriptor* dssi = synth->dssi; + const LADSPA_Descriptor* descr = dssi->LADSPA_Plugin; + k = 0; + // Connect the given buffers directly to the ports, up to a max of synth ports. + for(; k < np; ++k) + descr->connect_port(handle, synth->oIdx[k], buffer[k]); + // Connect the remaining ports to some local buffers (not used yet). + for(; k < synth->_outports; ++k) + descr->connect_port(handle, synth->oIdx[k], audioOutBuffers[k]); + + /* + // + // p3.3.39 Handle inputs... + // + //if((song->bounceTrack != this) && !noInRoute()) + if(!((AudioTrack*)synti)->noInRoute()) + { + RouteList* irl = ((AudioTrack*)synti)->inRoutes(); + iRoute i = irl->begin(); + if(!i->track->isMidiTrack()) + { + //if(debugMsg) + printf("DssiSynthIF::getData: Error: First route is a midi track route!\n"); + } + else + { + int ch = i->channel == -1 ? 0 : i->channel; + int remch = i->remoteChannel == -1 ? 0 : i->remoteChannel; + int chs = i->channels == -1 ? 0 : i->channels; + + // TODO: + //if(ch >= synth->_inports) + //iUsedIdx[ch] = true; + //if(chs == 2) + // iUsedIdx[ch + 1] = true; + + //((AudioTrack*)i->track)->copyData(framePos, channels, nframe, bp); + ((AudioTrack*)i->track)->copyData(pos, ports, + //(i->track->type() == Track::AUDIO_SOFTSYNTH && i->channel != -1) ? i->channel : 0, + i->channel, + i->channels, + n, bp); + } + + //unsigned pos, int ports, unsigned n, float** buffer + + ++i; + for(; i != irl->end(); ++i) + { + if(i->track->isMidiTrack()) + { + //if(debugMsg) + printf("DssiSynthIF::getData: Error: Route is a midi track route!\n"); + continue; + } + //((AudioTrack*)i->track)->addData(framePos, channels, nframe, bp); + ((AudioTrack*)i->track)->addData(framePos, channels, + //(i->track->type() == Track::AUDIO_SOFTSYNTH && i->channel != -1) ? i->channel : 0, + i->channel, + i->channels, + nframe, bp); + } + } + */ + + //#ifdef DSSI_DEBUG + //if(nevents) + // fprintf(stderr, "DssiSynthIF::getData run nevents:%d\n", nevents); + //#endif + + // Run the synth for one segment. This processes events and gets/fills our local buffers... + if(synth->dssi->run_synth) + { + synth->dssi->run_synth(handle, n, events, nevents); + + // NOTE: Just a test + //for(int m = 0; m < n; ++m) + //{ + // synth->dssi->run_synth(handle, 1, events, nevents); + //} + + } + else if (synth->dssi->run_multiple_synths) + { + snd_seq_event_t* ev = events; + synth->dssi->run_multiple_synths(1, &handle, n, &ev, &nevents); + } + //END: Run the synth + + return i; +} + +//--------------------------------------------------------- +// putEvent +//--------------------------------------------------------- + +//bool DssiSynthIF::putEvent(const MidiEvent& ev) +bool DssiSynthIF::putEvent(const MidiPlayEvent& ev) + { + #ifdef DSSI_DEBUG + fprintf(stderr, "DssiSynthIF::putEvent midi event time:%d chn:%d a:%d b:%d\n", ev.time(), ev.channel(), ev.dataA(), ev.dataB()); + #endif + + if (midiOutputTrace) + ev.dump(); + + return synti->putFifo.put(ev); + + //return false; + } + + +//--------------------------------------------------------- +// incInstances +//--------------------------------------------------------- + +void DssiSynth::incInstances(int val) +{ + _instances += val; + if (_instances == 0) + { + if (handle) + { + #ifdef DSSI_DEBUG + fprintf(stderr, "DssiSynth::incInstances no more instances, closing library\n"); + #endif + + dlclose(handle); + } + handle = 0; + dssi = NULL; + df = NULL; + pIdx.clear(); + opIdx.clear(); + iIdx.clear(); + oIdx.clear(); + rpIdx.clear(); + iUsedIdx.clear(); + midiCtl2PortMap.clear(); + port2MidiCtlMap.clear(); + //synti->_guiUpdateControls.clear(); + } +} + +//--------------------------------------------------------- +// initGui +//--------------------------------------------------------- +bool DssiSynthIF::initGui() +{ + #ifdef OSC_SUPPORT + return _oscif.oscInitGui(); + #endif + + return true; + + /* + // Are we already running? We don't want to allow another process do we... + if((guiQProc != 0) && (guiQProc->isRunning())) + return true; + + // + // start gui + // + static char oscUrl[1024]; + //snprintf(oscUrl, 1024, "%s/%s", url, synti->name().toAscii().data()); + //snprintf(oscUrl, 1024, "%s/%s", url, synti->name().toLatin1().constData()); + snprintf(oscUrl, 1024, "%s/%s/%s", url, synth->info.baseName().toLatin1().constData(), synti->name().toLatin1().constData()); + + //QString guiPath(info.path() + "/" + info.baseName()); + QString guiPath(synth->info.dirPath() + "/" + synth->info.baseName()); + + QDir guiDir(guiPath, "*", QDir::Unsorted, QDir::Files); + if (guiDir.exists()) + { + //const QFileInfoList list = guiDir.entryInfoList(); + QStringList list = guiDir.entryList(); + + //for (int i = 0; i < list.size(); ++i) { + for (unsigned int i = 0; i < list.count(); ++i) + { + + //QFileInfo fi = list.at(i); + QFileInfo fi(guiPath + QString("/") + list[i]); + + QString gui(fi.filePath()); + if (gui.contains('_') == 0) + continue; + struct stat buf; + + //if (stat(gui.toAscii().data(), &buf)) { + if (stat(gui.toLatin1().constData(), &buf)) { + + perror("stat failed"); + continue; + } + + #ifdef DSSI_DEBUG + fprintf(stderr, "DssiSynthIF::initGui %s %s %s %s\n", + //fi.filePath().toAscii().data(), + //fi.fileName().toAscii().data(), + fi.filePath().toLatin1().constData(), + //fi.fileName().toLatin1().constData(), + + oscUrl, + + synth->info.filePath().toLatin1().constData(), + + //name().toAscii().data(), + synth->name().toLatin1().constData()); + #endif + + if ((S_ISREG(buf.st_mode) || S_ISLNK(buf.st_mode)) && + (buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) + { + // Changed by T356. + // fork + execlp were causing the processes to remain after closing gui, requiring manual kill. + // Changed to QProcess, works OK now. + //if((guiPid = fork()) == 0) + { + // No QProcess created yet? Do it now. Only once per SynthIF instance. Exists until parent destroyed. + if(guiQProc == 0) + guiQProc = new QProcess(muse); + + // Don't forget this, he he... + guiQProc->clearArguments(); + + guiQProc->addArgument(fi.filePath()); + //guiQProc->addArgument(fi.fileName()); // No conventional 'Arg0' here. + guiQProc->addArgument(QString(oscUrl)); + guiQProc->addArgument(synth->info.filePath()); + guiQProc->addArgument(synth->name()); + guiQProc->addArgument(QString("channel 1")); + + #ifdef DSSI_DEBUG + fprintf(stderr, "DssiSynthIF::initGui starting QProcess\n"); + #endif + + if(guiQProc->start() == TRUE) + { + #ifdef DSSI_DEBUG + fprintf(stderr, "DssiSynthIF::initGui started QProcess\n"); + #endif + + //guiPid = guiQProc->processIdentifier(); + } + else + { + + // execlp( + // fi.filePath().toAscii().data(), + // fi.fileName().toAscii().data(), + // fi.filePath().toLatin1().constData(), + // fi.fileName().toLatin1().constData(), + + // oscUrl, + + // info.filePath().toAscii().data(), + // name().toAscii().data(), + // synth->info.filePath().toLatin1().constData(), + // synth->name().toLatin1().constData(), + + // "channel 1", (void*)0); + + fprintf(stderr, "exec %s %s %s %s failed: %s\n", + // fi.filePath().toAscii().data(), + // fi.fileName().toAscii().data(), + fi.filePath().toLatin1().constData(), + fi.fileName().toLatin1().constData(), + oscUrl, + // name().toAscii().data(), + synth->name().toLatin1().constData(), + strerror(errno)); + + // It's Ok, Keep going. So nothing happens. So what. The timeout in showGui will just leave. + // Maybe it's a 'busy' issue somewhere - allow to try again later + save work now. + // exit(1); + + } + + #ifdef DSSI_DEBUG + fprintf(stderr, "DssiSynthIF::initGui after QProcess\n"); + #endif + } + } + } + //synth->_hasGui = true; + } + else { + printf("%s: no dir for dssi gui found: %s\n", + //name().toAscii().data(), guiPath.toAscii().data()); + synth->name().toLatin1().constData(), guiPath.toLatin1().constData()); + + //synth->_hasGui = false; + } + + return true; + */ +} + +//--------------------------------------------------------- +// guiHeartBeat +//--------------------------------------------------------- + +void DssiSynthIF::guiHeartBeat() +{ + #ifdef OSC_SUPPORT + // Update the gui's program if needed. + if(synti->_guiUpdateProgram) + { + _oscif.oscSendProgram(synti->_curProgram, synti->_curBankL); + synti->_guiUpdateProgram = false; + } + + // Update the gui's controls if needed. + unsigned long ports = synth->_controlInPorts; + if(ports > synti->_guiUpdateControls.size()) + return; + for(unsigned long i = 0; i < ports; ++i) + { + if(synti->_guiUpdateControls[i]) + { + unsigned long k = synth->pIdx[i]; + _oscif.oscSendControl(k, controls[i].val); + + // Reset. + synti->_guiUpdateControls[i] = false; + } + } + #endif +} + +#ifdef OSC_SUPPORT +//--------------------------------------------------------- +// oscUpdate +//--------------------------------------------------------- + +int DssiSynthIF::oscUpdate() +{ + // Send project directory. + _oscif.oscSendConfigure(DSSI_PROJECT_DIRECTORY_KEY, museProject.toLatin1().constData()); // song->projectPath() + + // Send current string configuration parameters. + //StringParamMap& map = synti->_stringParamMap; + int i = 0; + for(ciStringParamMap r = synti->_stringParamMap.begin(); r != synti->_stringParamMap.end(); ++r) + { + _oscif.oscSendConfigure(r->first.c_str(), r->second.c_str()); + // Avoid overloading the GUI if there are lots and lots of params. + if((i+1) % 50 == 0) + usleep(300000); + ++i; + } + + // Send current bank and program. + //unsigned long bank, prog; + //synti->currentProg(&prog, &bank, 0); + //_oscif.oscSendProgram(prog, bank); + _oscif.oscSendProgram(synti->_curProgram, synti->_curBankL); + + // Send current control values. + unsigned long ports = synth->_controlInPorts; + for(unsigned long i = 0; i < ports; ++i) + { + unsigned long k = synth->pIdx[i]; + _oscif.oscSendControl(k, controls[i].val); + // Avoid overloading the GUI if there are lots and lots of ports. + if((i+1) % 50 == 0) + usleep(300000); + } + + +#if 0 + /* Send current bank/program (-FIX- another race...) */ + if (instance->pendingProgramChange < 0) { + unsigned long bank = instance->currentBank; + unsigned long program = instance->currentProgram; + instance->uiNeedsProgramUpdate = 0; + if (instance->uiTarget) { + lo_send(instance->uiTarget, instance->ui_osc_program_path, "ii", bank, program); + } + } + + /* Send control ports */ + for (i = 0; i < instance->plugin->controlIns; i++) { + int in = i + instance->firstControlIn; + int port = pluginControlInPortNumbers[in]; + lo_send(instance->uiTarget, instance->ui_osc_control_path, "if", port, + pluginControlIns[in]); + /* Avoid overloading the GUI if there are lots and lots of ports */ + if ((i+1) % 50 == 0) + usleep(300000); + } +#endif + return 0; +} + +//--------------------------------------------------------- +// oscProgram +//--------------------------------------------------------- + +int DssiSynthIF::oscProgram(unsigned long program, unsigned long bank) + { + //int bank = argv[0]->i; + //int program = argv[1]->i; + + int ch = 0; // TODO: ?? + + int port = synti->midiPort(); + + //_curBank = bank; + //_curProgram = program; + synti->_curBankH = 0; + synti->_curBankL = bank; + synti->_curProgram = program; + + bank &= 0xff; + program &= 0xff; + + //MidiEvent event(0, ch, ME_CONTROLLER, CTRL_PROGRAM, (bank << 8) + program); + + if(port != -1) + { + //MidiPlayEvent event(0, port, ch, ME_CONTROLLER, CTRL_PROGRAM, (bank << 8) + program); + MidiPlayEvent event(0, port, ch, ME_PROGRAM, (bank << 8) + program, 0); + + #ifdef DSSI_DEBUG + fprintf(stderr, "DssiSynthIF::oscProgram midi event chn:%d a:%d b:%d\n", event.channel(), event.dataA(), event.dataB()); + #endif + + midiPorts[port].sendEvent(event); + } + + + + //synti->playMidiEvent(&event); // TODO + // + //MidiDevice* md = dynamic_cast<MidiDevice*>(synti); + //if(md) + // md->putEvent(event); + // + //synti->putEvent(event); + + return 0; + } + +//--------------------------------------------------------- +// oscControl +//--------------------------------------------------------- + +int DssiSynthIF::oscControl(unsigned long port, float value) + { + //int port = argv[0]->i; + //LADSPA_Data value = argv[1]->f; + + #ifdef DSSI_DEBUG + printf("DssiSynthIF::oscControl received oscControl port:%ld val:%f\n", port, value); + #endif + + //int controlPorts = synth->_controlInPorts; + + //if(port >= controlPorts) + //if(port < 0 || port >= synth->rpIdx.size()) + if(port >= synth->rpIdx.size()) + { + //fprintf(stderr, "DssiSynthIF::oscControl: port number:%d is out of range of number of ports:%d\n", port, controlPorts); + fprintf(stderr, "DssiSynthIF::oscControl: port number:%ld is out of range of index list size:%zd\n", port, synth->rpIdx.size()); + return 0; + } + + // Convert from DSSI port number to control input port index. + unsigned long cport = synth->rpIdx[port]; + + if((int)cport == -1) + { + fprintf(stderr, "DssiSynthIF::oscControl: port number:%ld is not a control input\n", port); + return 0; + } + + // p3.3.39 Set the DSSI control input port's value. + // Observations: With a native DSSI synth like LessTrivialSynth, the native GUI's controls do not change the sound at all + // ie. they don't update the DSSI control port values themselves. + // Hence in response to the call to this oscControl, sent by the native GUI, it is required to that here. +/// controls[cport].val = value; + // DSSI-VST synths however, unlike DSSI synths, DO change their OWN sound in response to their gui controls. + // AND this function is called. + // Despite the descrepency we are STILL required to update the DSSI control port values here + // because dssi-vst is WAITING FOR A RESPONSE. (A CHANGE in the control port value). + // It will output something like "...4 events expected..." and count that number down as 4 actual control port value CHANGES + // are done here in response. Normally it says "...0 events expected..." when MusE is the one doing the DSSI control changes. + // TODO: (Done) May need FIFOs on each control(!) so that the control changes get sent one per process cycle. + // Observed countdown not actually going to zero upon string of changes. + // + // NOTE: NOTE: This line in RemoteVSTServer::setParameter(int p, float v) in dssi-vst-server.cpp : + // + // " if (tv.tv_sec > m_lastGuiComms.tv_sec + 10) " + // + // explains an observation that after ten seconds, the server automatically clears the expected number to 0. + // TODO: Now MusE should forget about all the VST fifo events past ten+ (?) seconds. Add event timestamps... + // You can't send any 'new' values until either you a): send all the expected events or b): wait ten seconds. + // (Because the server simply ignores the 'expected' messages.) + // + // Well, at least here are the fifos. Try this ... + OscControlFifo* cfifo = _oscif.oscFifo(cport); + if(cfifo) + { + OscControlValue cv; + //cv.idx = cport; + cv.value = value; + if(cfifo->put(cv)) + { + fprintf(stderr, "DssiSynthIF::oscControl: fifo overflow: in control number:%ld\n", cport); + } + } + + //const DSSI_Descriptor* dssi = synth->dssi; + //const LADSPA_Descriptor* ld = dssi->LADSPA_Plugin; + + ciMidiCtl2LadspaPort ip = synth->port2MidiCtlMap.find(cport); + if(ip != synth->port2MidiCtlMap.end()) + { + // TODO: TODO: Update midi MusE's midi controller knobs, sliders, boxes etc with a call to the midi port's setHwCtrlState() etc. + // But first we need a ladspa2MidiValue() function! ... + // + // + //float val = ladspa2MidiValue(ld, i, ?, ?); + + } + +#if 0 + int port = argv[0]->i; + LADSPA_Data value = argv[1]->f; + + if (port < 0 || port > instance->plugin->descriptor->LADSPA_Plugin->PortCount) { + fprintf(stderr, "MusE: OSC: %s port number (%d) is out of range\n", + instance->friendly_name, port); + return 0; + } + if (instance->pluginPortControlInNumbers[port] == -1) { + fprintf(stderr, "MusE: OSC: %s port %d is not a control in\n", + instance->friendly_name, port); + return 0; + } + pluginControlIns[instance->pluginPortControlInNumbers[port]] = value; + if (verbose) { + printf("MusE: OSC: %s port %d = %f\n", + instance->friendly_name, port, value); + } +#endif + return 0; + } + +/* +//--------------------------------------------------------- +// oscExiting +//--------------------------------------------------------- + +int DssiSynthIF::oscExiting(lo_arg**) + { + //printf("not impl.: oscExiting\n"); + + // The gui is gone now, right? + _guiVisible = false; + + //const DSSI_Descriptor* dssi = synth->dssi; + //const LADSPA_Descriptor* ld = dssi->LADSPA_Plugin; + //if(ld->deactivate) + // ld->deactivate(handle); + + if (uiOscPath == 0) { + printf("DssiSynthIF::oscExiting(): no uiOscPath\n"); + return 1; + } + char uiOscGuiPath[strlen(uiOscPath)+6]; + + sprintf(uiOscGuiPath, "%s/%s", uiOscPath, "quit"); + #ifdef DSSI_DEBUG + printf("DssiSynthIF::oscExiting(): sending quit to uiOscGuiPath:%s\n", uiOscGuiPath); + #endif + + lo_send(uiTarget, uiOscGuiPath, ""); + +#if 0 + int i; + + if (verbose) { + printf("MusE: OSC: got exiting notification for instance %d\n", + instance->number); + } + + if (instance->plugin) { + + // !!! No, this isn't safe -- plugins deactivated in this way + // would still be included in a run_multiple_synths call unless + // we re-jigged the instance array at the same time -- leave it + // for now + //if (instance->plugin->descriptor->LADSPA_Plugin->deactivate) { + // instance->plugin->descriptor->LADSPA_Plugin->deactivate + // (instanceHandles[instance->number]); + // } + // Leave this flag though, as we need it to determine when to exit + instance->inactive = 1; + } + + // Do we have any plugins left running? + + for (i = 0; i < instance_count; ++i) { + if (!instances[i].inactive) + return 0; + } + + if (verbose) { + printf("MusE: That was the last remaining plugin, exiting...\n"); + } + exiting = 1; +#endif + return 0; + } +*/ + +//--------------------------------------------------------- +// oscMidi +//--------------------------------------------------------- + +int DssiSynthIF::oscMidi(int a, int b, int c) + { + //int a = argv[0]->m[1]; + //int b = argv[0]->m[2]; + //int c = argv[0]->m[3]; + + if (a == ME_NOTEOFF) { + a = ME_NOTEON; + c = 0; + } + int channel = 0; // TODO: ?? + + int port = synti->midiPort(); + + //MidiEvent event(0, channel, a, b, c); + + if(port != -1) + { + MidiPlayEvent event(0, port, channel, a, b, c); + + #ifdef DSSI_DEBUG + fprintf(stderr, "DssiSynthIF::oscMidi midi event chn:%d a:%d b:%d\n", event.channel(), event.dataA(), event.dataB()); + #endif + + midiPorts[port].sendEvent(event); + } + + //synti->playMidiEvent(&event); // TODO + // + //MidiDevice* md = dynamic_cast<MidiDevice*>(synti); + //if(md) + // md->putEvent(event); + // + //synti->putEvent(event); + // + + return 0; + } + +//--------------------------------------------------------- +// oscConfigure +//--------------------------------------------------------- + +int DssiSynthIF::oscConfigure(const char *key, const char *value) + { + //const char *key = (const char *)&argv[0]->s; + //const char *value = (const char *)&argv[1]->s; + + // This is pretty much the simplest legal implementation of + // configure in a DSSI host. + + // The host has the option to remember the set of (key,value) + // pairs associated with a particular instance, so that if it + // wants to restore the "same" instance on another occasion it can + // just call configure() on it for each of those pairs and so + // restore state without any input from a GUI. Any real-world GUI + // host will probably want to do that. This host doesn't have any + // concept of restoring an instance from one run to the next, so + // we don't bother remembering these at all. + + #ifdef DSSI_DEBUG + printf("DssiSynthIF::oscConfigure synth name:%s key:%s value:%s\n", synti->name().toLatin1().constData(), key, value); + #endif + + // Add or modify the configuration map item. + synti->_stringParamMap.set(key, value); + + if (!strncmp(key, DSSI_RESERVED_CONFIGURE_PREFIX, + strlen(DSSI_RESERVED_CONFIGURE_PREFIX))) { + fprintf(stderr, "MusE: OSC: UI for plugin '%s' attempted to use reserved configure key \"%s\", ignoring\n", + //synti->name().toAscii().data(), key); + synti->name().toLatin1().constData(), key); + + return 0; + } + + if (!synth->dssi->configure) + return 0; + + char* message = synth->dssi->configure(handle, key, value); + if (message) { + printf("MusE: on configure '%s' '%s', plugin '%s' returned error '%s'\n", + //key, value, synti->name().toAscii().data(), message); + key, value, synti->name().toLatin1().constData(), message); + + free(message); + } + + // also call back on UIs for plugins other than the one + // that requested this: + // if (n != instance->number && instances[n].uiTarget) { + // lo_send(instances[n].uiTarget, + // instances[n].ui_osc_configure_path, "ss", key, value); + // } + + // configure invalidates bank and program information, so + // we should do this again now: + queryPrograms(); + return 0; + } +#endif // OSC_SUPPORT + +//--------------------------------------------------------- +// queryPrograms +//--------------------------------------------------------- + +void DssiSynthIF::queryPrograms() + { + for (std::vector<DSSI_Program_Descriptor>::const_iterator i = programs.begin(); + i != programs.end(); ++i) { + free((void*)(i->Name)); + } + programs.clear(); + + //if (!(synth->dssi->get_program && synth->dssi->select_program)) + if (!synth->dssi->get_program) + return; + + for (int i = 0;; ++i) { + const DSSI_Program_Descriptor* pd = synth->dssi->get_program(handle, i); + if (pd == 0) + break; + DSSI_Program_Descriptor d; + d.Name = strdup(pd->Name); + d.Program = pd->Program; + d.Bank = pd->Bank; + programs.push_back(d); + } + } + +//--------------------------------------------------------- +// getPatchName +//--------------------------------------------------------- + +//QString DssiSynthIF::getPatchName(int, int prog) +const char* DssiSynthIF::getPatchName(int /*chan*/, int prog, MType /*type*/, bool /*drum*/) + { + unsigned program = prog & 0x7f; + int lbank = (prog >> 8) & 0xff; + int hbank = (prog >> 16) & 0xff; + + if (lbank == 0xff) + lbank = 0; + if (hbank == 0xff) + hbank = 0; + unsigned bank = (hbank << 8) + lbank; + + for (std::vector<DSSI_Program_Descriptor>::const_iterator i = programs.begin(); + i != programs.end(); ++i) { + if (i->Bank == bank && i->Program ==program) + return i->Name; + } + return "?"; + } + +//--------------------------------------------------------- +// populatePatchPopup +//--------------------------------------------------------- + +//void DssiSynthIF::populatePatchPopup(QMenu* menu, int) +void DssiSynthIF::populatePatchPopup(QMenu* menu, int /*ch*/, MType /*type*/, 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<DSSI_Program_Descriptor>::const_iterator i = programs.begin(); + i != programs.end(); ++i) { + int bank = i->Bank; + int prog = i->Program; + int id = (bank << 16) + prog; + + QAction *act = menu->addAction(QString(i->Name)); + act->setData(id); + } + } + +int DssiSynthIF::getControllerInfo(int id, const char** name, int* ctrl, int* min, int* max, int* initval) +{ + int controlPorts = synth->_controlInPorts; + if(id >= controlPorts) + //if(id >= midiCtl2PortMap.size()) + return 0; + + const DSSI_Descriptor* dssi = synth->dssi; + const LADSPA_Descriptor* ld = dssi->LADSPA_Plugin; + + // Hmm, <map> has a weird [] operator. Would it work? + // For now just use duplicate code found in ::init() + //iMidiCtl2LadspaPort ip = midiCtl2PortMap[id]; + //int ctlnum = ip->first; + //int k = ip->second; + + int i = synth->pIdx[id]; + //int i = synth->pIdx[k]; + + //ladspaDefaultValue(ld, i, &controls[id].val); + + #ifdef DSSI_DEBUG + printf("DssiSynthIF::getControllerInfo control port:%d port idx:%d name:%s\n", id, i, ld->PortNames[i]); + #endif + + int ctlnum = DSSI_NONE; + if(dssi->get_midi_controller_for_port) + ctlnum = dssi->get_midi_controller_for_port(handle, i); + + + // No controller number? Give it one. + if(ctlnum == DSSI_NONE) + { + // TODO: If neccesary... choose non-existing numbers... + //for(int k = 0; k < controlPorts; ++k) + //{ + // int i = synth->pIdx[k]; + //} + + // Simple but flawed solution: Start them at 0x60000 + 0x2000 = 0x62000. Max NRPN number is 0x3fff. + ctlnum = CTRL_NRPN14_OFFSET + 0x2000 + id; + } + else + { + #ifdef DSSI_DEBUG + printf("DssiSynthIF::getControllerInfo ctlnum:%d\n", ctlnum); + #endif + + int c = ctlnum; + // Can be both CC and NRPN! Prefer CC over NRPN. + if(DSSI_IS_CC(ctlnum)) + { + #ifdef DSSI_DEBUG + printf("DssiSynthIF::getControllerInfo is CC control\n"); + #endif + + ctlnum = DSSI_CC_NUMBER(c); + + #ifdef DSSI_DEBUG + if(DSSI_IS_NRPN(ctlnum)) + printf("DssiSynthIF::getControllerInfo is also NRPN control. Using CC.\n"); + #endif + } + else + if(DSSI_IS_NRPN(ctlnum)) + { + #ifdef DSSI_DEBUG + printf("DssiSynthIF::getControllerInfo is NRPN control\n"); + #endif + + ctlnum = DSSI_NRPN_NUMBER(c) + CTRL_NRPN14_OFFSET; + } + } + + int def = CTRL_VAL_UNKNOWN; + if(ladspa2MidiControlValues(ld, i, ctlnum, min, max, &def)) + *initval = def; + else + *initval = CTRL_VAL_UNKNOWN; + + #ifdef DSSI_DEBUG + printf("DssiSynthIF::getControllerInfo passed ctlnum:%d min:%d max:%d initval:%d\n", ctlnum, *min, *max, *initval); + #endif + + *ctrl = ctlnum; + *name = ld->PortNames[i]; + return ++id; + + /* + // ...now create midi controllers for ports which did not define them ... + for(int k = 0; k < controlPorts; ++k) + { + int i = synth->pIdx[k]; + //controls[k].val = ladspaDefaultValue(ld, i); + ladspaDefaultValue(ld, i, &controls[k].val); + + printf("DssiSynthIF::getControllerInfo #2 control port:%d port idx:%d name:%s\n", k, i, ld->PortNames[i]); + + if(!dssi->get_midi_controller_for_port || (dssi->get_midi_controller_for_port(handle, i) == DSSI_NONE)) + { + int ctlnum; + //printf("DssiSynthIF::getControllerInfo #2 midi controller number:%d\n", ctlnum); + printf("DssiSynthIF::getControllerInfo #2 creating MidiController number:%d\n", ctlnum); + MidiController* mc = ladspa2MidiController(ld, i, ctlnum); + // Add to MidiInstrument controller list. + if(mc) + { + printf("DssiSynthIF::getControllerInfo #2 adding MidiController to instrument\n"); + ((MidiInstrument*)synti)->controller()->add(mc); + } + } + else + { + + } + } + */ + +} + +int DssiSynthIF::channels() const +{ + return synth->_outports > MAX_CHANNELS ? MAX_CHANNELS : synth->_outports; +} + +int DssiSynthIF::totalOutChannels() const +{ + return synth->_outports; +} + +int DssiSynthIF::totalInChannels() const +{ + return synth->_inports; +} + +//-------------------------------- +// Methods for PluginIBase: +//-------------------------------- + +bool DssiSynthIF::on() const { return true; } // Synth is not part of a rack plugin chain. Always on. +void DssiSynthIF::setOn(bool /*val*/) { } +int DssiSynthIF::pluginID() { return (synth && synth->dssi) ? synth->dssi->LADSPA_Plugin->UniqueID : 0; } +int DssiSynthIF::id() { return 0; } // Synth is not part of a rack plugin chain. Always 0. +QString DssiSynthIF::pluginLabel() const { return (synth && synth->dssi) ? QString(synth->dssi->LADSPA_Plugin->Label) : QString(); } +QString DssiSynthIF::name() const { return synti->name(); } +AudioTrack* DssiSynthIF::track() { return (AudioTrack*)synti; } +void DssiSynthIF::enableController(int i, bool v) { controls[i].enCtrl = v; } +bool DssiSynthIF::controllerEnabled(int i) const { return controls[i].enCtrl; } +bool DssiSynthIF::controllerEnabled2(int i) const { return controls[i].en2Ctrl; } +void DssiSynthIF::updateControllers() { } +void DssiSynthIF::writeConfiguration(int /*level*/, Xml& /*xml*/) { } +bool DssiSynthIF::readConfiguration(Xml& /*xml*/, bool /*readPreset*/) { return false; } +int DssiSynthIF::parameters() const { return synth ? synth->_controlInPorts : 0; } +void DssiSynthIF::setParam(int i, double val) { setParameter(i, val); } +double DssiSynthIF::param(int i) const { return getParameter(i); } +const char* DssiSynthIF::paramName(int i) { return (synth && synth->dssi) ? synth->dssi->LADSPA_Plugin->PortNames[i] : 0; } +//LADSPA_PortRangeHint DssiSynthIF::range(int i) { return (synth && synth->dssi) ? synth->dssi->LADSPA_Plugin->PortRangeHints[i] : 0; } +LADSPA_PortRangeHint DssiSynthIF::range(int i) { return synth->dssi->LADSPA_Plugin->PortRangeHints[i]; } + + +#else //DSSI_SUPPORT +void initDSSI() {} +#endif + |