From b4338aea07e6866c622fa550a895aaa3bd244db6 Mon Sep 17 00:00:00 2001 From: "Tim E. Real" Date: Wed, 28 Apr 2010 00:30:24 +0000 Subject: See ChangeLog --- muse/ChangeLog | 15 + muse/configure.ac | 82 ++- muse/muse/Makefile.am | 2 + muse/muse/app.cpp | 39 +- muse/muse/arranger/tlist.cpp | 26 + muse/muse/audiotrack.cpp | 112 ++-- muse/muse/dssihost.cpp | 1096 ++++++++++++++++++++++---------- muse/muse/dssihost.h | 112 +++- muse/muse/mixer/rack.cpp | 19 +- muse/muse/plugin.cpp | 1442 ++++++++++++++++++++++++++++++++---------- muse/muse/plugin.h | 166 +++-- muse/muse/song.cpp | 10 +- muse/muse/songfile.cpp | 9 + muse/muse/synth.cpp | 96 ++- muse/muse/synth.h | 59 +- muse/muse/ticksynth.cpp | 6 +- muse/muse/track.h | 1 + muse/muse/vst.cpp | 11 +- muse/muse/vst.h | 6 +- 19 files changed, 2495 insertions(+), 814 deletions(-) diff --git a/muse/ChangeLog b/muse/ChangeLog index c7f8a2bb..5a0c5b0c 100644 --- a/muse/ChangeLog +++ b/muse/ChangeLog @@ -1,3 +1,18 @@ +27.04.2010 + * Changed: Building: Separate --enable-dssi --enable-osc configure options. (T356) + --enable-dssi (Enable dssi synths AND special handling of dssi-vst ladspa effect plugins. No guis if by itself.) and + --enable-osc (Enable dssi guis. Useless by itself at the moment). + Use both for best features. + * Feature: If dssi and osc support are enabled, audio track effects rack dssi-vst plugins can now show their NATIVE guis. (T356) + * Fixed: Storing dssi synth states. Remembers current program, all configure strings, and control settings - for current program ONLY. (T356) + - With dssi-vst synths, some better are than others at remembering program. + - Todo: Ignore unwanted dssi-vst config strings/values like "guiVisible" "" (gives non-fatal error on reload). + * Fixed: Dssi native synths (hexter, LTS etc.) should actually work now. Their gui controls operate the sounds now. (T356) + - Todo: If using large audio buffers (Jack), my 'one-at-a-time-per-audio-process' osc control fifo streams kinda suck, + with a delay in their processing. Want to try MULTIPLE 'run's or 'run-multiple's PER audio process, for SAMPLE accuracy and + better quicker message processing. Just need to add a timestamp to my osc fifo items, then all forms of change - whether + control, midi event, or program - have a timestamp with which we can SPLIT UP the runs into 'runs between changes'. + * Tip: Currently a patch to dssi-vst is required for their guis to update properly. (For now, ask about this on our lists.) 21.04.2010 * Fixed: Shortcut for moving between tracks in arranger. Ctrl+Up/Down (rj) 19.04.2010 diff --git a/muse/configure.ac b/muse/configure.ac index 9decee23..e79419f2 100644 --- a/muse/configure.ac +++ b/muse/configure.ac @@ -585,15 +585,64 @@ fi AM_CONDITIONAL(VST_SUPPORT, test x$vst_possible = xyes -a x$vst_requested = xyes) +################### +### OSC support ### +################### + +osc_requested=no +LO_LIB_FOUND="no" +OSC="no" +AC_ARG_ENABLE(osc,[ --enable-osc build OSC support. (disabled by default) + --enable-dssi is also recommended! ], + [ if test x$enable_osc = xyes ; then + osc_requested=yes; + fi + ] +) + +if test x$osc_requested = xyes ; then + AC_MSG_CHECKING([support for OSC]) +## AC_CHECK_LIB(lo, lo_send, LO_LIB_FOUND="yes", LO_LIB_FOUND="no") +## PKG_CHECK_MODULES(LO, lo >= 0.2, LO_LIB_FOUND="yes", LO_LIB_FOUND="no") + PKG_CHECK_MODULES(LO,liblo, [ LO_LIB_FOUND=yes ], [ LO_LIB_FOUND=no ]) + if test x$LO_LIB_FOUND = xno; then + AC_MSG_ERROR([Library liblo from The Lightweight OSC Implementation is required]) + fi +fi + +if test x$LO_LIB_FOUND = xyes ; then + LO_CFLAGS="$LO_CFLAGS" + ##LO_LIBS="$LO_LIBS" + LO_LIBS="-llo" + OSC="yes" + AC_SUBST(LO_CFLAGS) + AC_SUBST(LO_LIBS) + AC_DEFINE(OSC_SUPPORT, 1, [support for OSC]) +fi + +AM_CONDITIONAL(OSC_SUPPORT, test x$LO_LIB_FOUND = xyes) + +## LO_LIBS="-llo" +## AC_SUBST(LO_CFLAGS) +## AC_SUBST(LO_LIBS) + +## PKG_CHECK_MODULES(LO, lo >= 0.23, +## true, +## AC_MSG_ERROR([need liblo >= 0.23 ]) +## ) + + + ################### ### dssi plugins ### ################### dssi_requested=no -LO_LIB_FOUND="no" +## LO_LIB_FOUND="no" DSSI_H_FOUND="no" DSSI="no" -AC_ARG_ENABLE(dssi,[ --enable-dssi build DSSI + DSSI-Vst support. (disabled by default) ], +AC_ARG_ENABLE(dssi,[ --enable-dssi build DSSI + DSSI-Vst support. (disabled by default) + --enable-osc is also recommended! ], [ if test x$enable_dssi = xyes ; then dssi_requested=yes; fi @@ -603,31 +652,33 @@ AC_ARG_ENABLE(dssi,[ --enable-dssi build DSSI + DSSI-Vst support. (di if test x$dssi_requested = xyes ; then AC_MSG_CHECKING([support for DSSI + DSSI-Vst plugins]) -## AC_CHECK_LIB(lo, lo_send, LO_LIB_FOUND="yes", LO_LIB_FOUND="no") -## PKG_CHECK_MODULES(LO, lo >= 0.2, LO_LIB_FOUND="yes", LO_LIB_FOUND="no") - PKG_CHECK_MODULES(LO,liblo, [ LO_LIB_FOUND=yes ], [ LO_LIB_FOUND=no ]) - if test x$LO_LIB_FOUND = xno; then - AC_MSG_ERROR([Library liblo from The Lightweight OSC Implementation is required]) - fi + ## AC_CHECK_LIB(lo, lo_send, LO_LIB_FOUND="yes", LO_LIB_FOUND="no") + ## PKG_CHECK_MODULES(LO, lo >= 0.2, LO_LIB_FOUND="yes", LO_LIB_FOUND="no") +## PKG_CHECK_MODULES(LO,liblo, [ LO_LIB_FOUND=yes ], [ LO_LIB_FOUND=no ]) +## if test x$LO_LIB_FOUND = xno; then +## AC_MSG_ERROR([Library liblo from The Lightweight OSC Implementation is required]) +## fi AC_CHECK_HEADER(dssi.h, DSSI_FOUND="yes", DSSI_FOUND="no") -## PKG_CHECK_MODULES(DSSI [ DSSI_FOUND=yes ], [ DSSI_FOUND=no ]) + ## PKG_CHECK_MODULES(DSSI [ DSSI_FOUND=yes ], [ DSSI_FOUND=no ]) if test x$DSSI_FOUND = xno; then AC_MSG_ERROR([Header file dssi.h from DSSI (Disposable Soft Synth Interface) is required]) fi fi -if test x$LO_LIB_FOUND = xyes -a x$DSSI_FOUND = xyes ; then - LO_CFLAGS="$LO_CFLAGS" +## if test x$LO_LIB_FOUND = xyes -a x$DSSI_FOUND = xyes ; then +if test x$DSSI_FOUND = xyes ; then +## LO_CFLAGS="$LO_CFLAGS" ##LO_LIBS="$LO_LIBS" - LO_LIBS="-llo" +## LO_LIBS="-llo" DSSI="yes" - AC_SUBST(LO_CFLAGS) - AC_SUBST(LO_LIBS) +## AC_SUBST(LO_CFLAGS) +## AC_SUBST(LO_LIBS) AC_DEFINE(DSSI_SUPPORT, 1, [support for DSSI + DSSI-Vst plugins]) fi -AM_CONDITIONAL(DSSI_SUPPORT, test x$LO_LIB_FOUND = xyes -a x$DSSI_FOUND = xyes) +## AM_CONDITIONAL(DSSI_SUPPORT, test x$LO_LIB_FOUND = xyes -a x$DSSI_FOUND = xyes) +AM_CONDITIONAL(DSSI_SUPPORT, test x$DSSI_FOUND = xyes) ## LO_LIBS="-llo" ## AC_SUBST(LO_CFLAGS) @@ -720,6 +771,7 @@ AC_MSG_NOTICE([ MusE configured LASH support: $LASH_FOUND + OSC support: $OSC DSSI support: $DSSI FluidSynth: $muse_enable_fluidlib diff --git a/muse/muse/Makefile.am b/muse/muse/Makefile.am index df5a552f..bbf6ae44 100644 --- a/muse/muse/Makefile.am +++ b/muse/muse/Makefile.am @@ -33,6 +33,8 @@ dist_muse_SOURCES = \ ticksynth.h ticksynth.cpp \ vst.h vst.cpp \ dssihost.h dssihost.cpp \ + osc.cpp osc.h \ + stringparam.cpp stringparam.h \ synth.h synth.cpp \ plugin.cpp plugin.h \ mtc.cpp mtc.h \ diff --git a/muse/muse/app.cpp b/muse/muse/app.cpp index dac144c4..c896ff51 100644 --- a/muse/muse/app.cpp +++ b/muse/muse/app.cpp @@ -131,6 +131,9 @@ extern void exitDummyAudio(); extern void initVST_fst_init(); extern void initVST(); extern void initDSSI(); +// p3.3.39 +extern void initOSC(); +extern void exitOSC(); #ifdef HAVE_LASH #include @@ -402,12 +405,25 @@ bool MusE::seqStart() //audioWriteback->start(0); } */ + int pfprio = 0; int midiprio = 0; + + // NOTE: realTimeScheduling can be true (gotten using jack_is_realtime()), + // while the determined realTimePriority can be 0. + // realTimePriority is gotten using pthread_getschedparam() on the client thread + // in JackAudioDevice::realtimePriority() which is a bit flawed - it reports there's no RT... if(realTimeScheduling) { - if(realTimePriority < 5) - printf("MusE: WARNING: Recommend setting audio realtime priority to at least 5!\n"); + //if(realTimePriority < 5) + // printf("MusE: WARNING: Recommend setting audio realtime priority to a higher value!\n"); + /* + if(realTimePriority == 0) + { + pfprio = 1; + midiprio = 2; + } + else if(realTimePriority == 1) { pfprio = 2; @@ -444,11 +460,17 @@ bool MusE::seqStart() midiprio = 6; } else + */ { - pfprio = realTimePriority - 5; + //pfprio = realTimePriority - 5; + // p3.3.40 + pfprio = realTimePriority + 1; + //midiprio = realTimePriority - 2; // p3.3.37 - midiprio = realTimePriority + 1; + //midiprio = realTimePriority + 1; + // p3.3.40 + midiprio = realTimePriority + 2; } } @@ -1832,6 +1854,10 @@ void MusE::closeEvent(QCloseEvent*) printf("Muse: Exiting Dsp\n"); AL::exitDsp(); + if(debugMsg) + printf("Muse: Exiting OSC\n"); + exitOSC(); + qApp->quit(); } @@ -2367,7 +2393,7 @@ static void usage(const char* prog, const char* txt) fprintf(stderr, " -a no audio\n"); //fprintf(stderr, " -P n set real time priority to n (default: 50)\n"); fprintf(stderr, " -P n set audio driver real time priority to n (Dummy only, default 40. Else fixed by Jack.)\n"); - fprintf(stderr, " -Y n force midi real time priority to n (default: audio driver prio +1)\n"); + fprintf(stderr, " -Y n force midi real time priority to n (default: audio driver prio +2)\n"); fprintf(stderr, " -p don't load LADSPA plugins\n"); #ifdef ENABLE_PYTHON fprintf(stderr, " -y enable Python control support\n"); @@ -2626,6 +2652,9 @@ int main(int argc, char* argv[]) if(loadDSSI) initDSSI(); + // p3.3.39 + initOSC(); + initIcons(); initMetronome(); diff --git a/muse/muse/arranger/tlist.cpp b/muse/muse/arranger/tlist.cpp index 1be0a240..e3c1a680 100644 --- a/muse/muse/arranger/tlist.cpp +++ b/muse/muse/arranger/tlist.cpp @@ -37,6 +37,11 @@ #include "event.h" #include "midiedit/drummap.h" #include "synth.h" +#include "config.h" + +#ifdef DSSI_SUPPORT +#include "dssihost.h" +#endif extern QPopupMenu* populateAddSynth(QWidget* parent, QObject* obj = 0, const char* slot = 0); @@ -605,6 +610,16 @@ void TList::oportPropertyPopupMenu(Track* t, int x, int y) p->setItemEnabled(0, synth->hasGui()); p->setItemChecked(0, synth->guiVisible()); + #ifndef OSC_SUPPORT + #ifdef DSSI_SUPPORT + if(dynamic_cast(synth->sif())) + { + p->setItemChecked(0, false); + p->setItemEnabled(0, false); + } + #endif + #endif + int n = p->exec(mapToGlobal(QPoint(x, y)), 0); if (n == 0) { bool show = !synth->guiVisible(); @@ -627,6 +642,17 @@ void TList::oportPropertyPopupMenu(Track* t, int x, int y) p->setItemEnabled(0, port->hasGui()); p->setItemChecked(0, port->guiVisible()); + #ifndef OSC_SUPPORT + #ifdef DSSI_SUPPORT + MidiDevice* dev = port->device(); + if(dev && dev->isSynti() && (dynamic_cast(((SynthI*)dev)->sif()))) + { + p->setItemChecked(0, false); + p->setItemEnabled(0, false); + } + #endif + #endif + int n = p->exec(mapToGlobal(QPoint(x, y)), 0); if (n == 0) { bool show = !port->guiVisible(); diff --git a/muse/muse/audiotrack.cpp b/muse/muse/audiotrack.cpp index fd6ba76a..ffc37ab8 100644 --- a/muse/muse/audiotrack.cpp +++ b/muse/muse/audiotrack.cpp @@ -211,47 +211,51 @@ Part* AudioTrack::newPart(Part*, bool /*clone*/) //--------------------------------------------------------- void AudioTrack::addPlugin(PluginI* plugin, int idx) +{ + if (plugin == 0) + { + PluginI* oldPlugin = (*_efxPipe)[idx]; + if (oldPlugin) + { + oldPlugin->setID(-1); + oldPlugin->setTrack(0); + + int controller = oldPlugin->parameters(); + for (int i = 0; i < controller; ++i) { - if (plugin == 0) { - PluginI* oldPlugin = (*_efxPipe)[idx]; - if (oldPlugin) { - - oldPlugin->setID(-1); - oldPlugin->setTrack(0); - - int controller = oldPlugin->parameters(); - for (int i = 0; i < controller; ++i) { - int id = genACnum(idx, i); - removeController(id); - } - } - } - efxPipe()->insert(plugin, idx); - if (plugin) { - plugin->setID(idx); - plugin->setTrack(this); - - int controller = plugin->parameters(); - for (int i = 0; i < controller; ++i) { - int id = genACnum(idx, i); - const char* name = plugin->paramName(i); - float min, max; - plugin->range(i, &min, &max); - CtrlValueType t = plugin->valueType(); - CtrlList* cl = new CtrlList(id); - cl->setRange(min, max); - cl->setName(QString(name)); - cl->setValueType(t); - LADSPA_PortRangeHint range = plugin->range(i); - if(LADSPA_IS_HINT_TOGGLED(range.HintDescriptor)) - cl->setMode(CtrlList::DISCRETE); - else - cl->setMode(CtrlList::INTERPOLATE); - cl->setCurVal(plugin->param(i)); - addController(cl); - } - } + int id = genACnum(idx, i); + removeController(id); } + } + } + efxPipe()->insert(plugin, idx); + if (plugin) + { + plugin->setID(idx); + plugin->setTrack(this); + + int controller = plugin->parameters(); + for (int i = 0; i < controller; ++i) + { + int id = genACnum(idx, i); + const char* name = plugin->paramName(i); + float min, max; + plugin->range(i, &min, &max); + CtrlValueType t = plugin->valueType(); + CtrlList* cl = new CtrlList(id); + cl->setRange(min, max); + cl->setName(QString(name)); + cl->setValueType(t); + LADSPA_PortRangeHint range = plugin->range(i); + if(LADSPA_IS_HINT_TOGGLED(range.HintDescriptor)) + cl->setMode(CtrlList::DISCRETE); + else + cl->setMode(CtrlList::INTERPOLATE); + cl->setCurVal(plugin->param(i)); + addController(cl); + } + } +} //--------------------------------------------------------- // addAuxSend @@ -924,13 +928,14 @@ void AudioTrack::readAuxSend(Xml& xml) bool AudioTrack::readProperties(Xml& xml, const QString& tag) { - if (tag == "plugin") { + if (tag == "plugin") + { int rackpos; for(rackpos = 0; rackpos < PipelineDepth; ++rackpos) { if(!(*_efxPipe)[rackpos]) - break; - } + break; + } if(rackpos < PipelineDepth) { PluginI* pi = new PluginI(); @@ -940,10 +945,10 @@ bool AudioTrack::readProperties(Xml& xml, const QString& tag) delete pi; else (*_efxPipe)[rackpos] = pi; - } + } else printf("can't load plugin - plugin rack is already full\n"); - } + } else if (tag == "auxSend") readAuxSend(xml); else if (tag == "prefader") @@ -1009,6 +1014,25 @@ bool AudioTrack::readProperties(Xml& xml, const QString& tag) return false; } +//--------------------------------------------------------- +// showPendingPluginNativeGuis +// This is needed because OSC needs all tracks with plugins to be already +// added to their track lists so it can find them and show their native guis. +//--------------------------------------------------------- + +void AudioTrack::showPendingPluginNativeGuis() +{ + for(int idx = 0; idx < PipelineDepth; ++idx) + { + PluginI* p = (*_efxPipe)[idx]; + if(!p) + continue; + + if(p->isShowNativeGuiPending()) + p->showNativeGui(true); + } +} + //--------------------------------------------------------- // mapRackPluginsToControllers //--------------------------------------------------------- diff --git a/muse/muse/dssihost.cpp b/muse/muse/dssihost.cpp index d952d9f5..ec2e94a7 100644 --- a/muse/muse/dssihost.cpp +++ b/muse/muse/dssihost.cpp @@ -19,7 +19,6 @@ //============================================================================= #include "config.h" - #ifdef DSSI_SUPPORT // Turn on debugging messages @@ -32,14 +31,14 @@ #include #include #include -#include +//#include //#include //#include #include -#include +//#include #include #include -#include +//#include #include "dssihost.h" #include "synth.h" @@ -47,6 +46,8 @@ #include "jackaudio.h" #include "midi.h" #include "midiport.h" +#include "stringparam.h" +#include "plugin.h" //#include "al/al.h" //#include "al/xml.h" #include "xml.h" @@ -58,7 +59,9 @@ #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; @@ -130,13 +133,13 @@ int DssiSynthIF::oscUpdate(lo_arg **argv) 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. - */ + // 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()); @@ -148,7 +151,7 @@ int DssiSynthIF::oscUpdate(lo_arg **argv) DSSI_PROJECT_DIRECTORY_KEY, museProject.ascii()); #if 0 - /* Send current bank/program (-FIX- another race...) */ + // Send current bank/program (-FIX- another race...) if (instance->pendingProgramChange < 0) { unsigned long bank = instance->currentBank; unsigned long program = instance->currentProgram; @@ -158,13 +161,13 @@ int DssiSynthIF::oscUpdate(lo_arg **argv) } } - /* Send control ports */ + // 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 */ + // Avoid overloading the GUI if there are lots and lots of ports if ((i+1) % 50 == 0) usleep(300000); } @@ -283,6 +286,7 @@ int oscMessageHandler(const char* path, const char* types, lo_arg** argv, return instance->oscExiting(argv); return oscDebugHandler(path, types, argv, argc, data, user_data); } +*/ //--------------------------------------------------------- // scanDSSILib @@ -291,14 +295,14 @@ int oscMessageHandler(const char* path, const char* types, lo_arg** argv, static void scanDSSILib(const QFileInfo& fi) { //void* handle = dlopen(fi.filePath().toAscii().data(), RTLD_NOW); - void* handle = dlopen(fi.filePath().ascii(), RTLD_NOW); - //void* handle = dlopen(fi.absFilePath().ascii(), RTLD_NOW); + void* handle = dlopen(fi.filePath().latin1(), RTLD_NOW); + //void* handle = dlopen(fi.absFilePath().latin1(), RTLD_NOW); if (handle == 0) { fprintf(stderr, "scanDSSILib: dlopen(%s) failed: %s\n", //fi.filePath().toAscii().data(), dlerror()); - fi.filePath().ascii(), dlerror()); - //fi.absFilePath().ascii(), dlerror()); + fi.filePath().latin1(), dlerror()); + //fi.absFilePath().latin1(), dlerror()); return; } @@ -316,7 +320,7 @@ static void scanDSSILib(const QFileInfo& fi) "Are you sure this is a DSSI plugin file?\n", //fi.filePath().toAscii().data(), fi.filePath().ascii(), - //fi.absFilePath().ascii(), + //fi.absFilePath().latin1(), txt); dlclose(handle); @@ -358,7 +362,8 @@ static void scanDSSILib(const QFileInfo& fi) { Synth* s = *is; //#ifdef DSSI_DEBUG - // fprintf(stderr, "scanDSSILib: name:%s listname:%s lib:%s listlib:%s\n", label.ascii(), s->name().ascii(), fi.baseName(true).ascii(), s->baseName().ascii()); + // fprintf(stderr, "scanDSSILib: name:%s listname:%s lib:%s listlib:%s\n", + // label.latin1(), s->name().latin1(), fi.baseName(true).latin1(), s->baseName().latin1()); //#endif if(s->name() == label && s->baseName() == fi.baseName(true)) @@ -367,15 +372,14 @@ static void scanDSSILib(const QFileInfo& fi) if(is != synthis.end()) continue; - //DssiSynth* s = new DssiSynth(&fi, label); - //DssiSynth* s = new DssiSynth(fi, label); - DssiSynth* s = new DssiSynth(fi, label, QString(descr->LADSPA_Plugin->Name), QString(descr->LADSPA_Plugin->Maker), QString()); + DssiSynth* s = new DssiSynth(fi, descr); if(debugMsg) { - fprintf(stderr, "scanDSSILib: name:%s listname:%s lib:%s listlib:%s\n", label.ascii(), s->name().ascii(), fi.baseName(true).ascii(), s->baseName().ascii()); + fprintf(stderr, "scanDSSILib: name:%s listname:%s lib:%s listlib:%s\n", + label.latin1(), s->name().latin1(), fi.baseName(true).latin1(), s->baseName().latin1()); int ai = 0, ao = 0, ci = 0, co = 0; - for(int pt = 0; pt < descr->LADSPA_Plugin->PortCount; ++pt) + 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)) @@ -397,8 +401,10 @@ static void scanDSSILib(const QFileInfo& fi) } else { + // NOTE: Just a test //QFileInfo ffi(fi); //plugins.add(&ffi, LADSPA_Descriptor_Function(NULL), descr->LADSPA_Plugin, false); + //plugins.add(&ffi, descr, false); } } } @@ -413,7 +419,7 @@ static void scanDSSIDir(const QString& s) { if(debugMsg) //printf("scan DSSI plugin dir <%s>\n", s.toAscii().data()); - printf("scanDSSIDir: scan DSSI plugin dir <%s>\n", s.ascii()); + printf("scanDSSIDir: scan DSSI plugin dir <%s>\n", s.latin1()); #ifdef __APPLE__ QDir pluginDir(s, QString("*.dylib"), QDir::Unsorted, QDir::Files); @@ -433,7 +439,7 @@ static void scanDSSIDir(const QString& s) for(unsigned int i = 0; i < list.count(); ++i) { if(debugMsg) - printf("scanDSSIDir: found %s\n", (s + QString("/") + list[i]).ascii()); + printf("scanDSSIDir: found %s\n", (s + QString("/") + list[i]).latin1()); QFileInfo fi(s + QString("/") + list[i]); scanDSSILib(fi); @@ -456,7 +462,7 @@ void initDSSI() const char* p = dssiPath; //QString pth = QString(dssiPath) + QString(":") + QString(ladspaPath); - //const char* p = pth.ascii(); + //const char* p = pth.latin1(); while (*p != '\0') { const char* pe = p; while (*pe != ':' && *pe != '\0') @@ -474,18 +480,233 @@ void initDSSI() 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) +//--------------------------------------------------------- - 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); +DssiSynth::DssiSynth(const QFileInfo& fi, const DSSI_Descriptor* d) : + //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.baseName(true) == QString("dssi-vst") && !config.vstInPlace)) + _inPlaceCapable = false; +} - lo_server_thread_add_method(serverThread, 0, 0, oscMessageHandler, 0); - lo_server_thread_start(serverThread); +DssiSynth::~DssiSynth() +{ + +} + +//--------------------------------------------------------- +// createSIF +//--------------------------------------------------------- + +SynthIF* DssiSynth::createSIF(SynthI* synti) +{ + if (_instances == 0) + { + //handle = dlopen(info.filePath().toAscii().data(), RTLD_NOW); + handle = dlopen(info.filePath().latin1(), RTLD_NOW); + //handle = dlopen(info.absFilePath().latin1(), RTLD_NOW); + + if (handle == 0) + { + fprintf(stderr, "DssiSynth::createSIF dlopen(%s) failed: %s\n", + //info.filePath().toAscii().data(), dlerror()); + info.filePath().latin1(), dlerror()); + //info.absFilePath().latin1(), 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().latin1(), + //info.absFilePath().latin1(), + + 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.baseName(true) == 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.latin1()); + 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().latin1()); +// snprintf(oscUrl, 1024, "%s/%s/%s", url, info.baseName().latin1(), synti->name().latin1()); + //QString guiPath(info.path() + "/" + info.baseName()); + QString guiPath(info.dirPath() + "/" + info.baseName()); + QDir guiDir(guiPath, "*", QDir::Unsorted, QDir::Files); + _hasGui = guiDir.exists(); + + //sif->initGui(); + + return sif; +} //--------------------------------------------------------- // guiVisible @@ -493,7 +714,11 @@ void initDSSI() bool DssiSynthIF::guiVisible() const { - return _guiVisible; + //return _guiVisible; + #ifdef OSC_SUPPORT + return _oscif.oscGuiVisible(); + #endif + return false; } //--------------------------------------------------------- @@ -502,10 +727,17 @@ bool DssiSynthIF::guiVisible() const 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; @@ -522,7 +754,7 @@ void DssiSynthIF::showGui(bool v) printf("DssiSynthIF::showGui(): No QProcess or process not running. Starting gui...\n"); #endif - startGui(); + initGui(); } //for (int i = 0; i < 5; ++i) { @@ -544,6 +776,7 @@ void DssiSynthIF::showGui(bool v) lo_send(uiTarget, uiOscGuiPath, ""); _guiVisible = v; + */ } //--------------------------------------------------------- @@ -574,6 +807,10 @@ bool DssiSynthIF::init(DssiSynth* s) const LADSPA_Descriptor* ld = dssi->LADSPA_Plugin; handle = ld->instantiate(ld, sampleRate); + #ifdef OSC_SUPPORT + _oscif.oscSetSynthIF(this); + #endif + queryPrograms(); int inports = synth->_inports; @@ -605,8 +842,8 @@ bool DssiSynthIF::init(DssiSynth* s) } } - int controlPorts = synth->_controller; - int controlOutPorts = synth->_controllerOut; + int controlPorts = synth->_controlInPorts; + int controlOutPorts = synth->_controlOutPorts; if(controlPorts != 0) controls = new Port[controlPorts]; @@ -620,12 +857,17 @@ bool DssiSynthIF::init(DssiSynth* s) 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 @@ -689,8 +931,6 @@ bool DssiSynthIF::init(DssiSynth* s) for (int k = 0; k < controlOutPorts; ++k) { int i = synth->opIdx[k]; - //controls[k].val = ladspaDefaultValue(ld, i); - ladspaDefaultValue(ld, i, &controlsOut[k].val); #ifdef DSSI_DEBUG printf("DssiSynthIF::init control output port:%d port idx:%d name:%s\n", k, i, ld->PortNames[i]); @@ -699,6 +939,9 @@ bool DssiSynthIF::init(DssiSynth* s) // p3.3.39 Removed. /* + //controls[k].val = ladspaDefaultValue(ld, i); + ladspaDefaultValue(ld, i, &controlsOut[k].val); + // This code is duplicated in ::getControllerInfo() // @@ -770,14 +1013,38 @@ bool DssiSynthIF::init(DssiSynth* s) if (ld->activate) ld->activate(handle); - if (dssi->configure) { - char *rv = dssi->configure(handle, DSSI_PROJECT_DIRECTORY_KEY, - //song->projectPath().toAscii().data()); - museProject.ascii()); + // Set current configuration values. + if(dssi->configure) + { + char *rv = dssi->configure(handle, DSSI_PROJECT_DIRECTORY_KEY, + museProject.latin1()); //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); + } + } + } - if (rv) - fprintf(stderr, "MusE: Warning: plugin doesn't like project directory: \"%s\"\n", 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; } @@ -792,15 +1059,28 @@ DssiSynthIF::DssiSynthIF(SynthI* s) printf("DssiSynthIF::DssiSynthIF\n"); #endif - _guiVisible = false; - uiTarget = 0; - uiOscShowPath = 0; - uiOscControlPath = 0; - uiOscConfigurePath = 0; - uiOscProgramPath = 0; - uiOscPath = 0; + 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; + //guiQProc = 0; + audioInBuffers = 0; audioOutBuffers = 0; } @@ -855,6 +1135,7 @@ DssiSynthIF::~DssiSynthIF() } } + /* //if (guiPid != -1) // kill(guiPid, SIGHUP); if(guiQProc) @@ -871,6 +1152,20 @@ DssiSynthIF::~DssiSynthIF() //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) @@ -878,7 +1173,7 @@ DssiSynthIF::~DssiSynthIF() // if(audioInBuffers[i]) // delete[] audioInBuffers[i]; //} - for(int i = 0; i < synth->_inports; ++i) + for(unsigned long i = 0; i < synth->_inports; ++i) { if(audioInBuffers[i]) free(audioInBuffers[i]); @@ -893,7 +1188,7 @@ DssiSynthIF::~DssiSynthIF() // if(audioOutBuffers[i]) // delete[] audioOutBuffers[i]; //} - for(int i = 0; i < synth->_outports; ++i) + for(unsigned long i = 0; i < synth->_outports; ++i) { if(audioOutBuffers[i]) free(audioOutBuffers[i]); @@ -902,19 +1197,52 @@ DssiSynthIF::~DssiSynthIF() } if(controls) - delete controls; + delete[] controls; if(controlsOut) - delete controlsOut; + delete[] controlsOut; +} + +//--------------------------------------------------------- +// getParameter +//--------------------------------------------------------- + +float DssiSynthIF::getParameter(unsigned long n) +{ + 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(int, float) - { - } +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 @@ -963,31 +1291,54 @@ void DssiSynthIF::write(int level, Xml& xml) const printf("support for vst chunks not compiled in!\n"); #endif - // p3.3.39 Store the state of program and bank and all input control values, but only if VSTSAVE above didn't do it already! + /* + // 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->_controller) + 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); - const unsigned long len = synth->_controller * fs + 2 * uls; // Controllers and bank + program. - unsigned long prog = 0; // TODO: To be added to class. Dummy zero for now. - unsigned long bnk = 0; + // 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++, "\n", len+9); // + "PARAMSAVE" length + 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 program... - void* p = &prog; + // 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)) @@ -998,8 +1349,9 @@ void DssiSynthIF::write(int level, Xml& xml) const xml.nput("%02x ", ((char*)(p))[j] & 0xff); ++i; } - // Store bank... - p = &bnk; + + // Store program... + p = &prog; for(int j = 0; j < uls; ++j) { if(i && ((i % 16) == 0)) @@ -1010,8 +1362,9 @@ void DssiSynthIF::write(int level, Xml& xml) const xml.nput("%02x ", ((char*)(p))[j] & 0xff); ++i; } + // Store controls... - for(int c = 0; c < synth->_controller; ++c) + for(unsigned long c = 0; c < synth->_controlInPorts; ++c) { float v = controls[c].val; p = &v; @@ -1032,6 +1385,14 @@ void DssiSynthIF::write(int level, Xml& xml) const } } */ + + // 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()); + } } //--------------------------------------------------------- @@ -1106,8 +1467,18 @@ bool DssiSynthIF::processEvent(const MidiPlayEvent& e, snd_seq_event_t* event) 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; } @@ -1129,8 +1500,19 @@ bool DssiSynthIF::processEvent(const MidiPlayEvent& e, snd_seq_event_t* event) 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; } @@ -1264,9 +1646,9 @@ bool DssiSynthIF::processEvent(const MidiPlayEvent& e, snd_seq_event_t* event) } //int num = ip->first; - int k = ip->second; + unsigned long k = ip->second; - int i = synth->pIdx[k]; + unsigned long i = synth->pIdx[k]; int ctlnum = DSSI_NONE; if(dssi->get_midi_controller_for_port) @@ -1276,7 +1658,7 @@ bool DssiSynthIF::processEvent(const MidiPlayEvent& e, snd_seq_event_t* event) if(ctlnum == DSSI_NONE) { // Sanity check. - if(k > synth->_controller) + if(k > synth->_controlInPorts) return false; // TODO: If necessary... choose non-existing numbers... @@ -1292,7 +1674,7 @@ bool DssiSynthIF::processEvent(const MidiPlayEvent& e, snd_seq_event_t* event) else { #ifdef DSSI_DEBUG - printf("DssiSynthIF::processEvent plugin requests DSSI-style ctlnum:%x(h) %d(d) be mapped to control port:%d...\n", ctlnum, ctlnum, i); + 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; @@ -1327,13 +1709,15 @@ bool DssiSynthIF::processEvent(const MidiPlayEvent& e, snd_seq_event_t* event) #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:%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; @@ -1438,9 +1822,9 @@ bool DssiSynthIF::processEvent(const MidiPlayEvent& e, snd_seq_event_t* event) // Event not filled. return false; } - // p3.3.39 Read the state of program and bank and all input control values. - // TODO: Needs to be better. See write(). /* + // 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")) { @@ -1451,16 +1835,21 @@ bool DssiSynthIF::processEvent(const MidiPlayEvent& e, snd_seq_event_t* event) unsigned long dlen = e.len() - 9; // Minus "PARAMSAVE" if(dlen > 0) { - if(dlen < 2 * sizeof(unsigned long)) - printf("DssiSynthIF::processEvent Error: PARAMSAVE data length does not include at least program and bank!\n"); + //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 { - //unsigned long* const ulp = (unsigned long*)(e.data() + 9); // After "PARAMSAVE" - //unsigned long prog = ulp[0]; - //unsigned long bnk = ulp[1]; - // TODO: TODO: Save them in class and set plugin program and bank. + // 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 * sizeof(unsigned long); // After the program and bank. + dlen -= (2 + 2 * sizeof(unsigned long)); // After the version major and minor, bank and program. if(dlen > 0) { @@ -1468,13 +1857,14 @@ bool DssiSynthIF::processEvent(const MidiPlayEvent& e, snd_seq_event_t* event) printf("DssiSynthIF::processEvent Error: PARAMSAVE float data length not integral multiple of float size!\n"); else { - const int n = dlen / sizeof(float); - if(n != synth->_controller) - printf("DssiSynthIF::processEvent Warning: PARAMSAVE number of floats:%d != number of controls:%d\n", n, synth->_controller); + 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); - float* const fp = (float*)(e.data() + 9 + 2 * sizeof(unsigned long)); // After "PARAMSAVE" and progam and bank. + // 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(int i = 0; i < synth->_controller && i < n; ++i) + for(unsigned long i = 0; i < synth->_controlInPorts && i < n; ++i) { const float v = fp[i]; controls[i].val = v; @@ -1610,6 +2000,42 @@ iMPEvent DssiSynthIF::getData(MidiPort* /*mp*/, MPEventList* el, iMPEvent /*i*/, ++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) @@ -1649,7 +2075,7 @@ iMPEvent DssiSynthIF::getData(MidiPort* /*mp*/, MPEventList* el, iMPEvent /*i*/, // All ports must be connected to something! // First, copy the given input buffers to our local input buffers. - int np, k; + 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); @@ -1733,6 +2159,13 @@ iMPEvent DssiSynthIF::getData(MidiPort* /*mp*/, MPEventList* el, iMPEvent /*i*/, 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) { @@ -1769,9 +2202,10 @@ bool DssiSynthIF::putEvent(const MidiPlayEvent& ev) //--------------------------------------------------------- void DssiSynth::incInstances(int val) - { +{ _instances += val; - if (_instances == 0) { + if (_instances == 0) + { if (handle) { #ifdef DSSI_DEBUG @@ -1780,143 +2214,33 @@ void DssiSynth::incInstances(int val) dlclose(handle); } - dssi = 0; - df = 0; - } + 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(); } +} //--------------------------------------------------------- -// createSIF +// initGui //--------------------------------------------------------- - -SynthIF* DssiSynth::createSIF(SynthI* synti) +bool DssiSynthIF::initGui() { - if (_instances == 0) - { - //handle = dlopen(info.filePath().toAscii().data(), RTLD_NOW); - handle = dlopen(info.filePath().ascii(), RTLD_NOW); - //handle = dlopen(info.absFilePath().ascii(), RTLD_NOW); - - if (handle == 0) - { - fprintf(stderr, "DssiSynth::createSIF dlopen(%s) failed: %s\n", - //info.filePath().toAscii().data(), dlerror()); - info.filePath().ascii(), dlerror()); - //info.absFilePath().ascii(), 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().ascii(), - //info.absFilePath().ascii(), - - 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; - _controller = 0; - _controllerOut = 0; - const LADSPA_Descriptor* d = dssi->LADSPA_Plugin; - //#ifdef DSSI_DEBUG - // printf("DssiSynth::createSIF ladspa plugin PortCount:%lu\n", d->PortCount); - //#endif - - for (unsigned k = 0; k < d->PortCount; ++k) - { - LADSPA_PortDescriptor pd = d->PortDescriptors[k]; - - #ifdef DSSI_DEBUG - printf("DssiSynth::createSIF ladspa plugin Port:%d Name:%s descriptor:%x\n", k, d->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(-1); - } - else if (LADSPA_IS_PORT_CONTROL(pd)) - { - if (LADSPA_IS_PORT_INPUT(pd)) - { - rpIdx.push_back(_controller); - ++_controller; - pIdx.push_back(k); - } - else if (LADSPA_IS_PORT_OUTPUT(pd)) - { - rpIdx.push_back(-1); - ++_controllerOut; - opIdx.push_back(k); - } - } - } - } - } - if (dssi == 0) - { - //fprintf(stderr, "cannot found DSSI synti %s\n", _name.toAscii().data()); - fprintf(stderr, "cannot find DSSI synti %s\n", _name.ascii()); - dlclose(handle); - handle = 0; - df = 0; - return 0; - } - DssiSynthIF* sif = new DssiSynthIF(synti); - ++_instances; - sif->init(this); - - static char oscUrl[1024]; - //snprintf(oscUrl, 1024, "%s/%s", url, synti->name().toAscii().data()); - //snprintf(oscUrl, 1024, "%s/%s", url, synti->name().ascii()); - snprintf(oscUrl, 1024, "%s/%s/%s", url, info.baseName().ascii(), synti->name().ascii()); - //QString guiPath(info.path() + "/" + info.baseName()); - QString guiPath(info.dirPath() + "/" + info.baseName()); - QDir guiDir(guiPath, "*", QDir::Unsorted, QDir::Files); - _hasGui = guiDir.exists(); + #ifdef OSC_SUPPORT + return _oscif.oscInitGui(); + #endif - //sif->startGui(); + return true; - return sif; -} - -//--------------------------------------------------------- -// startGui -//--------------------------------------------------------- -bool DssiSynthIF::startGui() -{ + /* // Are we already running? We don't want to allow another process do we... if((guiQProc != 0) && (guiQProc->isRunning())) return true; @@ -1926,8 +2250,8 @@ bool DssiSynthIF::startGui() // static char oscUrl[1024]; //snprintf(oscUrl, 1024, "%s/%s", url, synti->name().toAscii().data()); - //snprintf(oscUrl, 1024, "%s/%s", url, synti->name().ascii()); - snprintf(oscUrl, 1024, "%s/%s/%s", url, synth->info.baseName().ascii(), synti->name().ascii()); + //snprintf(oscUrl, 1024, "%s/%s", url, synti->name().latin1()); + snprintf(oscUrl, 1024, "%s/%s/%s", url, synth->info.baseName().latin1(), synti->name().latin1()); //QString guiPath(info.path() + "/" + info.baseName()); QString guiPath(synth->info.dirPath() + "/" + synth->info.baseName()); @@ -1951,25 +2275,25 @@ bool DssiSynthIF::startGui() struct stat buf; //if (stat(gui.toAscii().data(), &buf)) { - if (stat(gui.ascii(), &buf)) { + if (stat(gui.latin1(), &buf)) { perror("stat failed"); continue; } #ifdef DSSI_DEBUG - fprintf(stderr, "DssiSynthIF::startGui %s %s %s %s %s\n", + fprintf(stderr, "DssiSynthIF::initGui %s %s %s %s\n", //fi.filePath().toAscii().data(), //fi.fileName().toAscii().data(), - fi.filePath().ascii(), - fi.fileName().ascii(), + fi.filePath().latin1(), + //fi.fileName().latin1(), oscUrl, - synth->info.filePath().ascii(), + synth->info.filePath().latin1(), //name().toAscii().data(), - synth->name().ascii()); + synth->name().latin1()); #endif if ((S_ISREG(buf.st_mode) || S_ISLNK(buf.st_mode)) && @@ -1995,13 +2319,13 @@ bool DssiSynthIF::startGui() guiQProc->addArgument(QString("channel 1")); #ifdef DSSI_DEBUG - fprintf(stderr, "DssiSynthIF::startGui starting QProcess\n"); + fprintf(stderr, "DssiSynthIF::initGui starting QProcess\n"); #endif if(guiQProc->start() == TRUE) { #ifdef DSSI_DEBUG - fprintf(stderr, "DssiSynthIF::startGui started QProcess\n"); + fprintf(stderr, "DssiSynthIF::initGui started QProcess\n"); #endif //guiPid = guiQProc->processIdentifier(); @@ -2009,44 +2333,39 @@ bool DssiSynthIF::startGui() else { - /* - execlp( - //fi.filePath().toAscii().data(), - //fi.fileName().toAscii().data(), - fi.filePath().ascii(), - fi.fileName().ascii(), + // execlp( + // fi.filePath().toAscii().data(), + // fi.fileName().toAscii().data(), + // fi.filePath().latin1(), + // fi.fileName().latin1(), - oscUrl, + // oscUrl, - //info.filePath().toAscii().data(), - //name().toAscii().data(), - synth->info.filePath().ascii(), - synth->name().ascii(), + // info.filePath().toAscii().data(), + // name().toAscii().data(), + // synth->info.filePath().latin1(), + // synth->name().latin1(), - "channel 1", (void*)0); - */ + // "channel 1", (void*)0); fprintf(stderr, "exec %s %s %s %s failed: %s\n", - //fi.filePath().toAscii().data(), - //fi.fileName().toAscii().data(), - fi.filePath().ascii(), - fi.fileName().ascii(), - + // fi.filePath().toAscii().data(), + // fi.fileName().toAscii().data(), + fi.filePath().latin1(), + fi.fileName().latin1(), oscUrl, - - //name().toAscii().data(), - synth->name().ascii(), - + // name().toAscii().data(), + synth->name().latin1(), 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); + // exit(1); } #ifdef DSSI_DEBUG - fprintf(stderr, "DssiSynthIF::startGui after QProcess\n"); + fprintf(stderr, "DssiSynthIF::initGui after QProcess\n"); #endif } } @@ -2056,29 +2375,134 @@ bool DssiSynthIF::startGui() else { printf("%s: no dir for dssi gui found: %s\n", //name().toAscii().data(), guiPath.toAscii().data()); - synth->name().ascii(), guiPath.ascii()); + synth->name().latin1(), guiPath.latin1()); //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.latin1()); // 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(lo_arg** argv) +int DssiSynthIF::oscProgram(unsigned long program, unsigned long bank) { //int bank = argv[0]->i; //int program = argv[1]->i; - int bank = argv[0]->i & 0xff; - int program = argv[1]->i & 0xff; 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) @@ -2110,31 +2534,32 @@ int DssiSynthIF::oscProgram(lo_arg** argv) // oscControl //--------------------------------------------------------- -int DssiSynthIF::oscControl(lo_arg** argv) +int DssiSynthIF::oscControl(unsigned long port, float value) { - int port = argv[0]->i; - LADSPA_Data value = argv[1]->f; + //int port = argv[0]->i; + //LADSPA_Data value = argv[1]->f; #ifdef DSSI_DEBUG - printf("DssiSynthIF::oscControl received oscControl port:%d val:%f\n", port, value); + printf("DssiSynthIF::oscControl received oscControl port:%ld val:%f\n", port, value); #endif - //int controlPorts = synth->_controller; + //int controlPorts = synth->_controlInPorts; //if(port >= controlPorts) - if(port < 0 || port >= synth->rpIdx.size()) + //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:%d is out of range of index list size:%d\n", port, synth->rpIdx.size()); + fprintf(stderr, "DssiSynthIF::oscControl: port number:%ld is out of range of index list size:%d\n", port, synth->rpIdx.size()); return 0; } // Convert from DSSI port number to control input port index. - int cport = synth->rpIdx[port]; + unsigned long cport = synth->rpIdx[port]; - if(cport == -1) + if((int)cport == -1) { - fprintf(stderr, "DssiSynthIF::oscControl: port number:%d is not a control input\n", port); + fprintf(stderr, "DssiSynthIF::oscControl: port number:%ld is not a control input\n", port); return 0; } @@ -2144,16 +2569,38 @@ int DssiSynthIF::oscControl(lo_arg** argv) // 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 ! + // 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). + // 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: May need FIFOs on each control(!) so that the control changes get sent one per process cycle! + // 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; + //const DSSI_Descriptor* dssi = synth->dssi; + //const LADSPA_Descriptor* ld = dssi->LADSPA_Plugin; ciMidiCtl2LadspaPort ip = synth->port2MidiCtlMap.find(cport); if(ip != synth->port2MidiCtlMap.end()) @@ -2189,6 +2636,7 @@ int DssiSynthIF::oscControl(lo_arg** argv) return 0; } +/* //--------------------------------------------------------- // oscExiting //--------------------------------------------------------- @@ -2228,20 +2676,19 @@ int DssiSynthIF::oscExiting(lo_arg**) 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 */ + // !!! 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? */ + // Do we have any plugins left running? for (i = 0; i < instance_count; ++i) { if (!instances[i].inactive) @@ -2255,16 +2702,18 @@ int DssiSynthIF::oscExiting(lo_arg**) #endif return 0; } +*/ //--------------------------------------------------------- // oscMidi //--------------------------------------------------------- -int DssiSynthIF::oscMidi(lo_arg** argv) +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]; + //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; @@ -2302,44 +2751,47 @@ int DssiSynthIF::oscMidi(lo_arg** argv) // oscConfigure //--------------------------------------------------------- -int DssiSynthIF::oscConfigure(lo_arg** argv) +int DssiSynthIF::oscConfigure(const char *key, const char *value) { - if (!synth->dssi->configure) - return 0; - - const char *key = (const char *)&argv[0]->s; - const char *value = (const char *)&argv[1]->s; + //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. */ + // 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. */ + // 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().ascii(), key, value); + printf("DssiSynthIF::oscConfigure synth name:%s key:%s value:%s\n", synti->name().latin1(), 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().ascii(), key); + synti->name().latin1(), 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().ascii(), message); + key, value, synti->name().latin1(), message); free(message); } @@ -2351,11 +2803,12 @@ int DssiSynthIF::oscConfigure(lo_arg** argv) // instances[n].ui_osc_configure_path, "ss", key, value); // } - /* configure invalidates bank and program information, so - we should do this again now: */ + // configure invalidates bank and program information, so + // we should do this again now: queryPrograms(); return 0; } +#endif // OSC_SUPPORT //--------------------------------------------------------- // queryPrograms @@ -2369,7 +2822,8 @@ void DssiSynthIF::queryPrograms() } programs.clear(); - if (!(synth->dssi->get_program && synth->dssi->select_program)) + //if (!(synth->dssi->get_program && synth->dssi->select_program)) + if (!synth->dssi->get_program) return; for (int i = 0;; ++i) { @@ -2416,6 +2870,10 @@ const char* DssiSynthIF::getPatchName(int /*chan*/, int prog, MType /*type*/, bo //void DssiSynthIF::populatePatchPopup(QMenu* menu, int) void DssiSynthIF::populatePatchPopup(QPopupMenu* 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::const_iterator i = programs.begin(); @@ -2432,7 +2890,7 @@ void DssiSynthIF::populatePatchPopup(QPopupMenu* menu, int /*ch*/, MType /*type* int DssiSynthIF::getControllerInfo(int id, const char** name, int* ctrl, int* min, int* max, int* initval) { - int controlPorts = synth->_controller; + int controlPorts = synth->_controlInPorts; if(id >= controlPorts) //if(id >= midiCtl2PortMap.size()) return 0; diff --git a/muse/muse/dssihost.h b/muse/muse/dssihost.h index 526d793f..deaa87b9 100644 --- a/muse/muse/dssihost.h +++ b/muse/muse/dssihost.h @@ -21,16 +21,29 @@ #ifndef __DSSIHOST_H__ #define __DSSIHOST_H__ +#include "config.h" + #include +#include +#include +#ifdef OSC_SUPPORT #include +#include "osc.h" +#endif + #include #include #include "midictrl.h" #include "synth.h" -#include "plugin.h" +#include "stringparam.h" +//#include "plugin.h" + +#define DSSI_PARAMSAVE_VERSION_MAJOR 0 +#define DSSI_PARAMSAVE_VERSION_MINOR 1 + struct _DSSI; class DssiPluginIF; @@ -48,31 +61,34 @@ class DssiSynth : public Synth { void* handle; const DSSI_Descriptor* dssi; DSSI_Descriptor_Function df; - std::vector pIdx; // Control input index to port number. - std::vector opIdx; // Control output index to port number. This is sometimes a latency port and...? - std::vector iIdx; // Audio input index to port number. - std::vector oIdx; // Audio output index to port number. - std::vector iUsedIdx; // This is for audio input ports during process to tell whether an audio input port was used by any input routes. - int _inports, _outports, _controller, _controllerOut; - std::vector rpIdx; // Port number to control input index. Item is -1 if it's not a control input. + unsigned long _portCount, _inports, _outports, _controlInPorts, _controlOutPorts; + std::vector pIdx; // Control input index to port number. + std::vector opIdx; // Control output index to port number. This is sometimes a latency port and...? + std::vector iIdx; // Audio input index to port number. + std::vector oIdx; // Audio output index to port number. + std::vector iUsedIdx; // During process, tells whether an audio input port was used by any input routes. + std::vector rpIdx; // Port number to control input index. Item is -1 if it's not a control input. + //unsigned long* rpIdx; MidiCtl2LadspaPortMap midiCtl2PortMap; // Maps midi controller numbers to DSSI port numbers. MidiCtl2LadspaPortMap port2MidiCtlMap; // Maps DSSI port numbers to midi controller numbers. bool _hasGui; + bool _inPlaceCapable; public: //DssiSynth(const QFileInfo* fi, QString l) : Synth(fi, l) { //DssiSynth(const QFileInfo& fi, QString l) : Synth(fi, l) { - DssiSynth(const QFileInfo& fi, QString label, QString descr, QString maker, QString ver) : - Synth(fi, label, descr, maker, ver) { - df = 0; - handle = 0; - dssi = 0; - _hasGui = false; - } - virtual ~DssiSynth() { - //delete label; - } - virtual void incInstances(int val); + //DssiSynth(const QFileInfo& fi, QString label, QString descr, QString maker, QString ver) : + // Synth(fi, label, descr, maker, ver) { + // rpIdx = 0; + // df = 0; + // handle = 0; + // dssi = 0; + // _hasGui = false; + // } + //DssiSynth(const QFileInfo& fi, QString label, QString descr, QString maker, QString ver); + DssiSynth(const QFileInfo&, const DSSI_Descriptor*); + virtual ~DssiSynth(); + virtual void incInstances(int); //virtual void* instantiate(); @@ -80,7 +96,13 @@ class DssiSynth : public Synth { //virtual SynthIF* createSIF(); friend class DssiSynthIF; - float defaultValue(int); + //float defaultValue(int); // Not required + unsigned long inPorts() const { return _inports; } + unsigned long outPorts() const { return _outports; } + unsigned long inControls() const { return _controlInPorts; } + unsigned long outControls() const { return _controlOutPorts; } + + unsigned long inControlPortIdx(unsigned long i) { return pIdx[i]; } }; //--------------------------------------------------------- @@ -90,7 +112,7 @@ class DssiSynth : public Synth { class DssiSynthIF : public SynthIF { - bool _guiVisible; + //bool _guiVisible; DssiSynth* synth; LADSPA_Handle handle; @@ -98,12 +120,19 @@ class DssiSynthIF : public SynthIF Port* controls; Port* controlsOut; - void* uiTarget; - char* uiOscShowPath; - char* uiOscControlPath; - char* uiOscConfigurePath; - char* uiOscProgramPath; - char* uiOscPath; + //unsigned long _curBank; + //unsigned long _curProgram; + + #ifdef OSC_SUPPORT + OscDssiIF _oscif; + #endif + + //void* uiTarget; + //char* uiOscShowPath; + //char* uiOscControlPath; + //char* uiOscConfigurePath; + //char* uiOscProgramPath; + //char* uiOscPath; std::vector programs; void queryPrograms(); @@ -114,7 +143,7 @@ class DssiSynthIF : public SynthIF protected: //int guiPid; - QProcess* guiQProc; + //QProcess* guiQProc; public: DssiSynthIF(SynthI* s); @@ -122,7 +151,11 @@ class DssiSynthIF : public SynthIF virtual ~DssiSynthIF(); - virtual bool startGui(); + virtual DssiSynth* dssiSynth() { return synth; } + virtual SynthI* dssiSynthI() { return synti; } + + virtual bool initGui(); + virtual void guiHeartBeat(); virtual bool guiVisible() const; virtual void showGui(bool v); virtual bool hasGui() const { return synth->_hasGui; } @@ -159,19 +192,34 @@ class DssiSynthIF : public SynthIF //virtual void write(Xml& xml) const; virtual void write(int level, Xml& xml) const; - virtual void setParameter(int idx, float value); + virtual float getParameter(unsigned long /*idx*/); + virtual void setParameter(unsigned long /*idx*/, float /*value*/); //virtual int getControllerInfo(int, const char**, int*, int*, int*) { return 0; } virtual int getControllerInfo(int, const char**, int*, int*, int*, int*); bool init(DssiSynth* s); - int oscUpdate(lo_arg**); + //StringParamMap& stringParameters() { return synti->stringParameters(); } + + #ifdef OSC_SUPPORT + OscDssiIF& oscIF() { return _oscif; } + /* int oscProgram(lo_arg**); int oscControl(lo_arg**); - int oscExiting(lo_arg**); int oscMidi(lo_arg**); int oscConfigure(lo_arg**); + int oscUpdate(lo_arg**); + //int oscExiting(lo_arg**); + */ + + int oscProgram(unsigned long /*prog*/, unsigned long /*bank*/); + int oscControl(unsigned long /*dssiPort*/, float /*val*/); + int oscMidi(int /*a*/, int /*b*/, int /*c*/); + int oscConfigure(const char */*key*/, const char */*val*/); + int oscUpdate(); + //int oscExiting(); + #endif friend class DssiSynth; }; diff --git a/muse/muse/mixer/rack.cpp b/muse/muse/mixer/rack.cpp index e94acb97..30b3ff0d 100644 --- a/muse/muse/mixer/rack.cpp +++ b/muse/muse/mixer/rack.cpp @@ -24,6 +24,7 @@ #include "gconfig.h" #include "plugin.h" #include "filedialog.h" +#include "config.h" //--------------------------------------------------------- // class RackSlot @@ -149,16 +150,19 @@ void EffectRack::menuRequested(QListBoxItem* it, const QPoint& pt) mute = pipe->isOn(idx); } - enum { NEW, CHANGE, UP, DOWN, REMOVE, BYPASS, SHOW, SAVE }; + //enum { NEW, CHANGE, UP, DOWN, REMOVE, BYPASS, SHOW, SAVE }; + enum { NEW, CHANGE, UP, DOWN, REMOVE, BYPASS, SHOW, SHOW_NATIVE, SAVE }; QPopupMenu* menu = new QPopupMenu; menu->insertItem(QIconSet(*upIcon), tr("move up"), UP, UP); menu->insertItem(QIconSet(*downIcon), tr("move down"), DOWN, DOWN); menu->insertItem(tr("remove"), REMOVE, REMOVE); menu->insertItem(tr("bypass"), BYPASS, BYPASS); menu->insertItem(tr("show gui"), SHOW, SHOW); + menu->insertItem(tr("show native gui"), SHOW_NATIVE, SHOW_NATIVE); menu->setItemChecked(BYPASS, !pipe->isOn(idx)); menu->setItemChecked(SHOW, pipe->guiVisible(idx)); + menu->setItemChecked(SHOW_NATIVE, pipe->nativeGuiVisible(idx)); if (pipe->empty(idx)) { menu->insertItem(tr("new"), NEW, NEW); @@ -167,6 +171,7 @@ void EffectRack::menuRequested(QListBoxItem* it, const QPoint& pt) menu->setItemEnabled(REMOVE, false); menu->setItemEnabled(BYPASS, false); menu->setItemEnabled(SHOW, false); + menu->setItemEnabled(SHOW_NATIVE, false); menu->setItemEnabled(SAVE, false); } else { @@ -176,8 +181,14 @@ void EffectRack::menuRequested(QListBoxItem* it, const QPoint& pt) menu->setItemEnabled(UP, false); if (idx == (PipelineDepth-1)) menu->setItemEnabled(DOWN, false); + if(!pipe->isDssiPlugin(idx)) + menu->setItemEnabled(SHOW_NATIVE, false); } + #ifndef OSC_SUPPORT + menu->setItemEnabled(SHOW_NATIVE, false); + #endif + int sel = menu->exec(pt, 1); delete menu; if (sel == -1) @@ -230,6 +241,12 @@ void EffectRack::menuRequested(QListBoxItem* it, const QPoint& pt) pipe->showGui(idx, flag); break; } + case SHOW_NATIVE: + { + bool flag = !pipe->nativeGuiVisible(idx); + pipe->showNativeGui(idx, flag); + break; + } case UP: if (idx > 0) { setCurrentItem(idx-1); diff --git a/muse/muse/plugin.cpp b/muse/muse/plugin.cpp index ac06c5ab..4d6113ff 100644 --- a/muse/muse/plugin.cpp +++ b/muse/muse/plugin.cpp @@ -49,7 +49,10 @@ #include "audio.h" #include "al/dsp.h" -#define PLUGIN_DEBUGIN 0 +#include "config.h" + +// Turn on debugging messages. +//#define PLUGIN_DEBUGIN PluginList plugins; @@ -92,13 +95,15 @@ bool ladspa2MidiControlValues(const LADSPA_Descriptor* plugin, int port, int ctl //bool isint = desc & LADSPA_HINT_INTEGER; MidiController::ControllerType t = midiControllerType(ctlnum); - if(PLUGIN_DEBUGIN) - printf("ladspa2MidiControlValues: ctlnum:%d ladspa port:%d has default?:%d default:%f\n", ctlnum, port, hasdef, fdef); + #ifdef PLUGIN_DEBUGIN + printf("ladspa2MidiControlValues: ctlnum:%d ladspa port:%d has default?:%d default:%f\n", ctlnum, port, hasdef, fdef); + #endif if(desc & LADSPA_HINT_TOGGLED) { - if(PLUGIN_DEBUGIN) - printf("ladspa2MidiControlValues: has LADSPA_HINT_TOGGLED\n"); + #ifdef PLUGIN_DEBUGIN + printf("ladspa2MidiControlValues: has LADSPA_HINT_TOGGLED\n"); + #endif *min = 0; *max = 1; @@ -109,16 +114,18 @@ bool ladspa2MidiControlValues(const LADSPA_Descriptor* plugin, int port, int ctl float m = 1.0; if(desc & LADSPA_HINT_SAMPLE_RATE) { - if(PLUGIN_DEBUGIN) - printf("ladspa2MidiControlValues: has LADSPA_HINT_SAMPLE_RATE\n"); + #ifdef PLUGIN_DEBUGIN + printf("ladspa2MidiControlValues: has LADSPA_HINT_SAMPLE_RATE\n"); + #endif m = float(sampleRate); } if(desc & LADSPA_HINT_BOUNDED_BELOW) { - if(PLUGIN_DEBUGIN) - printf("ladspa2MidiControlValues: has LADSPA_HINT_BOUNDED_BELOW\n"); + #ifdef PLUGIN_DEBUGIN + printf("ladspa2MidiControlValues: has LADSPA_HINT_BOUNDED_BELOW\n"); + #endif fmin = range.LowerBound * m; } @@ -127,8 +134,9 @@ bool ladspa2MidiControlValues(const LADSPA_Descriptor* plugin, int port, int ctl if(desc & LADSPA_HINT_BOUNDED_ABOVE) { - if(PLUGIN_DEBUGIN) - printf("ladspa2MidiControlValues: has LADSPA_HINT_BOUNDED_ABOVE\n"); + #ifdef PLUGIN_DEBUGIN + printf("ladspa2MidiControlValues: has LADSPA_HINT_BOUNDED_ABOVE\n"); + #endif fmax = range.UpperBound * m; } @@ -143,8 +151,9 @@ bool ladspa2MidiControlValues(const LADSPA_Descriptor* plugin, int port, int ctl int ctlmn = 0; int ctlmx = 127; - if(PLUGIN_DEBUGIN) - printf("ladspa2MidiControlValues: port min:%f max:%f \n", fmin, fmax); + #ifdef PLUGIN_DEBUGIN + printf("ladspa2MidiControlValues: port min:%f max:%f \n", fmin, fmax); + #endif //bool isneg = (fmin < 0.0); bool isneg = (imin < 0); @@ -200,8 +209,9 @@ bool ladspa2MidiControlValues(const LADSPA_Descriptor* plugin, int port, int ctl // Is it an integer control? if(desc & LADSPA_HINT_INTEGER) { - if(PLUGIN_DEBUGIN) - printf("ladspa2MidiControlValues: has LADSPA_HINT_INTEGER\n"); + #ifdef PLUGIN_DEBUGIN + printf("ladspa2MidiControlValues: has LADSPA_HINT_INTEGER\n"); + #endif // If the upper or lower limit is beyond the controller limits, just scale the whole range to fit. // We could get fancy by scaling only the negative or positive domain, or each one separately, but no... @@ -247,8 +257,9 @@ bool ladspa2MidiControlValues(const LADSPA_Descriptor* plugin, int port, int ctl *def = (int)lrint(fdef) + bias; - if(PLUGIN_DEBUGIN) - printf("ladspa2MidiControlValues: setting default:%d\n", *def); + #ifdef PLUGIN_DEBUGIN + printf("ladspa2MidiControlValues: setting default:%d\n", *def); + #endif return hasdef; } @@ -274,22 +285,25 @@ float midi2LadspaValue(const LADSPA_Descriptor* plugin, int port, int ctlnum, in //bool isint = desc & LADSPA_HINT_INTEGER; MidiController::ControllerType t = midiControllerType(ctlnum); - if(PLUGIN_DEBUGIN) - printf("midi2LadspaValue: ctlnum:%d ladspa port:%d val:%d\n", ctlnum, port, val); + #ifdef PLUGIN_DEBUGIN + printf("midi2LadspaValue: ctlnum:%d ladspa port:%d val:%d\n", ctlnum, port, val); + #endif float m = 1.0; if(desc & LADSPA_HINT_SAMPLE_RATE) { - if(PLUGIN_DEBUGIN) - printf("midi2LadspaValue: has LADSPA_HINT_SAMPLE_RATE\n"); + #ifdef PLUGIN_DEBUGIN + printf("midi2LadspaValue: has LADSPA_HINT_SAMPLE_RATE\n"); + #endif m = float(sampleRate); } if(desc & LADSPA_HINT_BOUNDED_BELOW) { - if(PLUGIN_DEBUGIN) - printf("midi2LadspaValue: has LADSPA_HINT_BOUNDED_BELOW\n"); + #ifdef PLUGIN_DEBUGIN + printf("midi2LadspaValue: has LADSPA_HINT_BOUNDED_BELOW\n"); + #endif fmin = range.LowerBound * m; } @@ -298,8 +312,9 @@ float midi2LadspaValue(const LADSPA_Descriptor* plugin, int port, int ctlnum, in if(desc & LADSPA_HINT_BOUNDED_ABOVE) { - if(PLUGIN_DEBUGIN) - printf("midi2LadspaValue: has LADSPA_HINT_BOUNDED_ABOVE\n"); + #ifdef PLUGIN_DEBUGIN + printf("midi2LadspaValue: has LADSPA_HINT_BOUNDED_ABOVE\n"); + #endif fmax = range.UpperBound * m; } @@ -313,8 +328,9 @@ float midi2LadspaValue(const LADSPA_Descriptor* plugin, int port, int ctlnum, in if(desc & LADSPA_HINT_TOGGLED) { - if(PLUGIN_DEBUGIN) - printf("midi2LadspaValue: has LADSPA_HINT_TOGGLED\n"); + #ifdef PLUGIN_DEBUGIN + printf("midi2LadspaValue: has LADSPA_HINT_TOGGLED\n"); + #endif if(val > 0) return fmax; @@ -325,8 +341,9 @@ float midi2LadspaValue(const LADSPA_Descriptor* plugin, int port, int ctlnum, in int ctlmn = 0; int ctlmx = 127; - if(PLUGIN_DEBUGIN) - printf("midi2LadspaValue: port min:%f max:%f \n", fmin, fmax); + #ifdef PLUGIN_DEBUGIN + printf("midi2LadspaValue: port min:%f max:%f \n", fmin, fmax); + #endif //bool isneg = (fmin < 0.0); bool isneg = (imin < 0); @@ -391,9 +408,10 @@ float midi2LadspaValue(const LADSPA_Descriptor* plugin, int port, int ctlnum, in ret = fmin; if(ret > fmax) ret = fmax; - if(PLUGIN_DEBUGIN) - printf("midi2LadspaValue: has LADSPA_HINT_INTEGER returning:%f\n", ret); - + #ifdef PLUGIN_DEBUGIN + printf("midi2LadspaValue: has LADSPA_HINT_INTEGER returning:%f\n", ret); + #endif + return ret; } @@ -410,8 +428,9 @@ float midi2LadspaValue(const LADSPA_Descriptor* plugin, int port, int ctlnum, in float ret = normval * frng + fmin; - if(PLUGIN_DEBUGIN) - printf("midi2LadspaValue: float returning:%f\n", ret); + #ifdef PLUGIN_DEBUGIN + printf("midi2LadspaValue: float returning:%f\n", ret); + #endif return ret; } @@ -558,55 +577,278 @@ void ladspaControlRange(const LADSPA_Descriptor* plugin, int i, float* min, floa // Plugin //--------------------------------------------------------- -Plugin::Plugin(QFileInfo* f, - LADSPA_Descriptor_Function df, const LADSPA_Descriptor* d, bool ip) - : fi(*f), ladspa(df), plugin(d) +Plugin::Plugin(QFileInfo* f, const LADSPA_Descriptor* d, bool isDssi) +{ + _isDssi = isDssi; + #ifdef DSSI_SUPPORT + dssi_descr = NULL; + #endif + + fi = *f; + plugin = NULL; + ladspa = NULL; + _handle = 0; + _references = 0; + _instNo = 0; + _label = QString(d->Label); + _name = QString(d->Name); + _uniqueID = d->UniqueID; + _maker = QString(d->Maker); + _copyright = QString(d->Copyright); + + _portCount = d->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 = d->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(d->Properties); + + // By T356. Blacklist vst plugins in-place configurable for now. At one point they + // were working with in-place here, but not now, and RJ also reported they weren't working. + // Fixes problem with vst plugins not working or feeding back loudly. + // I can only think of two things that made them stop working: + // 1): I switched back from Jack-2 to Jack-1 + // 2): I changed winecfg audio to use Jack instead of ALSA. + // Will test later... + // Possibly the first one because under Mandriva2007.1 (Jack-1), no matter how hard I tried, + // the same problem existed. It may have been when using Jack-2 with Mandriva2009 that they worked. + // Apparently the plugins are lying about their in-place capability. + // Quote: + /* Property LADSPA_PROPERTY_INPLACE_BROKEN indicates that the plugin + may cease to work correctly if the host elects to use the same data + location for both input and output (see connect_port()). This + should be avoided as enabling this flag makes it impossible for + hosts to use the plugin to process audio `in-place.' */ + // Examination of all my ladspa and vst synths and effects plugins showed only one - + // EnsembleLite (EnsLite VST) has the flag set, but it is a vst synth and is not involved here! + // Yet many (all?) ladspa vst effect plugins exhibit this problem. + // Changed by Tim. p3.3.14 + if ((_inports != _outports) || (fi.baseName(true) == QString("dssi-vst") && !config.vstInPlace)) + _inPlaceCapable = false; +} + +Plugin::~Plugin() +{ + //if(_portDescriptors) + // delete[] _portDescriptors; +} + +//--------------------------------------------------------- +// incReferences +//--------------------------------------------------------- + +int Plugin::incReferences(int val) +{ + int newref = _references + val; + + if(newref == 0) + { + _references = 0; + if(_handle) + { + #ifdef PLUGIN_DEBUGIN + fprintf(stderr, "Plugin::incReferences no more instances, closing library\n"); + #endif + + dlclose(_handle); + } + + _handle = 0; + ladspa = NULL; + plugin = NULL; + rpIdx.clear(); + + #ifdef DSSI_SUPPORT + dssi_descr = NULL; + #endif + + return 0; + } + + //if(_references == 0) + if(_handle == 0) + { + //_references = 0; + _handle = dlopen(fi.filePath().latin1(), RTLD_NOW); + //handle = dlopen(fi.absFilePath().latin1(), RTLD_NOW); + + if(_handle == 0) + { + fprintf(stderr, "Plugin::incReferences dlopen(%s) failed: %s\n", + fi.filePath().latin1(), dlerror()); + //fi.absFilePath().latin1(), dlerror()); + return 0; + } + + #ifdef DSSI_SUPPORT + DSSI_Descriptor_Function dssi = (DSSI_Descriptor_Function)dlsym(_handle, "dssi_descriptor"); + if(dssi) + { + const DSSI_Descriptor* descr; + for(int i = 0;; ++i) + { + descr = dssi(i); + if(descr == NULL) + break; + + QString label(descr->LADSPA_Plugin->Label); + // Listing effect plugins only while excluding synths: + // Do exactly what dssi-vst.cpp does for listing ladspa plugins. + //if(label == _name && + if(label == _label && + !descr->run_synth && + !descr->run_synth_adding && + !descr->run_multiple_synths && + !descr->run_multiple_synths_adding) + { + _isDssi = true; + ladspa = NULL; + dssi_descr = descr; + plugin = descr->LADSPA_Plugin; + break; + } + } + } + else + #endif // DSSI_SUPPORT + { + LADSPA_Descriptor_Function ladspadf = (LADSPA_Descriptor_Function)dlsym(_handle, "ladspa_descriptor"); + if(ladspadf) { - _inPlaceCapable = ip; + const LADSPA_Descriptor* descr; + for(int i = 0;; ++i) + { + descr = ladspadf(i); + if(descr == NULL) + break; + + QString label(descr->Label); + //if(label == _name) + if(label == _label) + { + _isDssi = false; + ladspa = ladspadf; + plugin = descr; + + #ifdef DSSI_SUPPORT + dssi_descr = NULL; + #endif + + break; + } + } + } + } + + if(plugin != NULL) + { + //_instNo = 0; + _name = QString(plugin->Name); + _uniqueID = plugin->UniqueID; + _maker = QString(plugin->Maker); + _copyright = QString(plugin->Copyright); + + //if(_portDescriptors) + // delete[] _portDescriptors; + //_portDescriptors = 0; + _portCount = plugin->PortCount; + //if(_portCount) + // _portDescriptors = new LADSPA_PortDescriptor[_portCount]; + _inports = 0; _outports = 0; - for (unsigned k = 0; k < d->PortCount; ++k) { - LADSPA_PortDescriptor pd = d->PortDescriptors[k]; - if (pd & LADSPA_PORT_CONTROL) - continue; - if (pd & LADSPA_PORT_INPUT) - ++_inports; - else - ++_outports; - } + _controlInPorts = 0; + _controlOutPorts = 0; + for(unsigned long k = 0; k < _portCount; ++k) + { + LADSPA_PortDescriptor pd = plugin->PortDescriptors[k]; + //_portDescriptors[k] = pd; + if(pd & LADSPA_PORT_AUDIO) + { + if(pd & LADSPA_PORT_INPUT) + ++_inports; + else + if(pd & LADSPA_PORT_OUTPUT) + ++_outports; + + rpIdx.push_back((unsigned long)-1); + } + else + if(pd & LADSPA_PORT_CONTROL) + { + if(pd & LADSPA_PORT_INPUT) + { + rpIdx.push_back(_controlInPorts); + ++_controlInPorts; + } + else + if(pd & LADSPA_PORT_OUTPUT) + { + rpIdx.push_back((unsigned long)-1); + ++_controlOutPorts; + } + } + } - // By T356. Blacklist vst plugins in-place configurable for now. At one point they - // were working with in-place here, but not now, and RJ also reported they weren't working. - // Fixes problem with vst plugins not working or feeding back loudly. - // I can only think of two things that made them stop working: - // 1): I switched back from Jack-2 to Jack-1 - // 2): I changed winecfg audio to use Jack instead of ALSA. - // Will test later... - // Possibly the first one because under Mandriva2007.1 (Jack-1), no matter how hard I tried, - // the same problem existed. It may have been when using Jack-2 with Mandriva2009 that they worked. - // Apparently the plugins are lying about their in-place capability. - // Quote: - /* Property LADSPA_PROPERTY_INPLACE_BROKEN indicates that the plugin - may cease to work correctly if the host elects to use the same data - location for both input and output (see connect_port()). This - should be avoided as enabling this flag makes it impossible for - hosts to use the plugin to process audio `in-place.' */ - // Examination of all my ladspa and vst synths and effects plugins showed only one - - // EnsembleLite (EnsLite VST) has the flag set, but it is a vst synth and is not involved here! - // Yet many (all?) ladspa vst effect plugins exhibit this problem. - // Changed by Tim. p3.3.14 - if ((_inports != _outports) || (f->baseName(true) == QString("dssi-vst") && !config.vstInPlace)) - _inPlaceCapable = false; + _inPlaceCapable = !LADSPA_IS_INPLACE_BROKEN(plugin->Properties); - _references = 0; - _instNo = 0; - } + // Blacklist vst plugins in-place configurable for now. + if ((_inports != _outports) || (fi.baseName(true) == QString("dssi-vst") && !config.vstInPlace)) + _inPlaceCapable = false; + } + } + + if(plugin == NULL) + { + dlclose(_handle); + _handle = 0; + _references = 0; + fprintf(stderr, "Plugin::incReferences Error: %s no plugin!\n", fi.filePath().latin1()); + return 0; + } + + _references = newref; + + //QString guiPath(info.dirPath() + "/" + info.baseName()); + //QDir guiDir(guiPath, "*", QDir::Unsorted, QDir::Files); + //_hasGui = guiDir.exists(); + + return _references; +} //--------------------------------------------------------- // range //--------------------------------------------------------- -void Plugin::range(int i, float* min, float* max) const +void Plugin::range(unsigned long i, float* min, float* max) const { LADSPA_PortRangeHint range = plugin->PortRangeHints[i]; LADSPA_PortRangeHintDescriptor desc = range.HintDescriptor; @@ -633,7 +875,7 @@ void Plugin::range(int i, float* min, float* max) const // defaultValue //--------------------------------------------------------- -double Plugin::defaultValue(unsigned int port) const +double Plugin::defaultValue(unsigned long port) const { if(port >= plugin->PortCount) return 0.0; @@ -680,59 +922,91 @@ double Plugin::defaultValue(unsigned int port) const //--------------------------------------------------------- static void loadPluginLib(QFileInfo* fi) - { - void* handle = dlopen(fi->filePath().ascii(), RTLD_NOW); - if (handle == 0) { - fprintf(stderr, "dlopen(%s) failed: %s\n", - fi->filePath().ascii(), dlerror()); - return; - } - LADSPA_Descriptor_Function ladspa = (LADSPA_Descriptor_Function)dlsym(handle, "ladspa_descriptor"); - - if (!ladspa) { - const char *txt = dlerror(); - if (txt) { - fprintf(stderr, - "Unable to find ladspa_descriptor() function in plugin " - "library file \"%s\": %s.\n" - "Are you sure this is a LADSPA plugin file?\n", - fi->filePath().ascii(), - txt); - } - dlclose(handle); - return; - } - const LADSPA_Descriptor* descr; - for (int i = 0;; ++i) { - descr = ladspa(i); - if (descr == NULL) - break; - - // Make sure it doesn't already exist. - if(plugins.find(fi->baseName(true), QString(descr->Label)) != 0) - continue; - - LADSPA_Properties properties = descr->Properties; - - /* - int ai = 0; - int ao = 0; - for (unsigned k = 0; k < descr->PortCount; ++k) { - LADSPA_PortDescriptor pd = descr->PortDescriptors[k]; - if (pd & LADSPA_PORT_CONTROL) - continue; - if (pd & LADSPA_PORT_INPUT) - ++ai; - else - ++ao; - } - */ - - bool inPlaceBroken = LADSPA_IS_INPLACE_BROKEN(properties); - - plugins.add(fi, ladspa, descr, !inPlaceBroken); - } +{ + void* handle = dlopen(fi->filePath().ascii(), RTLD_NOW); + if (handle == 0) { + fprintf(stderr, "dlopen(%s) failed: %s\n", + fi->filePath().ascii(), dlerror()); + return; + } + + #ifdef DSSI_SUPPORT + DSSI_Descriptor_Function dssi = (DSSI_Descriptor_Function)dlsym(handle, "dssi_descriptor"); + if(dssi) + { + const DSSI_Descriptor* descr; + for (int i = 0;; ++i) + { + descr = dssi(i); + if (descr == 0) + break; + + // Listing effect plugins only while excluding synths: + // Do exactly what dssi-vst.cpp does for listing ladspa plugins. + if(!descr->run_synth && + !descr->run_synth_adding && + !descr->run_multiple_synths && + !descr->run_multiple_synths_adding) + { + // Make sure it doesn't already exist. + if(plugins.find(fi->baseName(true), QString(descr->LADSPA_Plugin->Label)) != 0) + continue; + + #ifdef PLUGIN_DEBUGIN + fprintf(stderr, "loadPluginLib: dssi effect name:%s inPlaceBroken:%d\n", descr->LADSPA_Plugin->Name, LADSPA_IS_INPLACE_BROKEN(descr->LADSPA_Plugin->Properties)); + #endif + + //LADSPA_Properties properties = descr->LADSPA_Plugin->Properties; + //bool inPlaceBroken = LADSPA_IS_INPLACE_BROKEN(properties); + //plugins.add(fi, descr, !inPlaceBroken); + plugins.add(fi, descr->LADSPA_Plugin, true); } + } + } + else + #endif + { + LADSPA_Descriptor_Function ladspa = (LADSPA_Descriptor_Function)dlsym(handle, "ladspa_descriptor"); + if(!ladspa) + { + const char *txt = dlerror(); + if(txt) + { + fprintf(stderr, + "Unable to find ladspa_descriptor() function in plugin " + "library file \"%s\": %s.\n" + "Are you sure this is a LADSPA plugin file?\n", + fi->filePath().ascii(), + txt); + } + dlclose(handle); + return; + } + + const LADSPA_Descriptor* descr; + for (int i = 0;; ++i) + { + descr = ladspa(i); + if (descr == NULL) + break; + + // Make sure it doesn't already exist. + if(plugins.find(fi->baseName(true), QString(descr->Label)) != 0) + continue; + + #ifdef PLUGIN_DEBUGIN + fprintf(stderr, "loadPluginLib: ladspa effect name:%s inPlaceBroken:%d\n", descr->Name, LADSPA_IS_INPLACE_BROKEN(descr->Properties)); + #endif + + //LADSPA_Properties properties = descr->Properties; + //bool inPlaceBroken = LADSPA_IS_INPLACE_BROKEN(properties); + //plugins.add(fi, ladspa, descr, !inPlaceBroken); + plugins.add(fi, descr); + } + } + + dlclose(handle); +} //--------------------------------------------------------- // loadPluginDir @@ -762,11 +1036,38 @@ void initPlugins() { loadPluginDir(museGlobalLib + QString("/plugins")); + const char* p = 0; + + // Take care of DSSI plugins first... + #ifdef DSSI_SUPPORT + const char* dssiPath = getenv("DSSI_PATH"); + if (dssiPath == 0) + dssiPath = "/usr/local/lib64/dssi:/usr/lib64/dssi:/usr/local/lib/dssi:/usr/lib/dssi"; + p = dssiPath; + 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'; + loadPluginDir(QString(buffer)); + delete[] buffer; + } + p = pe; + if (*p == ':') + p++; + } + #endif + + // Now do LADSPA plugins... 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 = ladspaPath; + p = ladspaPath; while (*p != '\0') { const char* pe = p; while (*pe != ':' && *pe != '\0') @@ -971,6 +1272,19 @@ void Pipeline::move(int idx, bool up) } } +//--------------------------------------------------------- +// isDssiPlugin +//--------------------------------------------------------- + +bool Pipeline::isDssiPlugin(int idx) const +{ + PluginI* p = (*this)[idx]; + if(p) + return p->isDssiPlugin(); + + return false; +} + //--------------------------------------------------------- // showGui //--------------------------------------------------------- @@ -982,6 +1296,19 @@ void Pipeline::showGui(int idx, bool flag) p->showGui(flag); } +//--------------------------------------------------------- +// showNativeGui +//--------------------------------------------------------- + +void Pipeline::showNativeGui(int idx, bool flag) + { + #ifdef OSC_SUPPORT + PluginI* p = (*this)[idx]; + if (p) + p->oscIF().oscShowGui(flag); + #endif + } + //--------------------------------------------------------- // deleteGui //--------------------------------------------------------- @@ -1017,6 +1344,18 @@ bool Pipeline::guiVisible(int idx) return false; } +//--------------------------------------------------------- +// nativeGuiVisible +//--------------------------------------------------------- + +bool Pipeline::nativeGuiVisible(int idx) + { + PluginI* p = (*this)[idx]; + if (p) + return p->nativeGuiVisible(); + return false; + } + //--------------------------------------------------------- // apply //--------------------------------------------------------- @@ -1078,16 +1417,19 @@ void PluginI::init() handle = 0; controls = 0; controlsOut = 0; + controlPorts = 0; controlOutPorts = 0; _gui = 0; _on = true; initControlValues = false; + _showNativeGuiPending = false; } PluginI::PluginI() { _id = -1; _track = 0; + init(); } @@ -1104,11 +1446,11 @@ PluginI::~PluginI() if (_gui) delete _gui; if (controlsOut) - delete controlsOut; + delete[] controlsOut; if (controls) - delete controls; + delete[] controls; if (handle) - delete handle; + delete[] handle; } //--------------------------------------------------------- @@ -1144,7 +1486,7 @@ CtrlValueType PluginI::valueType() const //--------------------------------------------------------- void PluginI::setChannels(int c) - { +{ if (channel == c) return; int ni = c / _plugin->outports(); @@ -1156,20 +1498,21 @@ void PluginI::setChannels(int c) // remove old instances: deactivate(); - delete handle; + delete[] handle; instances = ni; handle = new LADSPA_Handle[instances]; for (int i = 0; i < instances; ++i) { handle[i] = _plugin->instantiate(); - if (handle[i] == 0) { + if (handle[i] == NULL) { printf("cannot instantiate instance %d\n", i); return; } } + int curPort = 0; int curOutPort = 0; - int ports = _plugin->ports(); - for (int k = 0; k < ports; ++k) + unsigned long ports = _plugin->ports(); + for (unsigned long k = 0; k < ports; ++k) { LADSPA_PortDescriptor pd = _plugin->portd(k); if (pd & LADSPA_PORT_CONTROL) @@ -1191,8 +1534,9 @@ void PluginI::setChannels(int c) } } } + activate(); - } +} //--------------------------------------------------------- // defaultValue @@ -1207,6 +1551,22 @@ double PluginI::defaultValue(unsigned int param) const return _plugin->defaultValue(controls[param].idx); } +LADSPA_Handle Plugin::instantiate() +{ + LADSPA_Handle h = plugin->instantiate(plugin, sampleRate); + if(h == NULL) + { + fprintf(stderr, "Plugin::instantiate() Error: plugin:%s instantiate failed!\n", plugin->Label); + return NULL; + } + + //QString guiPath(info.dirPath() + "/" + info.baseName()); + //QDir guiDir(guiPath, "*", QDir::Unsorted, QDir::Files); + //_hasGui = guiDir.exists(); + + return h; +} + //--------------------------------------------------------- // initPluginInstance // return true on error @@ -1215,96 +1575,107 @@ double PluginI::defaultValue(unsigned int param) const bool PluginI::initPluginInstance(Plugin* plug, int c) { channel = c; - if (plug == 0) { - printf("initPluginInstance: zero plugin\n"); - return true; - } + if(plug == 0) + { + printf("initPluginInstance: zero plugin\n"); + return true; + } _plugin = plug; + _plugin->incReferences(1); + #ifdef OSC_SUPPORT + _oscif.oscSetPluginI(this); + #endif + QString inst("-" + QString::number(_plugin->instNo())); _name = _plugin->name() + inst; _label = _plugin->label() + inst; instances = channel/plug->outports(); - if (instances < 1) - instances = 1; + if(instances < 1) + instances = 1; handle = new LADSPA_Handle[instances]; - for (int i = 0; i < instances; ++i) { - handle[i] = _plugin->instantiate(); - if (handle[i] == 0) - return true; - } + for(int i = 0; i < instances; ++i) + { + handle[i] = _plugin->instantiate(); + //if (handle[i] == 0) + if(handle[i] == NULL) + return true; + } + unsigned long ports = _plugin->ports(); + controlPorts = 0; controlOutPorts = 0; - int ports = _plugin->ports(); - - for (int k = 0; k < ports; ++k) + + for(unsigned long k = 0; k < ports; ++k) { - LADSPA_PortDescriptor pd = _plugin->portd(k); - if (pd & LADSPA_PORT_CONTROL) - { - if (pd & LADSPA_PORT_INPUT) - ++controlPorts; - else - if (pd & LADSPA_PORT_OUTPUT) - ++controlOutPorts; - } + LADSPA_PortDescriptor pd = _plugin->portd(k); + if(pd & LADSPA_PORT_CONTROL) + { + if(pd & LADSPA_PORT_INPUT) + ++controlPorts; + else + if(pd & LADSPA_PORT_OUTPUT) + ++controlOutPorts; + } } + controls = new Port[controlPorts]; controlsOut = new Port[controlOutPorts]; + int i = 0; int ii = 0; - for (int k = 0; k < ports; ++k) + for(unsigned long k = 0; k < ports; ++k) { - LADSPA_PortDescriptor pd = _plugin->portd(k); - if (pd & LADSPA_PORT_CONTROL) - { - if (pd & LADSPA_PORT_INPUT) - { - double val = _plugin->defaultValue(k); - controls[i].val = val; - controls[i].tmpVal = val; - controls[i].enCtrl = true; - controls[i].en2Ctrl = true; - ++i; - } - else - if (pd & LADSPA_PORT_OUTPUT) - { - //double val = _plugin->defaultValue(k); - controls[ii].val = 0.0; - controls[ii].tmpVal = 0.0; - controls[ii].enCtrl = false; - controls[ii].en2Ctrl = false; - ++ii; - } - } + LADSPA_PortDescriptor pd = _plugin->portd(k); + if(pd & LADSPA_PORT_CONTROL) + { + if(pd & LADSPA_PORT_INPUT) + { + double val = _plugin->defaultValue(k); + controls[i].val = val; + controls[i].tmpVal = val; + controls[i].enCtrl = true; + controls[i].en2Ctrl = true; + ++i; + } + else + if(pd & LADSPA_PORT_OUTPUT) + { + //double val = _plugin->defaultValue(k); + controlsOut[ii].val = 0.0; + controlsOut[ii].tmpVal = 0.0; + controlsOut[ii].enCtrl = false; + controlsOut[ii].en2Ctrl = false; + ++ii; + } + } } - int curPort = 0; - int curOutPort = 0; - for (int k = 0; k < ports; ++k) + unsigned long curPort = 0; + unsigned long curOutPort = 0; + for(unsigned long k = 0; k < ports; ++k) { - LADSPA_PortDescriptor pd = _plugin->portd(k); - if (pd & LADSPA_PORT_CONTROL) - { - if (pd & LADSPA_PORT_INPUT) - { - for (int i = 0; i < instances; ++i) - _plugin->connectPort(handle[i], k, &controls[curPort].val); - controls[curPort].idx = k; - ++curPort; - } - else - if (pd & LADSPA_PORT_OUTPUT) - { - for (int i = 0; i < instances; ++i) - _plugin->connectPort(handle[i], k, &controlsOut[curOutPort].val); - controlsOut[curOutPort].idx = k; - ++curOutPort; - } - } + LADSPA_PortDescriptor pd = _plugin->portd(k); + if(pd & LADSPA_PORT_CONTROL) + { + if(pd & LADSPA_PORT_INPUT) + { + for(int i = 0; i < instances; ++i) + _plugin->connectPort(handle[i], k, &controls[curPort].val); + controls[curPort].idx = k; + ++curPort; + } + else + if(pd & LADSPA_PORT_OUTPUT) + { + for(int i = 0; i < instances; ++i) + _plugin->connectPort(handle[i], k, &controlsOut[curOutPort].val); + controlsOut[curOutPort].idx = k; + ++curOutPort; + } + } } activate(); return false; @@ -1318,7 +1689,7 @@ void PluginI::connect(int ports, float** src, float** dst) { int port = 0; for (int i = 0; i < instances; ++i) { - for (int k = 0; k < _plugin->ports(); ++k) { + for (unsigned long k = 0; k < _plugin->ports(); ++k) { if (isAudioIn(k)) { _plugin->connectPort(handle[i], k, src[port]); port = (port + 1) % ports; @@ -1327,7 +1698,7 @@ void PluginI::connect(int ports, float** src, float** dst) } port = 0; for (int i = 0; i < instances; ++i) { - for (int k = 0; k < _plugin->ports(); ++k) { + for (unsigned long k = 0; k < _plugin->ports(); ++k) { if (isAudioOut(k)) { _plugin->connectPort(handle[i], k, dst[port]); port = (port + 1) % ports; // overwrite output? @@ -1377,6 +1748,11 @@ void PluginI::writeConfiguration(int level, Xml& xml) xml.intTag(level, "gui", 1); xml.geometryTag(level, "geometry", _gui); } + if (nativeGuiVisible()) { + xml.intTag(level, "nativegui", 1); + // TODO: + //xml.geometryTag(level, "nativegeometry", ?); + } xml.tag(level--, "/plugin"); } @@ -1462,6 +1838,13 @@ bool PluginI::readConfiguration(Xml& xml, bool readPreset) bool flag = xml.parseInt(); showGui(flag); } + else if (tag == "nativegui") { + // We can't tell OSC to show the native plugin gui + // until the parent track is added to the lists. + // OSC needs to find the plugin in the track lists. + // Use this 'pending' flag so it gets done later. + _showNativeGuiPending = xml.parseInt(); + } else if (tag == "geometry") { QRect r(readGeometry(xml, tag)); if (_gui) { @@ -1556,6 +1939,48 @@ bool PluginI::guiVisible() return _gui && _gui->isVisible(); } +//--------------------------------------------------------- +// showNativeGui +//--------------------------------------------------------- + +void PluginI::showNativeGui() +{ + #ifdef OSC_SUPPORT + if (_plugin) + { + if (_oscif.oscGuiVisible()) + _oscif.oscShowGui(false); + else + _oscif.oscShowGui(true); + } + #endif + _showNativeGuiPending = false; +} + +void PluginI::showNativeGui(bool flag) +{ + #ifdef OSC_SUPPORT + if(_plugin) + { + _oscif.oscShowGui(flag); + } + #endif + _showNativeGuiPending = false; +} + +//--------------------------------------------------------- +// nativeGuiVisible +//--------------------------------------------------------- + +bool PluginI::nativeGuiVisible() +{ + #ifdef OSC_SUPPORT + return _oscif.oscGuiVisible(); + #endif + + return false; +} + //--------------------------------------------------------- // makeGui //--------------------------------------------------------- @@ -1602,22 +2027,350 @@ void PluginI::enable2AllControllers(bool v) //--------------------------------------------------------- void PluginI::apply(int n) +{ + // Process control value changes. + //if(automation && _track && _track->automationType() != AUTO_OFF && _id != -1) + //{ + // for(int i = 0; i < controlPorts; ++i) + // { + // if( controls[i].enCtrl && controls[i].en2Ctrl ) + // controls[i].tmpVal = _track->pluginCtrlVal(genACnum(_id, i)); + // } + //} + + unsigned long ctls = controlPorts; + for(unsigned long k = 0; k < ctls; ++k) { - if(automation && _track && _track->automationType() != AUTO_OFF && _id != -1) - { - for(int i = 0; i < controlPorts; ++i) + // First, update the temporary value if needed... + + #ifdef OSC_SUPPORT + // Process OSC gui input control fifo events. + // It is probably more important that these are processed 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. + + 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()) + if(cfifo && !cfifo->isEmpty()) { - if( controls[i].enCtrl && controls[i].en2Ctrl ) - controls[i].tmpVal = _track->pluginCtrlVal(genACnum(_id, i)); - } - } + OscControlValue v = cfifo->get(); + + #ifdef PLUGIN_DEBUGIN + fprintf(stderr, "PluginI::apply OscControlFifo event input control number:%ld value:%f\n", k, v.value); + #endif + + // Set the ladspa control port value. + controls[k].tmpVal = v.value; + + // Need to update the automation value, otherwise it overwrites later with the last automation value. + if(_track && _id != -1) + { + // 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, k), controls[k].val); + _track->setPluginCtrlVal(genACnum(_id, k), v.value); + + // Record automation. + // NO! Take care of this immediately in the OSC control handler, because we don't want + // the silly delay associated with processing the fifo one-at-a-time here. + + //AutomationType at = _track->automationType(); + // TODO: Taken from our native gui control handlers. + // This may need modification or may cause problems - + // we don't have the luxury of access to the dssi gui controls ! + //if(at == AUTO_WRITE || (audio->isPlaying() && at == AUTO_TOUCH)) + // enableController(k, false); + //_track->recordAutomation(id, v.value); + } + } + else + #endif // OSC_SUPPORT + { + // Process automation control value. + if(automation && _track && _track->automationType() != AUTO_OFF && _id != -1) + { + if(controls[k].enCtrl && controls[k].en2Ctrl ) + controls[k].tmpVal = _track->pluginCtrlVal(genACnum(_id, k)); + } + } + + // Now update the actual value from the temporary value... + controls[k].val = controls[k].tmpVal; + } + + //for (int i = 0; i < controlPorts; ++i) + // controls[i].val = controls[i].tmpVal; - for (int i = 0; i < controlPorts; ++i) - controls[i].val = controls[i].tmpVal; for (int i = 0; i < instances; ++i) _plugin->apply(handle[i], n); } +//--------------------------------------------------------- +// oscConfigure +//--------------------------------------------------------- + +#ifdef OSC_SUPPORT +int Plugin::oscConfigure(LADSPA_Handle handle, const char* key, const char* value) + { + #ifdef PLUGIN_DEBUGIN + printf("Plugin::oscConfigure effect plugin label:%s key:%s value:%s\n", plugin->Label, key, value); + #endif + + #ifdef DSSI_SUPPORT + if(!dssi_descr || !dssi_descr->configure) + return 0; + + if (!strncmp(key, DSSI_RESERVED_CONFIGURE_PREFIX, + strlen(DSSI_RESERVED_CONFIGURE_PREFIX))) { + fprintf(stderr, "Plugin::oscConfigure OSC: UI for plugin '%s' attempted to use reserved configure key \"%s\", ignoring\n", + plugin->Label, key); + + return 0; + } + + char* message = dssi_descr->configure(handle, key, value); + if (message) { + printf("Plugin::oscConfigure on configure '%s' '%s', plugin '%s' returned error '%s'\n", + //key, value, synti->name().toAscii().data(), message); + key, value, plugin->Label, 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(); + + #endif // DSSI_SUPPORT + + return 0; +} + +//--------------------------------------------------------- +// oscConfigure +//--------------------------------------------------------- + +int PluginI::oscConfigure(const char *key, const char *value) + { + if(!_plugin) + return 0; + + // 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. + + //const char *key = (const char *)&argv[0]->s; + //const char *value = (const char *)&argv[1]->s; + + #ifdef PLUGIN_DEBUGIN + printf("PluginI::oscConfigure effect plugin name:%s label:%s key:%s value:%s\n", _name.latin1(), _label.latin1(), key, value); + #endif + + #ifdef DSSI_SUPPORT + // FIXME: Don't think this is right, should probably do as example shows below. + for(int i = 0; i < instances; ++i) + _plugin->oscConfigure(handle[i], key, value); + + // 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(); + #endif // DSSI_SUPPORT + + return 0; +} + +//--------------------------------------------------------- +// oscUpdate +//--------------------------------------------------------- + +int PluginI::oscUpdate() +{ + #ifdef DSSI_SUPPORT + // Send project directory. + _oscif.oscSendConfigure(DSSI_PROJECT_DIRECTORY_KEY, museProject.latin1()); // song->projectPath() + #endif + + /* + // Send current string configuration parameters. + StringParamMap& map = synti->stringParameters(); + int i = 0; + for(ciStringParamMap r = map.begin(); r != map.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); + + // 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]); + // Avoid overloading the GUI if there are lots and lots of ports. + if((i+1) % 50 == 0) + usleep(300000); + } + + */ + + return 0; +} + +//--------------------------------------------------------- +// oscControl +//--------------------------------------------------------- + +int PluginI::oscControl(unsigned long port, float value) +{ + //int port = argv[0]->i; + //LADSPA_Data value = argv[1]->f; + + #ifdef PLUGIN_DEBUGIN + printf("PluginI::oscControl received oscControl port:%ld val:%f\n", port, value); + #endif + + //int controlPorts = synth->_controller; + + //if(port >= controlPorts) + //if(port < 0 || port >= _plugin->rpIdx.size()) + //{ + //fprintf(stderr, "DssiSynthIF::oscControl: port number:%d is out of range of number of ports:%d\n", port, controlPorts); + // fprintf(stderr, "PluginI::oscControl: port number:%d is out of range of index list size:%d\n", port, _plugin->rpIdx.size()); + // return 0; + //} + + // Convert from DSSI port number to control input port index. + //unsigned long cport = _plugin->rpIdx[port]; + unsigned long cport = _plugin->port2InCtrl(port); + + if((int)cport == -1) + { + fprintf(stderr, "PluginI::oscControl: port number:%ld is not a control input\n", port); + return 0; + } + + // (From DSSI module). + // 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: 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. + // Try this ... + OscControlFifo* cfifo = _oscif.oscFifo(cport); + if(cfifo) + { + OscControlValue cv; + //cv.idx = cport; + cv.value = value; + if(cfifo->put(cv)) + { + fprintf(stderr, "PluginI::oscControl: fifo overflow: in control number:%ld\n", cport); + } + } + + // Record automation: + // Take care of this immediately, because we don't want the silly delay associated with + // processing the fifo one-at-a-time in the apply(). + // NOTE: Ahh crap! We don't receive control events until the user RELEASES a control ! + // So the events all arrive at once when the user releases a control. + // That makes this pretty useless... But what the heck... + if(_track && _id != -1) + { + int id = genACnum(_id, cport); + AutomationType at = _track->automationType(); + + // TODO: Taken from our native gui control handlers. + // This may need modification or may cause problems - + // we don't have the luxury of access to the dssi gui controls ! + if(at == AUTO_WRITE || (audio->isPlaying() && at == AUTO_TOUCH)) + enableController(cport, false); + + _track->recordAutomation(id, value); + } + + /* + 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; + } + +#endif // OSC_SUPPORT + + //--------------------------------------------------------- // PluginDialog // select Plugin dialog @@ -1746,122 +2499,149 @@ void PluginDialog::accept() //--------------------------------------------------------- void PluginDialog::fillPlugs(int nbr) - { - pList->clear(); - for (iPlugin i = plugins.begin(); i != plugins.end(); ++i) { - int ai = 0; - int ao = 0; - int ci = 0; - int co = 0; - for (int k = 0; k < i->ports(); ++k) { - LADSPA_PortDescriptor pd = i->portd(k); - if (pd & LADSPA_PORT_CONTROL) { - if (pd & LADSPA_PORT_INPUT) - ++ci; - else - ++co; - } - else { - if (pd & LADSPA_PORT_INPUT) - ++ai; - else - ++ao; - } - } +{ + pList->clear(); + for(iPlugin i = plugins.begin(); i != plugins.end(); ++i) + { + /* + int ai = 0; + int ao = 0; + int ci = 0; + int co = 0; + for(unsigned long k = 0; k < i->ports(); ++k) + { + LADSPA_PortDescriptor pd = i->portd(k); + if(pd & LADSPA_PORT_CONTROL) + { + if(pd & LADSPA_PORT_INPUT) + ++ci; + else + if(pd & LADSPA_PORT_OUTPUT) + ++co; + } + else + if(pd & LADSPA_PORT_AUDIO) + { + if(pd & LADSPA_PORT_INPUT) + ++ai; + else + if(pd & LADSPA_PORT_OUTPUT) + ++ao; + } + } + */ + int ai = i->inports(); + int ao = i->outports(); + int ci = i->controlInPorts(); + int co = i->controlOutPorts(); - bool addFlag = false; - switch(nbr) - { - case 0: // stereo & mono - if ((ai == 1 || ai == 2) && (ao == 1 || ao ==2)) - { - addFlag = true; - } - break; - case 1: // stereo - if ((ai == 1 || ai == 2) && ao ==2) - { - addFlag = true; - } - break; - case 2: // mono - if (ai == 1 && ao == 1) - { - addFlag = true; - } - break; - case 3: // all - addFlag = true; - break; - } - if (addFlag) - { - QListViewItem* item = new QListViewItem(pList, - i->lib(), - i->label(), - i->name(), - QString().setNum(ai), - QString().setNum(ao), - QString().setNum(ci), - QString().setNum(co), - QString().setNum(i->inPlaceCapable()) - ); - item->setText(8, QString().setNum(i->id())); - item->setText(9, i->maker()); - item->setText(10, i->copyright()); + bool addFlag = false; + switch(nbr) + { + case 0: // stereo & mono + if ((ai == 1 || ai == 2) && (ao == 1 || ao ==2)) + { + addFlag = true; } - } - selectedPlugType = nbr; - } + break; + case 1: // stereo + if ((ai == 1 || ai == 2) && ao ==2) + { + addFlag = true; + } + break; + case 2: // mono + if (ai == 1 && ao == 1) + { + addFlag = true; + } + break; + case 3: // all + addFlag = true; + break; + } + if(addFlag) + { + QListViewItem* item = new QListViewItem(pList, + i->lib(), + i->label(), + i->name(), + QString().setNum(ai), + QString().setNum(ao), + QString().setNum(ci), + QString().setNum(co), + QString().setNum(i->inPlaceCapable()) + ); + item->setText(8, QString().setNum(i->id())); + item->setText(9, i->maker()); + item->setText(10, i->copyright()); + } + } + selectedPlugType = nbr; +} void PluginDialog::fillPlugs(const QString &sortValue) - { - pList->clear(); - for (iPlugin i = plugins.begin(); i != plugins.end(); ++i) { - int ai = 0; - int ao = 0; - int ci = 0; - int co = 0; - for (int k = 0; k < i->ports(); ++k) { - LADSPA_PortDescriptor pd = i->portd(k); - if (pd & LADSPA_PORT_CONTROL) { - if (pd & LADSPA_PORT_INPUT) - ++ci; - else - ++co; - } - else { - if (pd & LADSPA_PORT_INPUT) - ++ai; - else - ++ao; - } - } - - bool addFlag = false; - - if (i->label().lower().contains(sortValue.lower())) - addFlag = true; - else if (i->name().lower().contains(sortValue.lower())) - addFlag = true; - if (addFlag) - { - QListViewItem* item = new QListViewItem(pList, - i->lib(), - i->label(), - i->name(), - QString().setNum(ai), - QString().setNum(ao), - QString().setNum(ci), - QString().setNum(co), - QString().setNum(i->inPlaceCapable()) - ); - item->setText(8, QString().setNum(i->id())); - item->setText(9, i->maker()); - item->setText(10, i->copyright()); - } - } +{ + pList->clear(); + for(iPlugin i = plugins.begin(); i != plugins.end(); ++i) + { + /* + int ai = 0; + int ao = 0; + int ci = 0; + int co = 0; + for(unsigned long k = 0; k < i->ports(); ++k) + { + LADSPA_PortDescriptor pd = i->portd(k); + if(pd & LADSPA_PORT_CONTROL) + { + if(pd & LADSPA_PORT_INPUT) + ++ci; + else + if(pd & LADSPA_PORT_OUTPUT) + ++co; } + else + if(pd & LADSPA_PORT_AUDIO) + { + if(pd & LADSPA_PORT_INPUT) + ++ai; + else + if(pd & LADSPA_PORT_OUTPUT) + ++ao; + } + } + */ + int ai = i->inports(); + int ao = i->outports(); + int ci = i->controlInPorts(); + int co = i->controlOutPorts(); + + bool addFlag = false; + + if(i->label().lower().contains(sortValue.lower())) + addFlag = true; + else + if(i->name().lower().contains(sortValue.lower())) + addFlag = true; + if(addFlag) + { + QListViewItem* item = new QListViewItem(pList, + i->lib(), + i->label(), + i->name(), + QString().setNum(ai), + QString().setNum(ao), + QString().setNum(ci), + QString().setNum(co), + QString().setNum(i->inPlaceCapable()) + ); + item->setText(8, QString().setNum(i->id())); + item->setText(9, i->maker()); + item->setText(10, i->copyright()); + } + } +} //--------------------------------------------------------- // getPlugin @@ -2559,7 +3339,6 @@ void PluginGui::updateControls() } if(((Slider*)(gp->actuator))->value() != sv) { - // Added by Tim. p3.3.6 //printf("PluginGui::updateControls slider\n"); gp->label->blockSignals(true); @@ -2578,7 +3357,6 @@ void PluginGui::updateControls() bool v = (int)plugin->track()->pluginCtrlVal(genACnum(plugin->id(), i)); if(((CheckBox*)(gp->actuator))->isChecked() != v) { - // Added by Tim. p3.3.6 //printf("PluginGui::updateControls switch\n"); ((CheckBox*)(gp->actuator))->blockSignals(true); @@ -2601,7 +3379,6 @@ void PluginGui::updateControls() double v = plugin->track()->pluginCtrlVal(genACnum(plugin->id(), param)); if(((Slider*)widget)->value() != v) { - // Added by Tim. p3.3.6 //printf("PluginGui::updateControls slider\n"); ((Slider*)widget)->blockSignals(true); @@ -2616,7 +3393,6 @@ void PluginGui::updateControls() double v = plugin->track()->pluginCtrlVal(genACnum(plugin->id(), param)); if(((DoubleLabel*)widget)->value() != v) { - // Added by Tim. p3.3.6 //printf("PluginGui::updateControls label\n"); ((DoubleLabel*)widget)->blockSignals(true); @@ -2631,7 +3407,6 @@ void PluginGui::updateControls() bool b = (bool) plugin->track()->pluginCtrlVal(genACnum(plugin->id(), param)); if(((QCheckBox*)widget)->isChecked() != b) { - // Added by Tim. p3.3.6 //printf("PluginGui::updateControls checkbox\n"); ((QCheckBox*)widget)->blockSignals(true); @@ -2646,7 +3421,6 @@ void PluginGui::updateControls() int n = (int) plugin->track()->pluginCtrlVal(genACnum(plugin->id(), param)); if(((QComboBox*)widget)->currentItem() != n) { - // Added by Tim. p3.3.6 //printf("PluginGui::updateControls combobox\n"); ((QComboBox*)widget)->blockSignals(true); diff --git a/muse/muse/plugin.h b/muse/muse/plugin.h index 2ec13e29..8461b7f8 100644 --- a/muse/muse/plugin.h +++ b/muse/muse/plugin.h @@ -25,6 +25,19 @@ #include "globaldefs.h" #include "ctrl.h" +//#include "stringparam.h" + +#include "config.h" + +#ifdef OSC_SUPPORT +//class OscIF; +#include "osc.h" +#endif + +#ifdef DSSI_SUPPORT +#include +#endif + class Xml; class QWidget; // class QLabel; @@ -50,67 +63,107 @@ class PluginWidgetFactory : public QWidgetFactory //--------------------------------------------------------- class Plugin { + protected: + void* _handle; int _references; int _instNo; QFileInfo fi; LADSPA_Descriptor_Function ladspa; const LADSPA_Descriptor *plugin; - int _inports; - int _outports; + unsigned long _uniqueID; + QString _label; + QString _name; + QString _maker; + QString _copyright; + + bool _isDssi; + #ifdef DSSI_SUPPORT + const DSSI_Descriptor* dssi_descr; + #endif + + //LADSPA_PortDescriptor* _portDescriptors; + unsigned long _portCount; + unsigned long _inports; + unsigned long _outports; + unsigned long _controlInPorts; + unsigned long _controlOutPorts; + std::vector rpIdx; // Port number to control input index. Item is -1 if it's not a control input. + bool _inPlaceCapable; public: - Plugin(QFileInfo* f, - LADSPA_Descriptor_Function df, const LADSPA_Descriptor* d, bool inPlace); - - QString label() const { return QString(plugin->Label); } - QString name() const { return QString(plugin->Name); } - unsigned long id() const { return plugin->UniqueID; } - QString maker() const { return QString(plugin->Maker); } - QString copyright() const { return QString(plugin->Copyright); } - QString lib() const { return fi.baseName(true); } - QString path() const { return fi.dirPath(); } - int references() const { return _references; } - int incReferences(int n) { return _references += n; } - int instNo() { return _instNo++; } - - LADSPA_Handle instantiate() { - return plugin->instantiate(plugin, sampleRate); - } + Plugin(QFileInfo* f, const LADSPA_Descriptor* d, bool isDssi = false); + ~Plugin(); + + QString label() const { return _label; } + QString name() const { return _name; } + unsigned long id() const { return _uniqueID; } + QString maker() const { return _maker; } + QString copyright() const { return _copyright; } + QString lib(bool complete = true) const { return fi.baseName(complete); } + QString dirPath(bool complete = true) const { return fi.dirPath(complete); } + QString filePath() const { return fi.filePath(); } + int references() const { return _references; } + int incReferences(int); + int instNo() { return _instNo++; } + + bool isDssiPlugin() const { return _isDssi; } + + LADSPA_Handle instantiate(); void activate(LADSPA_Handle handle) { - if (plugin->activate) + if (plugin && plugin->activate) plugin->activate(handle); } void deactivate(LADSPA_Handle handle) { - if (plugin->deactivate) + if (plugin && plugin->deactivate) plugin->deactivate(handle); } void cleanup(LADSPA_Handle handle) { - if (plugin->cleanup) + if (plugin && plugin->cleanup) plugin->cleanup(handle); } void connectPort(LADSPA_Handle handle, int port, float* value) { - plugin->connect_port(handle, port, value); + if(plugin) + plugin->connect_port(handle, port, value); } void apply(LADSPA_Handle handle, int n) { - plugin->run(handle, n); + if(plugin) + plugin->run(handle, n); } - int ports() { return plugin->PortCount; } - double defaultValue(unsigned int port) const; - LADSPA_PortDescriptor portd(int k) const { - return plugin->PortDescriptors[k]; + + #ifdef OSC_SUPPORT + int oscConfigure(LADSPA_Handle /*handle*/, const char* /*key*/, const char* /*value*/); + #endif + + //int ports() { return plugin ? plugin->PortCount : 0; } + unsigned long ports() { return _portCount; } + + LADSPA_PortDescriptor portd(unsigned long k) const { + return plugin ? plugin->PortDescriptors[k] : 0; + //return _portDescriptors[k]; } - void range(int i, float*, float*) const; - LADSPA_PortRangeHint range(int i) { + + LADSPA_PortRangeHint range(unsigned long i) { + // FIXME: + //return plugin ? plugin->PortRangeHints[i] : 0; return plugin->PortRangeHints[i]; } - const char* portName(int i) { - return plugin->PortNames[i]; + double defaultValue(unsigned long port) const; + void range(unsigned long i, float*, float*) const; + + const char* portName(unsigned long i) { + return plugin ? plugin->PortNames[i] : 0; } - int inports() const { return _inports; } - int outports() const { return _outports; } - bool inPlaceCapable() const { return _inPlaceCapable; } + + // Returns (int)-1 if not an input control. + unsigned long port2InCtrl(unsigned long p) { return p >= rpIdx.size() ? (unsigned long)-1 : rpIdx[p]; } + + unsigned long inports() const { return _inports; } + unsigned long outports() const { return _outports; } + unsigned long controlInPorts() const { return _controlInPorts; } + unsigned long controlOutPorts() const { return _controlOutPorts; } + bool inPlaceCapable() const { return _inPlaceCapable; } }; typedef std::list::iterator iPlugin; @@ -121,10 +174,11 @@ typedef std::list::iterator iPlugin; class PluginList : public std::list { public: - void add(QFileInfo* fi, LADSPA_Descriptor_Function df, - const LADSPA_Descriptor* d, bool inPlaceOk) { - push_back(Plugin(fi, df, d, inPlaceOk)); - } + void add(QFileInfo* fi, const LADSPA_Descriptor* d, bool isDssi = false) + { + push_back(Plugin(fi, d, isDssi)); + } + Plugin* find(const QString&, const QString&); PluginList() {} }; @@ -242,9 +296,18 @@ class PluginI { QString _name; QString _label; + //#ifdef DSSI_SUPPORT + //StringParamMap _stringParamMap; + //#endif + + #ifdef OSC_SUPPORT + OscEffectIF _oscif; + #endif + bool _showNativeGuiPending; + void init(); void makeGui(); - + public: PluginI(); ~PluginI(); @@ -280,13 +343,33 @@ class PluginI { CtrlValueType valueType() const; QString lib() const { return _plugin->lib(); } + #ifdef OSC_SUPPORT + OscEffectIF& oscIF() { return _oscif; } + /* + int oscConfigure(lo_arg**); + int oscControl(lo_arg**); + //int oscUpdate(lo_arg**); + //int oscExiting(lo_arg**); + */ + + int oscControl(unsigned long /*dssiPort*/, float /*val*/); + int oscConfigure(const char */*key*/, const char */*val*/); + int oscUpdate(); + //int oscExiting(); + #endif + void writeConfiguration(int level, Xml& xml); bool readConfiguration(Xml& xml, bool readPreset=false); bool loadControl(Xml& xml); bool setControl(const QString& s, double val); void showGui(); void showGui(bool); + bool isDssiPlugin() const { return _plugin->isDssiPlugin(); } + void showNativeGui(); + void showNativeGui(bool); + bool isShowNativeGuiPending() { return _showNativeGuiPending; } bool guiVisible(); + bool nativeGuiVisible(); int parameters() const { return controlPorts; } void setParam(int i, double val) { controls[i].tmpVal = val; } double param(int i) const { return controls[i].val; } @@ -330,9 +413,12 @@ class Pipeline : public std::vector { QString label(int idx) const; QString name(int idx) const; void showGui(int, bool); + bool isDssiPlugin(int) const; + void showNativeGui(int, bool); void deleteGui(int idx); void deleteAllGuis(); bool guiVisible(int); + bool nativeGuiVisible(int); void apply(int ports, unsigned long nframes, float** buffer); void move(int idx, bool up); bool empty(int idx) const; diff --git a/muse/muse/song.cpp b/muse/muse/song.cpp index fa08f27f..bccb3f72 100644 --- a/muse/muse/song.cpp +++ b/muse/muse/song.cpp @@ -1559,9 +1559,15 @@ void Song::beat() midiPorts[port].syncInfo().setTime(); } + int tick = audio->tickPos(); if (audio->isPlaying()) setPos(0, tick, true, false, true); + + // p3.3.40 Update synth native guis at the heartbeat rate. + for(ciSynthI is = _synthIs.begin(); is != _synthIs.end(); ++is) + (*is)->guiHeartBeat(); + while (noteFifoSize) { int pv = recNoteFifo[noteFifoRindex]; noteFifoRindex = (noteFifoRindex + 1) % REC_NOTE_FIFO_SIZE; @@ -3123,7 +3129,7 @@ void Song::insertTrack2(Track* track, int idx) //--------------------------------------------------------- void Song::insertTrack3(Track* /*track*/, int /*idx*/)//prevent compiler warning: unused parameter - { +{ //printf("Song::insertTrack3\n"); /* @@ -3134,7 +3140,7 @@ void Song::insertTrack3(Track* /*track*/, int /*idx*/)//prevent compiler warning break; } */ - } +} //--------------------------------------------------------- // removeTrack0 diff --git a/muse/muse/songfile.cpp b/muse/muse/songfile.cpp index 07bf1e31..a1f51c69 100644 --- a/muse/muse/songfile.cpp +++ b/muse/muse/songfile.cpp @@ -1257,31 +1257,40 @@ void Song::read(Xml& xml) WaveTrack* track = new WaveTrack(); track->read(xml); insertTrack0(track,-1); + // Now that the track has been added to the lists in insertTrack2(), + // OSC can find the track and its plugins, and start their native guis if required... + track->showPendingPluginNativeGuis(); } else if (tag == "AudioInput") { AudioInput* track = new AudioInput(); track->read(xml); insertTrack0(track,-1); + track->showPendingPluginNativeGuis(); } else if (tag == "AudioOutput") { AudioOutput* track = new AudioOutput(); track->read(xml); insertTrack0(track,-1); + track->showPendingPluginNativeGuis(); } else if (tag == "AudioGroup") { AudioGroup* track = new AudioGroup(); track->read(xml); insertTrack0(track,-1); + track->showPendingPluginNativeGuis(); } else if (tag == "AudioAux") { AudioAux* track = new AudioAux(); track->read(xml); insertTrack0(track,-1); + track->showPendingPluginNativeGuis(); } else if (tag == "SynthI") { SynthI* track = new SynthI(); track->read(xml); + // Done in SynthI::read() // insertTrack(track,-1); + //track->showPendingPluginNativeGuis(); } else if (tag == "Route") { readRoute(xml); diff --git a/muse/muse/synth.cpp b/muse/muse/synth.cpp index 24780a52..a08f9fcc 100644 --- a/muse/muse/synth.cpp +++ b/muse/muse/synth.cpp @@ -33,6 +33,7 @@ #include "audio.h" #include "midiseq.h" #include "midictrl.h" +//#include "stringparam.h" std::vector synthis; // array of available synthis @@ -230,6 +231,11 @@ SynthI::SynthI() _openFlags = 1; _readEnable = false; _writeEnable = false; + + _curBankH = 0; + _curBankL = 0; + _curProgram = 0; + setVolume(1.0); setPan(0.0); } @@ -282,6 +288,20 @@ void SynthI::setName(const QString& s) MidiDevice::setName(s); } +//--------------------------------------------------------- +// currentProg +//--------------------------------------------------------- + +void SynthI::currentProg(unsigned long *prog, unsigned long *bankL, unsigned long *bankH) +{ + if(prog) + *prog = _curProgram; + if(bankL) + *bankL = _curBankL; + if(bankH) + *bankH = _curBankH; +} + //--------------------------------------------------------- // init //--------------------------------------------------------- @@ -392,9 +412,14 @@ bool SynthI::initInstance(Synth* s, const QString& instanceName) iel->clear(); } - int idx = 0; + unsigned long idx = 0; for (std::vector::iterator i = initParams.begin(); i != initParams.end(); ++i, ++idx) _sif->setParameter(idx, *i); + + // p3.3.40 Since we are done with the (sometimes huge) initial parameters list, clear it. + // TODO: Decide: Maybe keep them around for a 'reset to previously loaded values' (revert) command? ... + initParams.clear(); + return false; } @@ -576,6 +601,12 @@ SynthI* Song::createSynthI(const QString& sclass, const QString& label) audio->msgUpdateSoloStates(); } + // Now that the track has been added to the lists in insertTrack2(), + // if it's a dssi synth, OSC can find the synth, and initialize (and show) its native gui. + // No, initializing OSC without actually showing the gui doesn't work, at least for + // dssi-vst plugins - without showing the gui they exit after ten seconds. + //si->initGui(); + return si; } @@ -611,6 +642,10 @@ void SynthI::write(int level, Xml& xml) const xml.qrectTag(level, "geometry", QRect(x, y, w, h)); } + _stringParamMap.write(level, xml, "stringParam"); + + xml.tag(level, "curProgram bankH=\"%ld\" bankL=\"%ld\" prog=\"%ld\"/", _curBankH, _curBankL, _curProgram); + _sif->write(level, xml); xml.etag(level, "SynthI"); } @@ -642,6 +677,45 @@ void MessSynthIF::write(int level, Xml& xml) const } } +//--------------------------------------------------------- +// SynthI::readProgram +//--------------------------------------------------------- + +void SynthI::readProgram(Xml& xml, const QString& name) +{ + for (;;) + { + Xml::Token token = xml.parse(); + const QString tag = xml.s1(); + switch (token) + { + case Xml::Error: + case Xml::End: + return; + case Xml::TagStart: + xml.unknown(name); + break; + case Xml::Attribut: + if(tag == "bankH") + _curBankH = xml.s2().toUInt(); + else + if(tag == "bankL") + _curBankL = xml.s2().toUInt(); + else + if(tag == "prog") + _curProgram = xml.s2().toUInt(); + else + xml.unknown(name); + break; + case Xml::TagEnd: + if(tag == name) + return; + default: + break; + } + } +} + //--------------------------------------------------------- // SynthI::read //--------------------------------------------------------- @@ -652,7 +726,7 @@ void SynthI::read(Xml& xml) QString label; int port = -1; - bool startGui = false; + bool startgui = false; QRect r; for (;;) { @@ -670,13 +744,17 @@ void SynthI::read(Xml& xml) else if (tag == "port") port = xml.parseInt(); else if (tag == "guiVisible") - startGui = xml.parseInt(); + startgui = xml.parseInt(); else if (tag == "midistate") readMidiState(xml); else if (tag == "param") { float val = xml.parseFloat(); initParams.push_back(val); } + else if (tag == "stringParam") + _stringParamMap.read(xml, tag); + else if (tag == "curProgram") + readProgram(xml, tag); else if (tag == "geometry") r = readGeometry(xml, tag); else if (AudioTrack::readProperties(xml, tag)) @@ -693,11 +771,21 @@ void SynthI::read(Xml& xml) song->insertTrack0(this, -1); if (port != -1 && port < MIDI_PORTS) midiPorts[port].setMidiDevice(this); - showGui(startGui); + + // Now that the track has been added to the lists in insertTrack2(), + // if it's a dssi synth, OSC can find the synth, and initialize (and show) its native gui. + // No, initializing OSC without actually showing the gui doesn't work, at least for + // dssi-vst plugins - without showing the gui they exit after ten seconds. + //initGui(); + showGui(startgui); setGeometry(r.x(), r.y(), r.width(), r.height()); mapRackPluginsToControllers(); + // Now that the track has been added to the lists in insertTrack2(), if it's a dssi synth + // OSC can find the track and its plugins, and start their native guis if required... + showPendingPluginNativeGuis(); + return; } default: diff --git a/muse/muse/synth.h b/muse/muse/synth.h index 1841a50d..0bca9a75 100644 --- a/muse/muse/synth.h +++ b/muse/muse/synth.h @@ -9,7 +9,9 @@ #define __SYNTH_H__ #include +#include #include +#include #include "globals.h" #include "node.h" @@ -17,6 +19,7 @@ #include "mididev.h" #include "midiport.h" #include "track.h" +#include "stringparam.h" //class MidiEvent; class MidiPlayEvent; @@ -49,14 +52,16 @@ class Synth { //virtual const char* description() const { return ""; } //virtual const char* version() const { return ""; } - int instances() const { return _instances; } - virtual void incInstances(int val) { _instances += val; } - QString baseName() const { return info.baseName(true); } - QString name() const { return _name; } - QString path() const { return info.dirPath(true); } - QString description() const { return _description; } - QString version() const { return _version; } - QString maker() const { return _version; } + int instances() const { return _instances; } + virtual void incInstances(int val) { _instances += val; } + QString baseName(bool complete = true) const { return info.baseName(complete); } + QString name() const { return _name; } + QString dirPath(bool complete = true) const { return info.dirPath(complete); } + QString filePath() const { return info.filePath(); } + QString description() const { return _description; } + QString version() const { return _version; } + //QString maker() const { return _version; } ?? + QString maker() const { return _maker; } //virtual void* instantiate() = 0; @@ -104,12 +109,14 @@ class SynthIF { SynthIF(SynthI* s) { synti = s; } virtual ~SynthIF() {} + virtual bool initGui() = 0; + virtual void guiHeartBeat() = 0; virtual bool guiVisible() const = 0; virtual void showGui(bool v) = 0; virtual bool hasGui() const = 0; virtual void getGeometry(int*, int*, int*, int*) const = 0; virtual void setGeometry(int, int, int, int) = 0; - virtual void preProcessAlways() { }; + virtual void preProcessAlways() = 0; virtual iMPEvent getData(MidiPort*, MPEventList*, iMPEvent, unsigned pos, int ports, unsigned n, float** buffer) = 0; virtual bool putEvent(const MidiPlayEvent& ev) = 0; virtual MidiPlayEvent receiveEvent() = 0; @@ -125,7 +132,8 @@ class SynthIF { virtual const char* getPatchName(int, int, MType, bool) = 0; virtual void populatePatchPopup(QPopupMenu*, int, MType, bool) = 0; virtual void write(int level, Xml& xml) const = 0; - virtual void setParameter(int idx, float value) = 0; + virtual float getParameter(unsigned long idx) = 0; + virtual void setParameter(unsigned long idx, float value) = 0; virtual int getControllerInfo(int id, const char** name, int* ctrl, int* min, int* max, int* initval) = 0; }; @@ -144,14 +152,26 @@ class SynthI : public AudioTrack, public MidiDevice, protected: Synth* synthesizer; - std::vector initParams; MidiFifo putFifo; + + // List of initial floating point parameters, for synths which use them. + // Used once upon song reload, then discarded. + std::vector initParams; + // List of gui controls to update upon heartbeat. + std::vector _guiUpdateControls; + // Update gui program upon heartbeat. + bool _guiUpdateProgram; + // Initial, and running, string parameters for synths which use them, like dssi. + StringParamMap _stringParamMap; + // Current bank and program for synths which use them, like dssi. + // In cases like dssi which have no 'hi' and 'lo' bank, just use _curBankL. + unsigned long _curBankH; + unsigned long _curBankL; + unsigned long _curProgram; void preProcessAlways(); bool getData(unsigned a, int b, unsigned c, float** data); - std::vector guiUpdateControls; // List of gui controls to update upon heartbeat. - //bool putEvent(const MidiPlayEvent& ev); virtual QString open(); @@ -178,6 +198,7 @@ class SynthI : public AudioTrack, public MidiDevice, SynthIF* sif() const { return _sif; } bool initInstance(Synth* s, const QString& instanceName); + void readProgram(Xml&, const QString&); void read(Xml&); virtual void write(int, Xml&) const; @@ -194,8 +215,13 @@ class SynthI : public AudioTrack, public MidiDevice, virtual void populatePatchPopup(QPopupMenu* m, int i, MType t, bool d) { _sif->populatePatchPopup(m, i, t, d); } - void setParameter(const char* name, const char* value) const; + + // void setParameter(const char* name, const char* value) const; // Not required + //StringParamMap& stringParameters() { return _stringParamMap; } // Not required + void currentProg(unsigned long */*prog*/, unsigned long */*bankL*/, unsigned long */*bankH*/); + void guiHeartBeat() { return _sif->guiHeartBeat(); } + bool initGui() const { return _sif->initGui(); } bool guiVisible() const { return _sif->guiVisible(); } void showGui(bool v) { _sif->showGui(v); } bool hasGui() const { return _sif->hasGui(); } @@ -229,6 +255,8 @@ class MessSynthIF : public SynthIF { MessSynthIF(SynthI* s) : SynthIF(s) { _mess = 0; } virtual ~MessSynthIF() { } + virtual bool initGui() { return true; }; + virtual void guiHeartBeat() { } virtual bool guiVisible() const; virtual void showGui(bool v); virtual bool hasGui() const; @@ -250,7 +278,8 @@ class MessSynthIF : public SynthIF { virtual const char* getPatchName(int, int, MType, bool); virtual void populatePatchPopup(QPopupMenu*, int, MType, bool); virtual void write(int level, Xml& xml) const; - virtual void setParameter(int, float) {} + virtual float getParameter(unsigned long) { return 0.0; } + virtual void setParameter(unsigned long, float) {} virtual int getControllerInfo(int id, const char** name, int* ctrl, int* min, int* max, int* initval); }; diff --git a/muse/muse/ticksynth.cpp b/muse/muse/ticksynth.cpp index 2cd0ae82..eb360c17 100644 --- a/muse/muse/ticksynth.cpp +++ b/muse/muse/ticksynth.cpp @@ -61,11 +61,14 @@ class MetronomeSynthIF : public SynthIF MetronomeSynthIF(SynthI* s) : SynthIF(s) { data = 0; } + virtual bool initGui() { return true; }; + virtual void guiHeartBeat() { } virtual bool guiVisible() const { return false; } virtual void showGui(bool) {} virtual bool hasGui() const { return false; } virtual void getGeometry(int*, int*, int*, int*) const {} virtual void setGeometry(int, int, int, int) {} + virtual void preProcessAlways() { }; virtual iMPEvent getData(MidiPort*, MPEventList*, iMPEvent, unsigned pos, int ports, unsigned n, float** buffer); virtual bool putEvent(const MidiPlayEvent& ev); virtual MidiPlayEvent receiveEvent() { return MidiPlayEvent(); } @@ -81,7 +84,8 @@ class MetronomeSynthIF : public SynthIF virtual const char* getPatchName(int, int, MType, bool) { return ""; } virtual void populatePatchPopup(QPopupMenu*, int, MType, bool) {}; virtual void write(int, Xml&) const {} - virtual void setParameter(int, float) {} + virtual float getParameter(unsigned long) { return 0.0; } + virtual void setParameter(unsigned long, float) {} virtual int getControllerInfo(int, const char**, int*, int*, int*, int*) { return 0; } }; diff --git a/muse/muse/track.h b/muse/muse/track.h index 9449fd1b..c18d806f 100644 --- a/muse/muse/track.h +++ b/muse/muse/track.h @@ -321,6 +321,7 @@ class AudioTrack : public Track { void writeProperties(int, Xml&) const; void mapRackPluginsToControllers(); + void showPendingPluginNativeGuis(); //virtual AudioTrack* clone() const = 0; virtual AudioTrack* clone(bool cloneParts) const = 0; diff --git a/muse/muse/vst.cpp b/muse/muse/vst.cpp index 194e1993..74e34efa 100644 --- a/muse/muse/vst.cpp +++ b/muse/muse/vst.cpp @@ -513,11 +513,20 @@ void VstSynthIF::deactivate3() } } +//--------------------------------------------------------- +// getParameter +//--------------------------------------------------------- + +float VstSynthIF::getParameter(unsigned long idx) + { + return _fst->plugin->getParameter(_fst->plugin, idx); + } + //--------------------------------------------------------- // setParameter //--------------------------------------------------------- -void VstSynthIF::setParameter(int idx, float value) +void VstSynthIF::setParameter(unsigned long idx, float value) { _fst->plugin->setParameter(_fst->plugin, idx, value); } diff --git a/muse/muse/vst.h b/muse/muse/vst.h index 33eaaab3..51eade15 100644 --- a/muse/muse/vst.h +++ b/muse/muse/vst.h @@ -50,11 +50,14 @@ class VstSynthIF : public SynthIF _guiVisible = false; } + virtual bool initGui() { return true; }; + virtual void guiHeartBeat() { } virtual bool guiVisible() const; virtual void showGui(bool v); virtual bool hasGui() const; virtual void getGeometry(int*, int*, int*, int*) const {} virtual void setGeometry(int, int, int, int) {} + virtual void preProcessAlways() { }; virtual iMPEvent getData(MidiPort*, MPEventList*, iMPEvent, unsigned pos, int ports, unsigned n, float** buffer) ; virtual bool putEvent(const MidiPlayEvent& ev); virtual MidiPlayEvent receiveEvent(); @@ -68,7 +71,8 @@ class VstSynthIF : public SynthIF virtual const char* getPatchName(int, int, MType, bool) { return ""; } virtual void populatePatchPopup(QPopupMenu*, int, MType, bool) {}; virtual void write(int level, Xml& xml) const; - virtual void setParameter(int idx, float value); + virtual float getParameter(unsigned long idx); + virtual void setParameter(unsigned long idx, float value); virtual int getControllerInfo(int, const char**, int*, int*, int*, int*) { return 0; } }; -- cgit v1.2.3