summaryrefslogtreecommitdiff
path: root/muse2/muse
diff options
context:
space:
mode:
authorTim E. Real <termtech@rogers.com>2012-12-07 07:41:24 +0000
committerTim E. Real <termtech@rogers.com>2012-12-07 07:41:24 +0000
commit0a919a7b36ee4b58e5ce3628a2d8b97bf751d2c4 (patch)
tree335fccddc229d2d0299095dfe46daae614188c79 /muse2/muse
parenta9cef6554f73892b6c7ff6a7a44d8e72f06aa16f (diff)
Feature: Native VST instruments support. PLEASE SEE ChangeLog.
Diffstat (limited to 'muse2/muse')
-rw-r--r--muse2/muse/CMakeLists.txt1
-rw-r--r--muse2/muse/arranger/tlist.cpp14
-rw-r--r--muse2/muse/audiotrack.cpp122
-rw-r--r--muse2/muse/controlfifo.h4
-rw-r--r--muse2/muse/dssihost.cpp64
-rw-r--r--muse2/muse/dssihost.h19
-rw-r--r--muse2/muse/globals.cpp1
-rw-r--r--muse2/muse/globals.h1
-rw-r--r--muse2/muse/helper.cpp8
-rw-r--r--muse2/muse/main.cpp11
-rw-r--r--muse2/muse/osc.cpp11
-rw-r--r--muse2/muse/plugin.cpp2
-rw-r--r--muse2/muse/plugin.h4
-rw-r--r--muse2/muse/synth.cpp58
-rw-r--r--muse2/muse/synth.h47
-rw-r--r--muse2/muse/track.cpp18
-rw-r--r--muse2/muse/vst_native.cpp2628
-rw-r--r--muse2/muse/vst_native.h281
-rw-r--r--muse2/muse/widgets/CMakeLists.txt2
-rw-r--r--muse2/muse/widgets/vst_native_editor.cpp223
-rw-r--r--muse2/muse/widgets/vst_native_editor.h91
21 files changed, 3463 insertions, 147 deletions
diff --git a/muse2/muse/CMakeLists.txt b/muse2/muse/CMakeLists.txt
index 36645db4..574a84cf 100644
--- a/muse2/muse/CMakeLists.txt
+++ b/muse2/muse/CMakeLists.txt
@@ -138,6 +138,7 @@ file (GLOB core_source_files
undo.cpp
value.cpp
vst.cpp
+ vst_native.cpp
wave.cpp
waveevent.cpp
wavetrack.cpp
diff --git a/muse2/muse/arranger/tlist.cpp b/muse2/muse/arranger/tlist.cpp
index 3bc13934..31c2a7a3 100644
--- a/muse2/muse/arranger/tlist.cpp
+++ b/muse2/muse/arranger/tlist.cpp
@@ -1175,7 +1175,7 @@ void TList::oportPropertyPopupMenu(MusECore::Track* t, int x, int y)
{
if(t->type() == MusECore::Track::AUDIO_SOFTSYNTH)
{
- MusECore::SynthI* synth = (MusECore::SynthI*)t;
+ MusECore::SynthI* synth = static_cast<MusECore::SynthI*>(t);
QMenu* p = new QMenu;
QAction* gact = p->addAction(tr("show gui"));
@@ -1191,7 +1191,7 @@ void TList::oportPropertyPopupMenu(MusECore::Track* t, int x, int y)
// If it has a gui but we don't have OSC, disable the action.
#ifndef OSC_SUPPORT
#ifdef DSSI_SUPPORT
- if(dynamic_cast<MusECore::DssiSynthIF*>(synth->sif()))
+ if(synth->synth() && synth->synth()->synthType() == MusECore::Synth::DSSI_SYNTH)
{
nact->setChecked(false);
nact->setEnabled(false);
@@ -1233,10 +1233,14 @@ void TList::oportPropertyPopupMenu(MusECore::Track* t, int x, int y)
#ifndef OSC_SUPPORT
#ifdef DSSI_SUPPORT
MusECore::MidiDevice* dev = port->device();
- if(dev && dev->isSynti() && (dynamic_cast<MusECore::DssiSynthIF*>(((MusECore::SynthI*)dev)->sif())))
+ if(dev && dev->isSynti())
{
- nact->setChecked(false);
- nact->setEnabled(false);
+ MusECore::SynthI* synth = static_cast<MusECore::SynthI*>(dev);
+ if(synth->synth() && synth->synth()->synthType() == MusECore::Synth::DSSI_SYNTH)
+ {
+ nact->setChecked(false);
+ nact->setEnabled(false);
+ }
}
#endif
#endif
diff --git a/muse2/muse/audiotrack.cpp b/muse2/muse/audiotrack.cpp
index 08240f75..91b07063 100644
--- a/muse2/muse/audiotrack.cpp
+++ b/muse2/muse/audiotrack.cpp
@@ -38,6 +38,7 @@
#include "audiodev.h"
#include "synth.h"
#include "dssihost.h"
+#include "vst_native.h"
#include "app.h"
#include "controlfifo.h"
#include "fastlog.h"
@@ -845,7 +846,7 @@ double AudioTrack::pluginCtrlVal(int ctlID) const
}
else
{
- if(ctlID < (int)genACnum(MAX_PLUGINS, 0)) // The beginning of the special dssi synth controller block.
+ if(ctlID < (int)genACnum(MAX_PLUGINS, 0)) // The beginning of the special synth controller block.
{
_efxPipe->controllersEnabled(ctlID, &en_1, &en_2);
}
@@ -853,20 +854,14 @@ double AudioTrack::pluginCtrlVal(int ctlID) const
{
if(type() == AUDIO_SOFTSYNTH)
{
-#ifdef DSSI_SUPPORT
const SynthI* synth = static_cast<const SynthI*>(this);
- if(synth->synth() && synth->synth()->synthType() == Synth::DSSI_SYNTH)
+ const SynthIF* sif = synth->sif();
+ if(sif)
{
- SynthIF* sif = synth->sif();
- if(sif)
- {
- const DssiSynthIF* dssi_sif = static_cast<const DssiSynthIF*>(sif);
- int in_ctrl_idx = ctlID & AC_PLUGIN_CTL_ID_MASK;
- en_1 = dssi_sif->controllerEnabled(in_ctrl_idx);
- en_2 = dssi_sif->controllerEnabled2(in_ctrl_idx);
- }
+ int in_ctrl_idx = ctlID & AC_PLUGIN_CTL_ID_MASK;
+ en_1 = sif->controllerEnabled(in_ctrl_idx);
+ en_2 = sif->controllerEnabled2(in_ctrl_idx);
}
-#endif
}
}
}
@@ -905,25 +900,19 @@ bool AudioTrack::addScheduledControlEvent(int track_ctrl_id, float val, unsigned
}
else
{
- if(track_ctrl_id < (int)genACnum(MAX_PLUGINS, 0)) // The beginning of the special dssi synth controller block.
+ if(track_ctrl_id < (int)genACnum(MAX_PLUGINS, 0)) // The beginning of the special synth controller block.
return _efxPipe->addScheduledControlEvent(track_ctrl_id, val, frame);
else
{
if(type() == AUDIO_SOFTSYNTH)
{
- #ifdef DSSI_SUPPORT
const SynthI* synth = static_cast<const SynthI*>(this);
- if(synth->synth() && synth->synth()->synthType() == Synth::DSSI_SYNTH)
+ SynthIF* sif = synth->sif();
+ if(sif)
{
- SynthIF* sif = synth->sif();
- if(sif)
- {
- DssiSynthIF* dssi_sif = static_cast<DssiSynthIF*>(sif);
- int in_ctrl_idx = track_ctrl_id & AC_PLUGIN_CTL_ID_MASK;
- return dssi_sif->addScheduledControlEvent(in_ctrl_idx, val, frame);
- }
+ int in_ctrl_idx = track_ctrl_id & AC_PLUGIN_CTL_ID_MASK;
+ return sif->addScheduledControlEvent(in_ctrl_idx, val, frame);
}
- #endif
}
}
}
@@ -949,25 +938,19 @@ void AudioTrack::enableController(int track_ctrl_id, bool en)
}
else
{
- if(track_ctrl_id < (int)genACnum(MAX_PLUGINS, 0)) // The beginning of the special dssi synth controller block.
+ if(track_ctrl_id < (int)genACnum(MAX_PLUGINS, 0)) // The beginning of the special synth controller block.
_efxPipe->enableController(track_ctrl_id, en);
else
{
if(type() == AUDIO_SOFTSYNTH)
{
-#ifdef DSSI_SUPPORT
- SynthI* synth = static_cast<SynthI*>(this);
- if(synth->synth() && synth->synth()->synthType() == Synth::DSSI_SYNTH)
+ const SynthI* synth = static_cast<const SynthI*>(this);
+ SynthIF* sif = synth->sif();
+ if(sif)
{
- SynthIF* sif = synth->sif();
- if(sif)
- {
- DssiSynthIF* dssi_sif = static_cast<DssiSynthIF*>(sif);
- int in_ctrl_idx = track_ctrl_id & AC_PLUGIN_CTL_ID_MASK;
- dssi_sif->enableController(in_ctrl_idx, en);
- }
+ int in_ctrl_idx = track_ctrl_id & AC_PLUGIN_CTL_ID_MASK;
+ sif->enableController(in_ctrl_idx, en);
}
-#endif
}
}
}
@@ -996,7 +979,7 @@ void AudioTrack::controllersEnabled(int track_ctrl_id, bool* en1, bool* en2) con
}
else
{
- if(track_ctrl_id < (int)genACnum(MAX_PLUGINS, 0)) // The beginning of the special dssi synth controller block.
+ if(track_ctrl_id < (int)genACnum(MAX_PLUGINS, 0)) // The beginning of the special synth controller block.
{
_efxPipe->controllersEnabled(track_ctrl_id, &en_1, &en_2);
}
@@ -1004,20 +987,14 @@ void AudioTrack::controllersEnabled(int track_ctrl_id, bool* en1, bool* en2) con
{
if(type() == AUDIO_SOFTSYNTH)
{
-#ifdef DSSI_SUPPORT
const SynthI* synth = static_cast<const SynthI*>(this);
- if(synth->synth() && synth->synth()->synthType() == Synth::DSSI_SYNTH)
+ const SynthIF* sif = synth->sif();
+ if(sif)
{
- SynthIF* sif = synth->sif();
- if(sif)
- {
- const DssiSynthIF* dssi_sif = static_cast<const DssiSynthIF*>(sif);
- int in_ctrl_idx = track_ctrl_id & AC_PLUGIN_CTL_ID_MASK;
- en_1 = dssi_sif->controllerEnabled(in_ctrl_idx);
- en_2 = dssi_sif->controllerEnabled2(in_ctrl_idx);
- }
+ int in_ctrl_idx = track_ctrl_id & AC_PLUGIN_CTL_ID_MASK;
+ en_1 = sif->controllerEnabled(in_ctrl_idx);
+ en_2 = sif->controllerEnabled2(in_ctrl_idx);
}
-#endif
}
}
}
@@ -1204,38 +1181,24 @@ bool AudioTrack::readProperties(Xml& xml, const QString& tag)
// controls would all be set to zero.
// But we will allow for the (unintended, useless) possibility of a controller
// with no matching plugin control.
- PluginIBase* p = 0;
+ const PluginIBase* p = 0;
bool ctlfound = false;
unsigned m = l->id() & AC_PLUGIN_CTL_ID_MASK;
int n = (l->id() >> AC_PLUGIN_CTL_BASE_POW) - 1;
if(n >= 0 && n < PipelineDepth)
- {
p = (*_efxPipe)[n];
- if(p && m < p->parameters())
- ctlfound = true;
- }
- // Support a special block for dssi synth ladspa controllers.
+ // Support a special block for synth controllers.
else if(n == MAX_PLUGINS && type() == AUDIO_SOFTSYNTH)
{
- SynthI* synti = dynamic_cast < SynthI* > (this);
- if(synti)
- {
- SynthIF* sif = synti->sif();
- if(sif)
- {
-#ifdef DSSI_SUPPORT
- DssiSynthIF* dsif = dynamic_cast < DssiSynthIF* > (sif);
- if(dsif)
- {
- p = dsif;
- if(p && m < p->parameters())
- ctlfound = true;
- }
-#endif
- }
- }
+ const SynthI* synti = static_cast < SynthI* > (this);
+ const SynthIF* sif = synti->sif();
+ if(sif)
+ p = static_cast < const PluginIBase* > (sif);
}
+ if(p && m < p->parameters())
+ ctlfound = true;
+
iCtrlList icl = _controller.find(l->id());
if (icl == _controller.end())
_controller.add(l);
@@ -1382,22 +1345,13 @@ void AudioTrack::mapRackPluginsToControllers()
const PluginIBase* p = 0;
if(idx >= 0 && idx < PipelineDepth)
p = (*_efxPipe)[idx];
- // Support a special block for dssi synth ladspa controllers.
+ // Support a special block for synth controllers.
else if(idx == MAX_PLUGINS && type() == AUDIO_SOFTSYNTH)
{
- const SynthI* synti = dynamic_cast < const SynthI* > (this);
- if(synti)
- {
- SynthIF* sif = synti->sif();
- if(sif)
- {
-#ifdef DSSI_SUPPORT
- const DssiSynthIF* dsif = dynamic_cast < const DssiSynthIF* > (sif);
- if(dsif)
- p = dsif;
-#endif
- }
- }
+ const SynthI* synti = static_cast < const SynthI* > (this);
+ SynthIF* sif = synti->sif();
+ if(sif)
+ p = static_cast < const PluginIBase* > (sif);
}
// If there's no plugin at that rack position, or the param is out of range of
diff --git a/muse2/muse/controlfifo.h b/muse2/muse/controlfifo.h
index 6a35c8a5..6ee2cedb 100644
--- a/muse2/muse/controlfifo.h
+++ b/muse2/muse/controlfifo.h
@@ -36,7 +36,9 @@ struct ControlEvent
// Unique: Whether the item must not be skipped, even if it has the same
// (possibly rounded) frame and index as the previous item. This is mainly for
// dssi-vst guis, they require acknowledgment of every message.
- bool unique;
+ bool unique;
+ // Whether or not the event is from a synth or effect's own GUI.
+ bool fromGui;
unsigned long idx;
float value;
unsigned long frame;
diff --git a/muse2/muse/dssihost.cpp b/muse2/muse/dssihost.cpp
index 70db2a47..f62d1434 100644
--- a/muse2/muse/dssihost.cpp
+++ b/muse2/muse/dssihost.cpp
@@ -157,7 +157,7 @@ static void scanDSSILib(QFileInfo& fi) // ddskrjo removed const for argument
// scanVstDir
//---------------------------------------------------------
-static void scanDSSIDir(QString& s) // ddskrjo removed const for argument
+static void scanDSSIDir(const QString& s)
{
if(MusEGlobal::debugMsg)
printf("scanDSSIDir: scan DSSI plugin dir <%s>\n", s.toLatin1().constData());
@@ -202,8 +202,7 @@ void initDSSI()
char* buffer = new char[n + 1];
strncpy(buffer, p, n);
buffer[n] = '\0';
- QString tmpStr(buffer);
- scanDSSIDir(tmpStr);
+ scanDSSIDir(QString(buffer));
delete[] buffer;
}
p = pe;
@@ -270,7 +269,8 @@ DssiSynth::DssiSynth(QFileInfo& fi, const DSSI_Descriptor* d) : // ddskrjo remov
DssiSynth::~DssiSynth()
{
if(dssi)
- delete dssi;
+ // delete dssi;
+ printf("DssiSynth::~DssiSynth Error: dssi descriptor is not NULL\n");
}
//---------------------------------------------------------
@@ -650,9 +650,8 @@ bool DssiSynthIF::init(DssiSynth* s)
}
}
}
-
- if (ld->activate)
- ld->activate(handle);
+
+ activate();
// Set current configuration values.
if(dssi->configure)
@@ -1988,6 +1987,7 @@ int DssiSynthIF::oscControl(unsigned long port, float value)
// p4.0.21
ControlEvent ce;
ce.unique = synth->_isDssiVst; // Special for messages from vst gui to host - requires processing every message.
+ ce.fromGui = true; // It came form the plugin's own GUI.
ce.idx = cport;
ce.value = value;
@@ -2305,21 +2305,27 @@ int DssiSynthIF::totalInChannels() const
return synth->_inports;
}
+void DssiSynthIF::deactivate3()
+{
+ deactivate();
+}
+
+
//--------------------------------
// Methods for PluginIBase:
//--------------------------------
-bool DssiSynthIF::on() const { return true; } // Synth is not part of a rack plugin chain. Always on.
-void DssiSynthIF::setOn(bool /*val*/) { }
+//bool DssiSynthIF::on() const { return true; } // Synth is not part of a rack plugin chain. Always on.
+//void DssiSynthIF::setOn(bool /*val*/) { }
unsigned long DssiSynthIF::pluginID() { return (synth && synth->dssi) ? synth->dssi->LADSPA_Plugin->UniqueID : 0; }
int DssiSynthIF::id() { return MAX_PLUGINS; } // Set for special block reserved for dssi synth. p4.0.20
QString DssiSynthIF::pluginLabel() const { return (synth && synth->dssi) ? QString(synth->dssi->LADSPA_Plugin->Label) : QString(); }
-QString DssiSynthIF::name() const { return synti->name(); }
+//QString DssiSynthIF::name() const { return synti->name(); }
QString DssiSynthIF::lib() const { return synth ? synth->completeBaseName() : QString(); }
QString DssiSynthIF::dirPath() const { return synth ? synth->absolutePath() : QString(); }
QString DssiSynthIF::fileName() const { return synth ? synth->fileName() : QString(); }
-QString DssiSynthIF::titlePrefix() const { return QString(); }
-MusECore::AudioTrack* DssiSynthIF::track() { return (MusECore::AudioTrack*)synti; }
+//QString DssiSynthIF::titlePrefix() const { return QString(); }
+//MusECore::AudioTrack* DssiSynthIF::track() { return (MusECore::AudioTrack*)synti; }
void DssiSynthIF::enableController(unsigned long i, bool v) { controls[i].enCtrl = v; }
bool DssiSynthIF::controllerEnabled(unsigned long i) const { return controls[i].enCtrl; }
void DssiSynthIF::enable2Controller(unsigned long i, bool v) { controls[i].en2Ctrl = v; }
@@ -2338,10 +2344,38 @@ void DssiSynthIF::enable2AllControllers(bool v)
for(unsigned long i = 0; i < synth->_controlInPorts; ++i)
controls[i].en2Ctrl = v;
}
+void DssiSynthIF::updateControllers() { }
+void DssiSynthIF::activate()
+{
+ if(synth && synth->dssi && synth->dssi->LADSPA_Plugin && synth->dssi->LADSPA_Plugin->activate)
+ //for (int i = 0; i < instances; ++i)
+ // _plugin->activate(handle[i]);
+ synth->dssi->LADSPA_Plugin->activate(handle);
+
+// REMOVE Tim. Or keep? From PluginI::activate().
+// if (initControlValues) {
+// for (unsigned long i = 0; i < controlPorts; ++i) {
+// controls[i].val = controls[i].tmpVal;
+// }
+// }
+// else {
+// // get initial control values from plugin
+// for (unsigned long i = 0; i < controlPorts; ++i) {
+// controls[i].tmpVal = controls[i].val;
+// }
+// }
+}
+void DssiSynthIF::deactivate()
+{
+ if(!synth || !synth->dssi || !synth->dssi->LADSPA_Plugin ||!synth->dssi->LADSPA_Plugin->deactivate)
+ return;
+ //for (int i = 0; i < instances; ++i)
+ // synth->dssi->LADSPA_Plugin->deactivate(handle[i]);
+ synth->dssi->LADSPA_Plugin->deactivate(handle);
+}
-void DssiSynthIF::updateControllers() { }
-void DssiSynthIF::writeConfiguration(int /*level*/, Xml& /*xml*/) { }
-bool DssiSynthIF::readConfiguration(Xml& /*xml*/, bool /*readPreset*/) { return false; }
+//void DssiSynthIF::writeConfiguration(int /*level*/, Xml& /*xml*/) { }
+//bool DssiSynthIF::readConfiguration(Xml& /*xml*/, bool /*readPreset*/) { return false; }
unsigned long DssiSynthIF::parameters() const { return synth ? synth->_controlInPorts : 0; }
unsigned long DssiSynthIF::parametersOut() const { return synth ? synth->_controlOutPorts : 0; }
diff --git a/muse2/muse/dssihost.h b/muse2/muse/dssihost.h
index baa6adbe..99626c92 100644
--- a/muse2/muse/dssihost.h
+++ b/muse2/muse/dssihost.h
@@ -114,7 +114,7 @@ class DssiSynth : public Synth {
// VSTi synthesizer instance
//---------------------------------------------------------
-class DssiSynthIF : public SynthIF, public PluginIBase
+class DssiSynthIF : public SynthIF
{
DssiSynth* synth;
LADSPA_Handle handle;
@@ -143,6 +143,8 @@ class DssiSynthIF : public SynthIF, public PluginIBase
virtual ~DssiSynthIF();
+ bool init(DssiSynth* s);
+
virtual DssiSynth* dssiSynth() { return synth; }
virtual SynthI* dssiSynthI() { return synti; }
@@ -169,7 +171,7 @@ class DssiSynthIF : public SynthIF, public PluginIBase
virtual int totalOutChannels() const;
virtual int totalInChannels() const;
- virtual void deactivate3() {}
+ virtual void deactivate3();
virtual const char* getPatchName(int, int, bool);
virtual void populatePatchPopup(MusEGui::PopupMenu*, int, bool);
@@ -179,11 +181,8 @@ class DssiSynthIF : public SynthIF, public PluginIBase
virtual float getParameter(unsigned long /*idx*/) const;
virtual float getParameterOut(unsigned long n) const;
virtual void setParameter(unsigned long /*idx*/, float /*value*/);
-
virtual int getControllerInfo(int, const char**, int*, int*, int*, int*);
- bool init(DssiSynth* s);
-
#ifdef OSC_SUPPORT
OscDssiIF& oscIF() { return _oscif; }
int oscProgram(unsigned long prog, unsigned long bank);
@@ -196,17 +195,13 @@ class DssiSynthIF : public SynthIF, public PluginIBase
//-------------------------
// Methods for PluginIBase:
//-------------------------
- bool on() const;
- void setOn(bool val);
+
unsigned long pluginID();
int id();
QString pluginLabel() const;
- QString name() const;
QString lib() const;
QString dirPath() const;
QString fileName() const;
- QString titlePrefix() const;
- MusECore::AudioTrack* track();
void enableController(unsigned long i, bool v = true);
bool controllerEnabled(unsigned long i) const;
void enable2Controller(unsigned long i, bool v = true);
@@ -214,8 +209,8 @@ class DssiSynthIF : public SynthIF, public PluginIBase
void enableAllControllers(bool v = true);
void enable2AllControllers(bool v = true);
void updateControllers();
- void writeConfiguration(int level, Xml& xml);
- bool readConfiguration(Xml& xml, bool readPreset=false);
+ void activate();
+ void deactivate();
unsigned long parameters() const;
unsigned long parametersOut() const;
diff --git a/muse2/muse/globals.cpp b/muse2/muse/globals.cpp
index b61cda6d..831558f4 100644
--- a/muse2/muse/globals.cpp
+++ b/muse2/muse/globals.cpp
@@ -111,6 +111,7 @@ int realTimePriority = 40; // 80
int midiRTPrioOverride = -1;
bool loadPlugins = true;
bool loadVST = true;
+bool loadNativeVST = true;
bool loadDSSI = true;
bool usePythonBridge = false;
bool useLASH = true;
diff --git a/muse2/muse/globals.h b/muse2/muse/globals.h
index da8221b9..3eb941a7 100644
--- a/muse2/muse/globals.h
+++ b/muse2/muse/globals.h
@@ -81,6 +81,7 @@ extern bool heavyDebugMsg;
extern bool debugSync;
extern bool loadPlugins;
extern bool loadVST;
+extern bool loadNativeVST;
extern bool loadDSSI;
extern bool usePythonBridge;
extern bool useLASH;
diff --git a/muse2/muse/helper.cpp b/muse2/muse/helper.cpp
index 1c4db8c7..0d9b74bd 100644
--- a/muse2/muse/helper.cpp
+++ b/muse2/muse/helper.cpp
@@ -50,14 +50,6 @@
#include <QFileDialog>
#include <QString>
-#ifdef DSSI_SUPPORT
-#include "dssihost.h"
-#endif
-
-#ifdef VST_SUPPORT
-#include "vst.h"
-#endif
-
using std::set;
namespace MusEGlobal {
diff --git a/muse2/muse/main.cpp b/muse2/muse/main.cpp
index ceb245ba..e233a28e 100644
--- a/muse2/muse/main.cpp
+++ b/muse2/muse/main.cpp
@@ -71,6 +71,7 @@ extern void initMidiController();
extern void initMetronome();
extern void initOSC();
extern void initVST();
+extern void initVST_Native();
extern void initPlugins();
extern void initDSSI();
extern void readConfiguration();
@@ -238,6 +239,9 @@ static void usage(const char* prog, const char* txt)
#ifdef VST_SUPPORT
fprintf(stderr, " -V Don't load VST plugins\n");
#endif
+#ifdef VST_NATIVE_SUPPORT
+ fprintf(stderr, " -N Don't load Native VST plugins\n");
+#endif
#ifdef DSSI_SUPPORT
fprintf(stderr, " -I Don't load DSSI plugins\n");
#endif
@@ -352,6 +356,9 @@ int main(int argc, char* argv[])
#ifdef VST_SUPPORT
optstr += QString("V");
#endif
+#ifdef VST_NATIVE_SUPPORT
+ optstr += QString("N");
+#endif
#ifdef DSSI_SUPPORT
optstr += QString("I");
#endif
@@ -398,6 +405,7 @@ int main(int argc, char* argv[])
case 'Y': MusEGlobal::midiRTPrioOverride = atoi(optarg); break;
case 'p': MusEGlobal::loadPlugins = false; break;
case 'V': MusEGlobal::loadVST = false; break;
+ case 'N': MusEGlobal::loadNativeVST = false; break;
case 'I': MusEGlobal::loadDSSI = false; break;
case 'L': MusEGlobal::useLASH = false; break;
case 'y': MusEGlobal::usePythonBridge = true; break;
@@ -650,6 +658,9 @@ int main(int argc, char* argv[])
if (MusEGlobal::loadVST)
MusECore::initVST();
+ if (MusEGlobal::loadNativeVST)
+ MusECore::initVST_Native();
+
if(MusEGlobal::loadDSSI)
MusECore::initDSSI();
diff --git a/muse2/muse/osc.cpp b/muse2/muse/osc.cpp
index 381e4acc..0f15083b 100644
--- a/muse2/muse/osc.cpp
+++ b/muse2/muse/osc.cpp
@@ -173,12 +173,11 @@ int oscMessageHandler(const char* path, const char* types, lo_arg** argv,
const char* sub = strstr(p, ba.constData());
if(sub == NULL)
continue;
-
- // TODO: Fix this dynamic cast - it may be a slowdown.
- DssiSynthIF* instance = dynamic_cast<DssiSynthIF*>(synti->sif());
- if(!instance)
- break;
-
+
+ if(!synti->sif() || !synti->synth() || synti->synth()->synthType() != MusECore::Synth::DSSI_SYNTH)
+ continue;
+ DssiSynthIF* instance = static_cast<DssiSynthIF*>(synti->sif());
+
p = sub + ba.length();
if (*p != '/' || *(p + 1) == 0)
{
diff --git a/muse2/muse/plugin.cpp b/muse2/muse/plugin.cpp
index 9dc2005e..0ca7524c 100644
--- a/muse2/muse/plugin.cpp
+++ b/muse2/muse/plugin.cpp
@@ -1575,6 +1575,7 @@ bool PluginIBase::addScheduledControlEvent(unsigned long i, float val, unsigned
}
ControlEvent ce;
ce.unique = false;
+ ce.fromGui = false;
ce.idx = i;
ce.value = val;
// Time-stamp the event. This does a possibly slightly slow call to gettimeofday via timestamp().
@@ -2800,6 +2801,7 @@ int PluginI::oscControl(unsigned long port, float value)
*/
ControlEvent ce;
ce.unique = _plugin->_isDssiVst; // Special for messages from vst gui to host - requires processing every message.
+ ce.fromGui = true; // It came form the plugin's own GUI.
ce.idx = cport;
ce.value = value;
// Time-stamp the event. This does a possibly slightly slow call to gettimeofday via timestamp().
diff --git a/muse2/muse/plugin.h b/muse2/muse/plugin.h
index 25d434ca..822bafbd 100644
--- a/muse2/muse/plugin.h
+++ b/muse2/muse/plugin.h
@@ -274,6 +274,9 @@ class PluginIBase
virtual void enable2AllControllers(bool v = true) = 0;
virtual void updateControllers() = 0;
+ virtual void activate() = 0;
+ virtual void deactivate() = 0;
+
virtual void writeConfiguration(int level, Xml& xml) = 0;
virtual bool readConfiguration(Xml& xml, bool readPreset=false) = 0;
@@ -285,6 +288,7 @@ class PluginIBase
virtual float paramOut(unsigned long i) const = 0;
virtual const char* paramName(unsigned long i) = 0;
virtual const char* paramOutName(unsigned long i) = 0;
+ // FIXME TODO: Either find a way to agnosticize these two ranges, or change them from ladspa ranges to a new MusE range class.
virtual LADSPA_PortRangeHint range(unsigned long i) = 0;
virtual LADSPA_PortRangeHint rangeOut(unsigned long i) = 0;
diff --git a/muse2/muse/synth.cpp b/muse2/muse/synth.cpp
index 840365ee..92c0a27d 100644
--- a/muse2/muse/synth.cpp
+++ b/muse2/muse/synth.cpp
@@ -61,7 +61,7 @@ namespace MusECore {
extern void connectNodes(AudioTrack*, AudioTrack*);
bool SynthI::_isVisible=false;
-const char* synthTypes[] = { "METRONOME", "MESS", "DSSI", "VST", "UNKNOWN" };
+const char* synthTypes[] = { "METRONOME", "MESS", "DSSI", "VST", "VST_NATIVE", "UNKNOWN" };
QString synthType2String(Synth::Type type) { return QString(synthTypes[type]); }
Synth::Type string2SynthType(const QString& type)
@@ -74,6 +74,62 @@ Synth::Type string2SynthType(const QString& type)
return Synth::SYNTH_TYPE_END;
}
+//--------------------------------
+// Methods for PluginIBase:
+//--------------------------------
+
+bool SynthIF::on() const { return true; } // Synth is not part of a rack plugin chain. Always on.
+void SynthIF::setOn(bool /*val*/) { }
+unsigned long SynthIF::pluginID() { return 0; }
+int SynthIF::id() { return MAX_PLUGINS; } // Set for special block reserved for synth.
+QString SynthIF::pluginLabel() const { return QString(); }
+QString SynthIF::name() const { return synti->name(); }
+QString SynthIF::lib() const { return QString(); }
+QString SynthIF::dirPath() const { return QString(); }
+QString SynthIF::fileName() const { return QString(); }
+QString SynthIF::titlePrefix() const { return QString(); }
+MusECore::AudioTrack* SynthIF::track() { return static_cast < MusECore::AudioTrack* > (synti); }
+void SynthIF::enableController(unsigned long, bool) { }
+bool SynthIF::controllerEnabled(unsigned long) const { return true;}
+void SynthIF::enable2Controller(unsigned long, bool) { }
+bool SynthIF::controllerEnabled2(unsigned long) const { return true; }
+void SynthIF::enableAllControllers(bool) { }
+void SynthIF::enable2AllControllers(bool) { }
+void SynthIF::updateControllers() { }
+void SynthIF::activate() { }
+void SynthIF::deactivate() { }
+void SynthIF::writeConfiguration(int /*level*/, Xml& /*xml*/) { }
+bool SynthIF::readConfiguration(Xml& /*xml*/, bool /*readPreset*/) { return false; }
+unsigned long SynthIF::parameters() const { return 0; }
+unsigned long SynthIF::parametersOut() const { return 0; }
+void SynthIF::setParam(unsigned long, float) { }
+float SynthIF::param(unsigned long) const { return 0.0; }
+float SynthIF::paramOut(unsigned long) const { return 0.0; }
+const char* SynthIF::paramName(unsigned long) { return NULL; }
+const char* SynthIF::paramOutName(unsigned long) { return NULL; }
+LADSPA_PortRangeHint SynthIF::range(unsigned long)
+{
+ LADSPA_PortRangeHint h;
+ h.HintDescriptor = 0;
+ h.LowerBound = 0.0;
+ h.UpperBound = 1.0;
+ return h;
+}
+LADSPA_PortRangeHint SynthIF::rangeOut(unsigned long)
+{
+ LADSPA_PortRangeHint h;
+ h.HintDescriptor = 0;
+ h.LowerBound = 0.0;
+ h.UpperBound = 1.0;
+ return h;
+}
+CtrlValueType SynthIF::ctrlValueType(unsigned long) const { return VAL_LINEAR; }
+CtrlList::Mode SynthIF::ctrlMode(unsigned long) const { return CtrlList::INTERPOLATE; };
+
+//-------------------------------------------------------------------------
+
+
+
bool MessSynthIF::nativeGuiVisible() const
{
return _mess ? _mess->nativeGuiVisible() : false;
diff --git a/muse2/muse/synth.h b/muse2/muse/synth.h
index f5ecc9ab..c41d5535 100644
--- a/muse2/muse/synth.h
+++ b/muse2/muse/synth.h
@@ -34,6 +34,7 @@
#include "midiport.h"
#include "track.h"
#include "stringparam.h"
+#include "plugin.h"
#include <QFileInfo>
@@ -67,7 +68,7 @@ class Synth {
QString _version;
public:
- enum Type { METRO_SYNTH=0, MESS_SYNTH, DSSI_SYNTH, VST_SYNTH, SYNTH_TYPE_END };
+ enum Type { METRO_SYNTH=0, MESS_SYNTH, DSSI_SYNTH, VST_SYNTH, VST_NATIVE_SYNTH, SYNTH_TYPE_END };
Synth(const QFileInfo& fi, QString label, QString descr, QString maker, QString ver);
@@ -119,7 +120,7 @@ class MessSynth : public Synth {
// 0xNN The synth's unique ID byte
//---------------------------------------------------------
-class SynthIF {
+class SynthIF : public PluginIBase {
protected:
SynthI* synti;
@@ -159,6 +160,47 @@ class SynthIF {
virtual float getParameter(unsigned long idx) const = 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;
+
+ //-------------------------
+ // Methods for PluginIBase:
+ //-------------------------
+
+ virtual bool on() const;
+ virtual void setOn(bool val);
+ virtual unsigned long pluginID();
+ virtual int id();
+ virtual QString pluginLabel() const;
+ virtual QString name() const;
+ virtual QString lib() const;
+ virtual QString dirPath() const;
+ virtual QString fileName() const;
+ virtual QString titlePrefix() const;
+ virtual MusECore::AudioTrack* track();
+ virtual void enableController(unsigned long i, bool v = true);
+ virtual bool controllerEnabled(unsigned long i) const;
+ virtual void enable2Controller(unsigned long i, bool v = true);
+ virtual bool controllerEnabled2(unsigned long i) const;
+ virtual void enableAllControllers(bool v = true);
+ virtual void enable2AllControllers(bool v = true);
+ virtual void updateControllers();
+ virtual void activate();
+ virtual void deactivate();
+
+ virtual void writeConfiguration(int level, Xml& xml);
+ virtual bool readConfiguration(Xml& xml, bool readPreset=false);
+
+ virtual unsigned long parameters() const;
+ virtual unsigned long parametersOut() const;
+ virtual void setParam(unsigned long i, float val);
+ virtual float param(unsigned long i) const;
+ virtual float paramOut(unsigned long i) const;
+ virtual const char* paramName(unsigned long i);
+ virtual const char* paramOutName(unsigned long i);
+ // FIXME TODO: Either find a way to agnosticize these two ranges, or change them from ladspa ranges to a new MusE range class.
+ virtual LADSPA_PortRangeHint range(unsigned long i);
+ virtual LADSPA_PortRangeHint rangeOut(unsigned long i);
+ virtual CtrlValueType ctrlValueType(unsigned long i) const;
+ virtual CtrlList::Mode ctrlMode(unsigned long i) const;
};
//---------------------------------------------------------
@@ -207,6 +249,7 @@ class SynthI : public AudioTrack, public MidiDevice,
friend class MessSynthIF;
friend class DssiSynthIF;
friend class VstSynthIF;
+ friend class VstNativeSynthIF;
SynthI();
virtual ~SynthI();
diff --git a/muse2/muse/track.cpp b/muse2/muse/track.cpp
index b18a9410..b17515fe 100644
--- a/muse2/muse/track.cpp
+++ b/muse2/muse/track.cpp
@@ -291,7 +291,7 @@ void Track::internal_assign(const Track& t, int flags)
{
// Do not call setName here. Audio Input and Output override it and try to set
// Jack ports, which have not been initialized yet here. Must wait until
- // .Audio Input and Output copy constructors or assign are called.
+ // Audio Input and Output copy constructors or assign are called.
_name = s;
break;
}
@@ -389,18 +389,10 @@ void Track::clearRecAutomation(bool clearList)
if(type() == AUDIO_SOFTSYNTH)
{
-#ifdef DSSI_SUPPORT
- SynthI* synth = static_cast<SynthI*>(this);
- if(synth->synth() && synth->synth()->synthType() == Synth::DSSI_SYNTH)
- {
- SynthIF* sif = synth->sif();
- if(sif)
- {
- DssiSynthIF* dssi_sif = static_cast<DssiSynthIF*>(sif);
- dssi_sif->enableAllControllers(true);
- }
- }
-#endif
+ const SynthI* synth = static_cast<const SynthI*>(this);
+ SynthIF* sif = synth->sif();
+ if(sif)
+ sif->enableAllControllers(true);
}
if(clearList)
diff --git a/muse2/muse/vst_native.cpp b/muse2/muse/vst_native.cpp
new file mode 100644
index 00000000..8febe142
--- /dev/null
+++ b/muse2/muse/vst_native.cpp
@@ -0,0 +1,2628 @@
+//===================================================================
+// MusE
+// Linux Music Editor
+//
+// vst_native.cpp
+// (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net)
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; version 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+//===================================================================
+
+#include "config.h"
+
+#ifdef VST_NATIVE_SUPPORT
+
+#include <QDir>
+#include <QMenu>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <dlfcn.h>
+#include <cmath>
+#include <set>
+#include <jack/jack.h>
+
+#include "globals.h"
+#include "gconfig.h"
+#include "audio.h"
+#include "synth.h"
+#include "jackaudio.h"
+#include "midi.h"
+#include "xml.h"
+#include "plugin.h"
+#include "popupmenu.h"
+
+#include "vst_native.h"
+
+#define OLD_PLUGIN_ENTRY_POINT "main"
+#define NEW_PLUGIN_ENTRY_POINT "VSTPluginMain"
+
+// Enable debugging messages
+//#define VST_NATIVE_DEBUG
+//#define VST_NATIVE_DEBUG_PROCESS
+
+namespace MusECore {
+
+extern JackAudioDevice* jackAudio;
+
+//-----------------------------------------------------------------------------------------
+// vstHostCallback
+// This must be a function, it cannot be a class method so we dispatch to various objects from here.
+//-----------------------------------------------------------------------------------------
+
+VstIntPtr VSTCALLBACK vstNativeHostCallback(AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt)
+{
+ VSTPlugin* plugin;
+ if(effect && effect->user)
+ {
+ plugin = (VSTPlugin*)(effect->user);
+ return ((VstNativeSynthIF*)plugin)->hostCallback(opcode, index, value, ptr, opt);
+ }
+
+#ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "vstNativeHostCallback eff:%p opcode:%ld\n", effect, opcode);
+#endif
+
+ switch (opcode) {
+ case audioMasterAutomate:
+ // index, value, returns 0
+ return 0;
+
+ case audioMasterVersion:
+ // vst version, currently 2 (0 for older)
+ return 2300;
+
+ case audioMasterCurrentId:
+ // returns the unique id of a plug that's currently
+ // loading
+ return 0;
+
+ case audioMasterIdle:
+ // call application idle routine (this will
+ // call effEditIdle for all open editors too)
+ return 0;
+
+ case audioMasterPinConnected:
+ // inquire if an input or output is beeing connected;
+ // index enumerates input or output counting from zero:
+ // value is 0 for input and != 0 otherwise. note: the
+ // return value is 0 for <true> such that older versions
+ // will always return true.
+ return 1;
+
+ case audioMasterWantMidi:
+ // <value> is a filter which is currently ignored
+ return 0;
+
+ case audioMasterGetTime:
+ // returns const VstTimeInfo* (or 0 if not supported)
+ // <value> should contain a mask indicating which fields are required
+ // (see valid masks above), as some items may require extensive
+ // conversions
+ return 0;
+
+ case audioMasterProcessEvents:
+ // VstEvents* in <ptr>
+ return 0;
+
+ case audioMasterSetTime:
+ // VstTimenfo* in <ptr>, filter in <value>, not supported
+ return 0;
+
+ case audioMasterTempoAt:
+ // returns tempo (in bpm * 10000) at sample frame location passed in <value>
+ return 0;
+
+ case audioMasterGetNumAutomatableParameters:
+ return 0;
+
+ case audioMasterGetParameterQuantization:
+ // returns the integer value for +1.0 representation,
+ // or 1 if full single float precision is maintained
+ // in automation. parameter index in <value> (-1: all, any)
+ return 0;
+
+ case audioMasterIOChanged:
+ // numInputs and/or numOutputs has changed
+ return 0;
+
+ case audioMasterNeedIdle:
+ // plug needs idle calls (outside its editor window)
+ return 0;
+
+ case audioMasterSizeWindow:
+ // index: width, value: height
+ return 0;
+
+ case audioMasterGetSampleRate:
+ return MusEGlobal::sampleRate;
+
+ case audioMasterGetBlockSize:
+ return MusEGlobal::segmentSize;
+
+ case audioMasterGetInputLatency:
+ return 0;
+
+ case audioMasterGetOutputLatency:
+ return 0;
+
+ case audioMasterGetPreviousPlug:
+ // input pin in <value> (-1: first to come), returns cEffect*
+ return 0;
+
+ case audioMasterGetNextPlug:
+ // output pin in <value> (-1: first to come), returns cEffect*
+
+ case audioMasterWillReplaceOrAccumulate:
+ // returns: 0: not supported, 1: replace, 2: accumulate
+ return 0;
+
+ case audioMasterGetCurrentProcessLevel:
+ // returns: 0: not supported,
+ // 1: currently in user thread (gui)
+ // 2: currently in audio thread (where process is called)
+ // 3: currently in 'sequencer' thread (midi, timer etc)
+ // 4: currently offline processing and thus in user thread
+ // other: not defined, but probably pre-empting user thread.
+ return 0;
+
+ case audioMasterGetAutomationState:
+ // returns 0: not supported, 1: off, 2:read, 3:write, 4:read/write
+ // offline
+ return 0;
+
+ case audioMasterOfflineStart:
+ case audioMasterOfflineRead:
+ // ptr points to offline structure, see below. return 0: error, 1 ok
+ return 0;
+
+ case audioMasterOfflineWrite:
+ // same as read
+ return 0;
+
+ case audioMasterOfflineGetCurrentPass:
+ case audioMasterOfflineGetCurrentMetaPass:
+ return 0;
+
+ case audioMasterSetOutputSampleRate:
+ // for variable i/o, sample rate in <opt>
+ return 0;
+
+ case audioMasterGetSpeakerArrangement:
+ // (long)input in <value>, output in <ptr>
+ return 0;
+
+ case audioMasterGetVendorString:
+ // fills <ptr> with a string identifying the vendor (max 64 char)
+ strcpy ((char*) ptr, "MusE");
+ return 1;
+
+ case audioMasterGetProductString:
+ // fills <ptr> with a string with product name (max 64 char)
+ strcpy ((char*) ptr, "NativeVST");
+ return 1;
+
+ case audioMasterGetVendorVersion:
+ // returns vendor-specific version
+ return 2000;
+
+ case audioMasterVendorSpecific:
+ // no definition, vendor specific handling
+ return 0;
+
+ case audioMasterSetIcon:
+ // void* in <ptr>, format not defined yet
+ return 0;
+
+ case audioMasterCanDo:
+ // string in ptr, see below
+ return 0;
+
+ case audioMasterGetLanguage:
+ // see enum
+ return kVstLangEnglish;
+
+ case audioMasterOpenWindow:
+ // returns platform specific ptr
+ return 0;
+
+ case audioMasterCloseWindow:
+ // close window, platform specific handle in <ptr>
+ return 0;
+
+ case audioMasterGetDirectory:
+ // get plug directory, FSSpec on MAC, else char*
+ return 0;
+
+ case audioMasterUpdateDisplay:
+ // something has changed, update 'multi-fx' display
+ return 0;
+
+ case audioMasterBeginEdit:
+ // begin of automation session (when mouse down), parameter index in <index>
+ return 0;
+
+ case audioMasterEndEdit:
+ // end of automation session (when mouse up), parameter index in <index>
+ return 0;
+
+ case audioMasterOpenFileSelector:
+ // open a fileselector window with VstFileSelect* in <ptr>
+ return 0;
+
+ case audioMasterCloseFileSelector:
+ return 0;
+
+ default:
+ break;
+ }
+
+ if(MusEGlobal::debugMsg)
+ fprintf(stderr, " unknown opcode\n");
+
+ return 0;
+ }
+
+//---------------------------------------------------------
+// loadPluginLib
+//---------------------------------------------------------
+
+static void scanVstNativeLib(QFileInfo& fi)
+{
+ void* handle = dlopen(fi.filePath().toAscii().constData(), RTLD_NOW);
+ if (handle == NULL)
+ {
+ fprintf(stderr, "scanVstNativeLib: dlopen(%s) failed: %s\n", fi.filePath().toAscii().constData(), dlerror());
+ return;
+ }
+
+ char buffer[128];
+ QString effectName;
+ QString vendorString;
+ QString productString;
+ std::vector<Synth*>::iterator is;
+ int vst_version = 0;
+ VstNativeSynth* new_synth = NULL;
+
+ AEffect *(*getInstance)(audioMasterCallback);
+ getInstance = (AEffect*(*)(audioMasterCallback))dlsym(handle, NEW_PLUGIN_ENTRY_POINT);
+ if(!getInstance)
+ {
+ if(MusEGlobal::debugMsg)
+ {
+ fprintf(stderr, "VST 2.4 entrypoint \"" NEW_PLUGIN_ENTRY_POINT "\" not found in library %s, looking for \""
+ OLD_PLUGIN_ENTRY_POINT "\"\n", fi.filePath().toAscii().constData());
+ }
+
+ getInstance = (AEffect*(*)(audioMasterCallback))dlsym(handle, OLD_PLUGIN_ENTRY_POINT);
+ if(!getInstance)
+ {
+ fprintf(stderr, "ERROR: VST entrypoints \"" NEW_PLUGIN_ENTRY_POINT "\" or \""
+ OLD_PLUGIN_ENTRY_POINT "\" not found in library\n");
+ dlclose(handle);
+ return;
+ }
+ else if(MusEGlobal::debugMsg)
+ {
+ fprintf(stderr, "VST entrypoint \"" OLD_PLUGIN_ENTRY_POINT "\" found\n");
+ }
+ }
+ else if(MusEGlobal::debugMsg)
+ {
+ fprintf(stderr, "VST entrypoint \"" NEW_PLUGIN_ENTRY_POINT "\" found\n");
+ }
+
+ AEffect *plugin = getInstance(vstNativeHostCallback);
+ if(!plugin)
+ {
+ fprintf(stderr, "ERROR: Failed to instantiate plugin in VST library \"%s\"\n", fi.filePath().toAscii().constData());
+ dlclose(handle);
+ return;
+ }
+ else if(MusEGlobal::debugMsg)
+ fprintf(stderr, "plugin instantiated\n");
+
+ if(plugin->magic != kEffectMagic)
+ {
+ fprintf(stderr, "Not a VST plugin in library \"%s\"\n", fi.filePath().toAscii().constData());
+ dlclose(handle);
+ return;
+ }
+ else if(MusEGlobal::debugMsg)
+ fprintf(stderr, "plugin is a VST\n");
+
+ if(!(plugin->flags & effFlagsHasEditor))
+ {
+ if(MusEGlobal::debugMsg)
+ fprintf(stderr, "Plugin has no GUI\n");
+ }
+ else if(MusEGlobal::debugMsg)
+ fprintf(stderr, "Plugin has a GUI\n");
+
+ if(!(plugin->flags & effFlagsCanReplacing))
+ fprintf(stderr, "Plugin does not support processReplacing\n");
+ else if(MusEGlobal::debugMsg)
+ fprintf(stderr, "Plugin supports processReplacing\n");
+
+ plugin->dispatcher(plugin, effOpen, 0, 0, NULL, 0);
+
+ buffer[0] = 0;
+ plugin->dispatcher(plugin, effGetEffectName, 0, 0, buffer, 0);
+ if(buffer[0])
+ effectName = QString(buffer);
+
+ buffer[0] = 0;
+ plugin->dispatcher(plugin, effGetVendorString, 0, 0, buffer, 0);
+ if (buffer[0])
+ vendorString = QString(buffer);
+
+ buffer[0] = 0;
+ plugin->dispatcher(plugin, effGetProductString, 0, 0, buffer, 0);
+ if (buffer[0])
+ productString = QString(buffer);
+
+ // Make sure it doesn't already exist.
+ for(is = MusEGlobal::synthis.begin(); is != MusEGlobal::synthis.end(); ++is)
+ if((*is)->name() == effectName && (*is)->baseName() == fi.completeBaseName())
+ goto _ending;
+
+ // "2 = VST2.x, older versions return 0". Observed 2400 on all the ones tested so far.
+ vst_version = plugin->dispatcher(plugin, effGetVstVersion, 0, 0, NULL, 0.0f);
+ if(vst_version < 2 || !((plugin->flags & effFlagsIsSynth) || (plugin->dispatcher(plugin, effCanDo, 0, 0,(void*) "receiveVstEvents", 0.0f) > 0)))
+ {
+ if(MusEGlobal::debugMsg)
+ fprintf(stderr, "Plugin is not a synth\n");
+ goto _ending;
+ }
+
+ new_synth = new VstNativeSynth(fi, plugin, effectName, productString, vendorString, QString()); // TODO Version string?
+
+ if(MusEGlobal::debugMsg)
+ fprintf(stderr, "scanVstNativeLib: adding vst synth plugin:%s name:%s effectName:%s vendorString:%s productString:%s vstver:%d\n",
+ fi.filePath().toLatin1().constData(),
+ fi.completeBaseName().toLatin1().constData(),
+ effectName.toLatin1().constData(),
+ vendorString.toLatin1().constData(),
+ productString.toLatin1().constData(),
+ vst_version
+ );
+
+ MusEGlobal::synthis.push_back(new_synth);
+
+_ending: ;
+
+ //plugin->dispatcher(plugin, effMainsChanged, 0, 0, NULL, 0);
+ plugin->dispatcher(plugin, effClose, 0, 0, NULL, 0);
+ dlclose(handle);
+}
+
+//---------------------------------------------------------
+// scanVstDir
+//---------------------------------------------------------
+
+static void scanVstNativeDir(const QString& s)
+{
+ if (MusEGlobal::debugMsg)
+ fprintf(stderr, "scan vst native plugin dir <%s>\n", s.toLatin1().constData());
+ QDir pluginDir(s, QString("*.so"), QDir::Unsorted, QDir::Files);
+ if(!pluginDir.exists())
+ return;
+ QStringList list = pluginDir.entryList();
+ int count = list.count();
+ for(int i = 0; i < count; ++i)
+ {
+ if(MusEGlobal::debugMsg)
+ fprintf(stderr, "scanVstNativeDir: found %s\n", (s + QString("/") + list[i]).toLatin1().constData());
+
+ QFileInfo fi(s + QString("/") + list[i]);
+ scanVstNativeLib(fi);
+ }
+}
+
+//---------------------------------------------------------
+// initVST_Native
+//---------------------------------------------------------
+
+void initVST_Native()
+ {
+ const char* vstPath = getenv("VST_NATIVE_PATH"); // FIXME TODO: What's the right path and env var?
+ if (vstPath == 0)
+ vstPath = "/usr/lib/vst:/usr/local/lib/vst";
+
+ const char* p = vstPath;
+ while (*p != '\0') {
+ const char* pe = p;
+ while (*pe != ':' && *pe != '\0')
+ pe++;
+
+ int n = pe - p;
+ if (n) {
+ char* buffer = new char[n + 1];
+ strncpy(buffer, p, n);
+ buffer[n] = '\0';
+ scanVstNativeDir(QString(buffer));
+ delete[] buffer;
+ }
+ p = pe;
+ if (*p == ':')
+ p++;
+ }
+ }
+
+//---------------------------------------------------------
+// VstNativeSynth
+//---------------------------------------------------------
+
+VstNativeSynth::VstNativeSynth(const QFileInfo& fi, AEffect* plugin, const QString& label, const QString& desc, const QString& maker, const QString& ver)
+ : Synth(fi, label, desc, maker, ver)
+{
+ _handle = NULL;
+ _hasGui = plugin->flags & effFlagsHasEditor;
+ _inports = plugin->numInputs;
+ _outports = plugin->numOutputs;
+ _controlInPorts = plugin->numParams;
+ _inPlaceCapable = false; //(plugin->flags & effFlagsCanReplacing) && (_inports == _outports) && MusEGlobal::config.vstInPlace;
+#ifdef VST_SDK_SUPPORT
+ _hasChunks = plugin->flags & effFlagsProgramChunks;
+#else
+ _hasChunks = false;
+#endif
+
+ _flags = 0;
+ _vst_version = 0;
+ _vst_version = plugin->dispatcher(plugin, effGetVstVersion, 0, 0, NULL, 0.0f);
+ // "2 = VST2.x, older versions return 0". Observed 2400 on all the ones tested so far.
+ if(_vst_version >= 2)
+ {
+ if(plugin->dispatcher(plugin, effCanDo, 0, 0, (void*)"receiveVstEvents", 0.0f) > 0)
+ _flags |= canReceiveVstEvents;
+ if(plugin->dispatcher(plugin, effCanDo, 0, 0, (void*)"sendVstEvents", 0.0f) > 0)
+ _flags |= canSendVstEvents;
+ if(plugin->dispatcher(plugin, effCanDo, 0, 0, (void*)"sendVstMidiEvent", 0.0f) > 0)
+ _flags |= canSendVstMidiEvents;
+ if(plugin->dispatcher(plugin, effCanDo, 0, 0, (void*)"sendVstTimeInfo", 0.0f) > 0)
+ _flags |= canSendVstTimeInfo;
+ if(plugin->dispatcher(plugin, effCanDo, 0, 0, (void*)"receiveVstMidiEvent", 0.0f) > 0)
+ _flags |= canReceiveVstMidiEvents;
+ if(plugin->dispatcher(plugin, effCanDo, 0, 0, (void*)"receiveVstTimeInfo", 0.0f) > 0)
+ _flags |= canReceiveVstTimeInfo;
+ if(plugin->dispatcher(plugin, effCanDo, 0, 0, (void*)"offline", 0.0f) > 0)
+ _flags |=canProcessOffline;
+ if(plugin->dispatcher(plugin, effCanDo, 0, 0, (void*)"plugAsChannelInsert", 0.0f) > 0)
+ _flags |= canUseAsInsert;
+ if(plugin->dispatcher(plugin, effCanDo, 0, 0, (void*)"plugAsSend", 0.0f) > 0)
+ _flags |= canUseAsSend;
+ if(plugin->dispatcher(plugin, effCanDo, 0, 0, (void*)"mixDryWet", 0.0f) > 0)
+ _flags |= canMixDryWet;
+ if(plugin->dispatcher(plugin, effCanDo, 0, 0, (void*)"midiProgramNames", 0.0f) > 0)
+ _flags |= canMidiProgramNames;
+ }
+}
+
+//---------------------------------------------------------
+// incInstances
+//---------------------------------------------------------
+
+void VstNativeSynth::incInstances(int val)
+{
+ _instances += val;
+ if(_instances == 0)
+ {
+ if(_handle)
+ {
+ #ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "VstNativeSynth::incInstances no more instances, closing library\n");
+ #endif
+
+ dlclose(_handle);
+ }
+ _handle = NULL;
+ iIdx.clear();
+ oIdx.clear();
+ rpIdx.clear();
+ midiCtl2PortMap.clear();
+ port2MidiCtlMap.clear();
+ }
+}
+
+//---------------------------------------------------------
+// instantiate
+//---------------------------------------------------------
+
+AEffect* VstNativeSynth::instantiate()
+{
+ int inst_num = _instances;
+ inst_num++;
+ QString n;
+ n.setNum(inst_num);
+ QString instanceName = baseName() + "-" + n;
+ QByteArray ba = info.filePath().toLatin1();
+ const char* path = ba.constData();
+ void* hnd = _handle;
+ int vst_version;
+
+ if(hnd == NULL)
+ {
+ hnd = dlopen(path, RTLD_NOW);
+ if(hnd == NULL)
+ {
+ fprintf(stderr, "dlopen(%s) failed: %s\n", path, dlerror());
+ return NULL;
+ }
+ }
+
+ AEffect *(*getInstance)(audioMasterCallback);
+ getInstance = (AEffect*(*)(audioMasterCallback))dlsym(hnd, NEW_PLUGIN_ENTRY_POINT);
+ if(!getInstance)
+ {
+ if(MusEGlobal::debugMsg)
+ {
+ fprintf(stderr, "VST 2.4 entrypoint \"" NEW_PLUGIN_ENTRY_POINT "\" not found in library %s, looking for \""
+ OLD_PLUGIN_ENTRY_POINT "\"\n", path);
+ }
+
+ getInstance = (AEffect*(*)(audioMasterCallback))dlsym(hnd, OLD_PLUGIN_ENTRY_POINT);
+ if(!getInstance)
+ {
+ fprintf(stderr, "ERROR: VST entrypoints \"" NEW_PLUGIN_ENTRY_POINT "\" or \""
+ OLD_PLUGIN_ENTRY_POINT "\" not found in library\n");
+ dlclose(hnd);
+ return NULL;
+ }
+ else if(MusEGlobal::debugMsg)
+ {
+ fprintf(stderr, "VST entrypoint \"" OLD_PLUGIN_ENTRY_POINT "\" found\n");
+ }
+ }
+ else if(MusEGlobal::debugMsg)
+ {
+ fprintf(stderr, "VST entrypoint \"" NEW_PLUGIN_ENTRY_POINT "\" found\n");
+ }
+
+ AEffect *plugin = getInstance(vstNativeHostCallback);
+ if(!plugin)
+ {
+ fprintf(stderr, "ERROR: Failed to instantiate plugin in VST library \"%s\"\n", path);
+ dlclose(hnd);
+ return NULL;
+ }
+ else if(MusEGlobal::debugMsg)
+ fprintf(stderr, "plugin instantiated\n");
+
+ if(plugin->magic != kEffectMagic)
+ {
+ fprintf(stderr, "Not a VST plugin in library \"%s\"\n", path);
+ dlclose(hnd);
+ return NULL;
+ }
+ else if(MusEGlobal::debugMsg)
+ fprintf(stderr, "plugin is a VST\n");
+
+ if(!(plugin->flags & effFlagsHasEditor))
+ {
+ if(MusEGlobal::debugMsg)
+ fprintf(stderr, "Plugin has no GUI\n");
+ }
+ else if(MusEGlobal::debugMsg)
+ fprintf(stderr, "Plugin has a GUI\n");
+
+ if(!(plugin->flags & effFlagsCanReplacing))
+ fprintf(stderr, "Plugin does not support processReplacing\n");
+ else if(MusEGlobal::debugMsg)
+ fprintf(stderr, "Plugin supports processReplacing\n");
+
+ plugin->dispatcher(plugin, effOpen, 0, 0, NULL, 0);
+
+ // "2 = VST2.x, older versions return 0". Observed 2400 on all the ones tested so far.
+ vst_version = plugin->dispatcher(plugin, effGetVstVersion, 0, 0, NULL, 0.0f);
+ if(vst_version < 2 || !((plugin->flags & effFlagsIsSynth) || (plugin->dispatcher(plugin, effCanDo, 0, 0,(void*) "receiveVstEvents", 0.0f) > 0)))
+ {
+ if(MusEGlobal::debugMsg)
+ fprintf(stderr, "Plugin is not a synth\n");
+ goto _error;
+ }
+
+ ++_instances;
+ _handle = hnd;
+
+ plugin->dispatcher(plugin, effOpen, 0, 0, NULL, 0);
+ //plugin->dispatcher(plugin, effSetProgram, 0, 0, NULL, 0.0f); // REMOVE Tim. Or keep?
+ return plugin;
+
+_error:
+ //plugin->dispatcher(plugin, effMainsChanged, 0, 0, NULL, 0);
+ plugin->dispatcher(plugin, effClose, 0, 0, NULL, 0);
+ dlclose(hnd);
+ return NULL;
+}
+
+//---------------------------------------------------------
+// createSIF
+//---------------------------------------------------------
+
+SynthIF* VstNativeSynth::createSIF(SynthI* s)
+ {
+ VstNativeSynthIF* sif = new VstNativeSynthIF(s);
+ sif->init(this);
+ return sif;
+ }
+
+//---------------------------------------------------------
+// VstNativeSynthIF
+//---------------------------------------------------------
+
+VstNativeSynthIF::VstNativeSynthIF(SynthI* s) : SynthIF(s)
+{
+ _guiVisible = false;
+ _synth = NULL;
+ _plugin = NULL;
+ _editor = NULL;
+ _inProcess = false;
+ _controls = NULL;
+// controlsOut = 0;
+ _audioInBuffers = NULL;
+ _audioInSilenceBuf = NULL;
+ //_audioInSilenceBufs = NULL;
+ _audioOutBuffers = NULL;
+}
+
+VstNativeSynthIF::~VstNativeSynthIF()
+{
+ // Just in case it wasn't removed or deactivate3 wasn't called etc...
+ if(_plugin)
+ fprintf(stderr, "ERROR: ~VstNativeSynthIF: _plugin is not NULL!\n");
+
+ if(_audioOutBuffers)
+ {
+ unsigned long op = _synth->outPorts();
+ for(unsigned long i = 0; i < op; ++i)
+ {
+ if(_audioOutBuffers[i])
+ free(_audioOutBuffers[i]);
+ }
+ delete[] _audioOutBuffers;
+ }
+
+ if(_audioInBuffers)
+ {
+ unsigned long ip = _synth->inPorts();
+ for(unsigned long i = 0; i < ip; ++i)
+ {
+ if(_audioInBuffers[i])
+ free(_audioInBuffers[i]);
+ }
+ delete[] _audioInBuffers;
+ }
+
+ if(_audioInSilenceBuf)
+ free(_audioInSilenceBuf);
+
+// if(_audioInSilenceBufs)
+// {
+// for(unsigned long i = 0; i < _synth->inPorts(); ++i)
+// {
+// if(_audioInSilenceBufs[i])
+// free(_audioInSilenceBufs[i]);
+// }
+// delete[] _audioInSilenceBufs;
+// }
+
+ if(_controls)
+ delete[] _controls;
+}
+
+//---------------------------------------------------------
+// init
+//---------------------------------------------------------
+
+bool VstNativeSynthIF::init(Synth* s)
+ {
+ _synth = (VstNativeSynth*)s;
+ _plugin = _synth->instantiate();
+ if(!_plugin)
+ return false;
+ _plugin->user = this;
+
+ queryPrograms();
+
+ unsigned long outports = _synth->outPorts();
+ if(outports != 0)
+ {
+ _audioOutBuffers = new float*[outports];
+ for(unsigned long k = 0; k < outports; ++k)
+ {
+ posix_memalign((void**)&_audioOutBuffers[k], 16, sizeof(float) * MusEGlobal::segmentSize);
+ memset(_audioOutBuffers[k], 0, sizeof(float) * MusEGlobal::segmentSize);
+ }
+ }
+
+ unsigned long inports = _synth->inPorts();
+ if(inports != 0)
+ {
+ _audioInBuffers = new float*[inports];
+ //_audioInSilenceBufs = new float*[inports];
+ for(unsigned long k = 0; k < inports; ++k)
+ {
+ posix_memalign((void**)&_audioInBuffers[k], 16, sizeof(float) * MusEGlobal::segmentSize);
+ memset(_audioInBuffers[k], 0, sizeof(float) * MusEGlobal::segmentSize);
+ //posix_memalign((void**)&_audioInSilenceBufs[k], 16, sizeof(float) * MusEGlobal::segmentSize);
+ //memset(_audioInSilenceBufs[k], 0, sizeof(float) * MusEGlobal::segmentSize);
+ _iUsedIdx.push_back(false); // Start out with all false.
+ }
+
+ posix_memalign((void**)&_audioInSilenceBuf, 16, sizeof(float) * MusEGlobal::segmentSize);
+ memset(_audioInSilenceBuf, 0, sizeof(float) * MusEGlobal::segmentSize);
+ }
+
+ int controlPorts = _synth->inControls();
+ if(controlPorts != 0)
+ _controls = new Port[controlPorts];
+ else
+ _controls = NULL;
+
+ //_synth->midiCtl2PortMap.clear();
+ //_synth->port2MidiCtlMap.clear();
+
+ for(unsigned long i = 0; i < _synth->inControls(); ++i)
+ {
+ _controls[i].idx = i;
+ //float val; // TODO
+ //ladspaDefaultValue(ld, k, &val); // FIXME TODO
+ float val = _plugin->getParameter(_plugin, i); // TODO
+ _controls[i].val = val;
+ _controls[i].tmpVal = val;
+ _controls[i].enCtrl = true;
+ _controls[i].en2Ctrl = true;
+
+ // Support a special block for synth ladspa controllers.
+ // Put the ID at a special block after plugins (far after).
+ int id = genACnum(MAX_PLUGINS, i);
+ const char* param_name = paramName(i);
+
+ // TODO FIXME!
+ ///float min, max;
+ ///ladspaControlRange(ld, k, &min, &max);
+ float min = 0.0, max = 1.0;
+
+ CtrlList* cl;
+ CtrlListList* cll = ((MusECore::AudioTrack*)synti)->controller();
+ iCtrlList icl = cll->find(id);
+ if (icl == cll->end())
+ {
+ cl = new CtrlList(id);
+ cll->add(cl);
+ //cl->setCurVal(controls[cip].val);
+ cl->setCurVal(_plugin->getParameter(_plugin, i));
+ }
+ else
+ {
+ cl = icl->second;
+ ///controls[cip].val = cl->curVal();
+ //setParam(i, cl->curVal());
+ if(dispatch(effCanBeAutomated, i, 0, NULL, 0.0f) == 1)
+ _plugin->setParameter(_plugin, i, cl->curVal());
+#ifdef VST_NATIVE_DEBUG
+ else
+ fprintf(stderr, "VstNativeSynthIF::init %s parameter:%lu cannot be automated\n", name().toLatin1().constData(), i);
+#endif
+
+ }
+ cl->setRange(min, max);
+ cl->setName(QString(param_name));
+ //cl->setValueType(ladspaCtrlValueType(ld, k));
+ cl->setValueType(ctrlValueType(i));
+ //cl->setMode(ladspaCtrlMode(ld, k));
+ cl->setMode(ctrlMode(i));
+ }
+
+ activate();
+ doSelectProgram(synti->_curBankH, synti->_curBankL, synti->_curProgram);
+ //doSelectProgram(synti->_curProgram);
+
+ return true;
+ }
+
+//---------------------------------------------------------
+// resizeEditor
+//---------------------------------------------------------
+
+bool VstNativeSynthIF::resizeEditor(int w, int h)
+{
+ if(!_editor || w <= 0 || h <= 0)
+ return false;
+ _editor->resize(w, h);
+ return true;
+}
+
+//---------------------------------------------------------
+// hostCallback
+//---------------------------------------------------------
+
+VstIntPtr VstNativeSynthIF::hostCallback(VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt)
+ {
+ //static VstTimeInfo _timeInfo;
+ //jack_position_t jack_pos;
+ //jack_transport_state_t tstate;
+
+#ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "VstNativeSynthIF::hostCallback %s opcode:%ld\n", name().toLatin1().constData(), opcode);
+#endif
+
+ switch (opcode) {
+ case audioMasterAutomate:
+ // index, value, returns 0
+ ///_plugin->setParameter (_plugin, index, opt);
+ guiControlChanged(index, opt);
+ return 0;
+
+ case audioMasterVersion:
+ // vst version, currently 2 (0 for older)
+ return 2300;
+
+ case audioMasterCurrentId:
+ // returns the unique id of a plug that's currently
+ // loading
+ return 0;
+
+ case audioMasterIdle:
+ // call application idle routine (this will
+ // call effEditIdle for all open editors too)
+ //_plugin->updateParamValues(false);
+ //_plugin->dispatcher(_plugin, effEditIdle, 0, 0, NULL, 0.0f);
+ idleEditor();
+ return 0;
+
+ case audioMasterGetTime:
+ // returns const VstTimeInfo* (or 0 if not supported)
+ // <value> should contain a mask indicating which fields are required
+ // (see valid masks above), as some items may require extensive
+ // conversions
+
+// FIXME: TODO: Change this to MusE tempo and sig, and even if Jack is used,
+// the jackAudio pointer may be 0. Jack may not be running and dummy used instead.
+#if 1
+ return 0;
+#else
+ memset(&_timeInfo, 0, sizeof(_timeInfo));
+
+ if (_plugin) {
+ tstate = jackAudio->transportQuery(&jack_pos);
+
+ _timeInfo.samplePos = jack_pos.frame;
+ _timeInfo.sampleRate = jack_pos.frame_rate;
+ _timeInfo.flags = 0;
+
+ if ((value & (kVstBarsValid|kVstTempoValid)) && (jack_pos.valid & JackPositionBBT)) {
+ _timeInfo.tempo = jack_pos.beats_per_minute;
+ _timeInfo.timeSigNumerator = (long) floor (jack_pos.beats_per_bar);
+ _timeInfo.timeSigDenominator = (long) floor (jack_pos.beat_type);
+ _timeInfo.flags |= (kVstBarsValid|kVstTempoValid);
+ }
+ if (tstate == JackTransportRolling) {
+ _timeInfo.flags |= kVstTransportPlaying;
+ }
+ }
+ else {
+ _timeInfo.samplePos = 0;
+ _timeInfo.sampleRate = MusEGlobal::sampleRate;
+ }
+ return (long)&_timeInfo;
+#endif
+
+ case audioMasterProcessEvents:
+ // VstEvents* in <ptr>
+ return 0; // TODO:
+
+ case audioMasterIOChanged:
+ // numInputs and/or numOutputs has changed
+ return 0;
+
+ case audioMasterSizeWindow:
+ // index: width, value: height
+ if(resizeEditor(int(index), int(value)))
+ return 1; // supported.
+ return 0;
+
+ case audioMasterGetSampleRate:
+ //return 0;
+ return MusEGlobal::sampleRate;
+
+ case audioMasterGetBlockSize:
+ //return 0;
+ return MusEGlobal::segmentSize;
+
+ case audioMasterGetInputLatency:
+ return 0;
+
+ case audioMasterGetOutputLatency:
+ return 0;
+
+ case audioMasterGetCurrentProcessLevel:
+ // returns: 0: not supported,
+ // 1: currently in user thread (gui)
+ // 2: currently in audio thread (where process is called)
+ // 3: currently in 'sequencer' thread (midi, timer etc)
+ // 4: currently offline processing and thus in user thread
+ // other: not defined, but probably pre-empting user thread.
+ if(_inProcess)
+ return 2;
+ else
+ return 1;
+
+ case audioMasterGetAutomationState:
+ // returns 0: not supported, 1: off, 2:read, 3:write, 4:read/write
+ // offline
+ return 1; // TODO:
+
+ case audioMasterOfflineStart:
+ case audioMasterOfflineRead:
+ // ptr points to offline structure, see below. return 0: error, 1 ok
+ return 0;
+
+ case audioMasterOfflineWrite:
+ // same as read
+ return 0;
+
+ case audioMasterOfflineGetCurrentPass:
+ case audioMasterOfflineGetCurrentMetaPass:
+ return 0;
+
+ case audioMasterGetVendorString:
+ // fills <ptr> with a string identifying the vendor (max 64 char)
+ strcpy ((char*) ptr, "MusE");
+ return 1;
+
+ case audioMasterGetProductString:
+ // fills <ptr> with a string with product name (max 64 char)
+ strcpy ((char*) ptr, "MusE Sequencer");
+ return 1;
+
+ case audioMasterGetVendorVersion:
+ // returns vendor-specific version
+ return 2000;
+
+ case audioMasterVendorSpecific:
+ // no definition, vendor specific handling
+ return 0;
+
+ case audioMasterCanDo:
+ // string in ptr, see below
+ if(!strcmp((char*)ptr, "sendVstEvents") ||
+ !strcmp((char*)ptr, "receiveVstMidiEvent") ||
+ !strcmp((char*)ptr, "sendVstMidiEvent") ||
+ !strcmp((char*)ptr, "sendVstTimeInfo") ||
+ !strcmp((char*)ptr, "sizeWindow") ||
+ !strcmp((char*)ptr, "supplyIdle"))
+ return 1;
+
+#if 0 //ifdef VST_SDK_SUPPORT
+ else
+ if(!strcmp((char*)ptr, "openFileSelector") ||
+ !strcmp((char*)ptr, "closeFileSelector"))
+ return 1;
+#endif
+ return 0;
+
+ case audioMasterGetLanguage:
+ // see enum
+ //return 0;
+ return kVstLangEnglish;
+
+ case audioMasterGetDirectory:
+ // get plug directory, FSSpec on MAC, else char*
+ return 0;
+
+ case audioMasterUpdateDisplay:
+ // something has changed, update 'multi-fx' display
+
+ //_plugin->updateParamValues(false);
+ //QApplication::processEvents(); // REMOVE Tim. Or keep. Commented in QTractor.
+
+ _plugin->dispatcher(_plugin, effEditIdle, 0, 0, NULL, 0.0f); // ?
+
+ return 0;
+
+ case audioMasterBeginEdit:
+ // begin of automation session (when mouse down), parameter index in <index>
+ guiAutomationBegin(index);
+ return 1;
+
+ case audioMasterEndEdit:
+ // end of automation session (when mouse up), parameter index in <index>
+ guiAutomationEnd(index);
+ return 1;
+
+#if 0 //ifdef VST_SDK_SUPPORT
+ case audioMasterOpenFileSelector:
+ // open a fileselector window with VstFileSelect* in <ptr>
+ return 0;
+
+ case audioMasterCloseFileSelector:
+ return 0;
+#endif
+
+#ifdef VST_NATIVE_FORCE_DEPRECATED
+
+ case audioMasterGetSpeakerArrangement:
+ // (long)input in <value>, output in <ptr>
+ return 0;
+
+ case audioMasterPinConnected:
+ // inquire if an input or output is beeing connected;
+ // index enumerates input or output counting from zero:
+ // value is 0 for input and != 0 otherwise. note: the
+ // return value is 0 for <true> such that older versions
+ // will always return true.
+ //return 1;
+ return 0;
+
+ // VST 2.0 opcodes...
+ case audioMasterWantMidi:
+ // <value> is a filter which is currently ignored
+ return 0;
+
+ case audioMasterSetTime:
+ // VstTimenfo* in <ptr>, filter in <value>, not supported
+ return 0;
+
+ case audioMasterTempoAt:
+ // returns tempo (in bpm * 10000) at sample frame location passed in <value>
+ return 0; // TODO:
+
+ case audioMasterGetNumAutomatableParameters:
+ return 0;
+
+ case audioMasterGetParameterQuantization:
+ // returns the integer value for +1.0 representation,
+ // or 1 if full single float precision is maintained
+ // in automation. parameter index in <value> (-1: all, any)
+ //return 0;
+ return 1;
+
+ case audioMasterNeedIdle:
+ // plug needs idle calls (outside its editor window)
+ return 0;
+
+ case audioMasterGetPreviousPlug:
+ // input pin in <value> (-1: first to come), returns cEffect*
+ return 0;
+
+ case audioMasterGetNextPlug:
+ // output pin in <value> (-1: first to come), returns cEffect*
+ return 0;
+
+ case audioMasterWillReplaceOrAccumulate:
+ // returns: 0: not supported, 1: replace, 2: accumulate
+ //return 0;
+ return 1;
+
+ case audioMasterSetOutputSampleRate:
+ // for variable i/o, sample rate in <opt>
+ return 0;
+
+ case audioMasterSetIcon:
+ // void* in <ptr>, format not defined yet
+ return 0;
+
+ case audioMasterOpenWindow:
+ // returns platform specific ptr
+ return 0;
+
+ case audioMasterCloseWindow:
+ // close window, platform specific handle in <ptr>
+ return 0;
+#endif
+
+
+ default:
+ break;
+ }
+ return 0;
+ }
+
+//---------------------------------------------------------
+// idleEditor
+//---------------------------------------------------------
+
+void VstNativeSynthIF::idleEditor()
+{
+#ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "VstNativeSynthIF::idleEditor %p\n", this);
+#endif
+
+ _plugin->dispatcher(_plugin, effEditIdle, 0, 0, NULL, 0.0f);
+ if(_editor)
+ _editor->update();
+}
+
+//---------------------------------------------------------
+// nativeGuiVisible
+//---------------------------------------------------------
+
+bool VstNativeSynthIF::nativeGuiVisible() const
+ {
+ return _guiVisible;
+ }
+
+//---------------------------------------------------------
+// guiVisible
+//---------------------------------------------------------
+
+bool VstNativeSynthIF::guiVisible() const
+ {
+ return _gui && _gui->isVisible();
+ }
+
+//---------------------------------------------------------
+// showGui
+//---------------------------------------------------------
+
+void VstNativeSynthIF::showGui(bool v)
+{
+ if (v) {
+ if (_gui == 0)
+ makeGui();
+ _gui->show();
+ }
+ else {
+ if (_gui)
+ _gui->hide();
+ }
+}
+
+//---------------------------------------------------------
+// showGui
+//---------------------------------------------------------
+
+void VstNativeSynthIF::showNativeGui(bool v)
+ {
+ if(!(_plugin->flags & effFlagsHasEditor)) // || v == nativeGuiVisible())
+ return;
+ if(v)
+ {
+ if(_editor)
+ {
+ if(!_editor->isVisible())
+ _editor->show();
+ _editor->raise();
+ _editor->activateWindow();
+ }
+ else
+ {
+ Qt::WindowFlags wflags = Qt::Window
+ | Qt::CustomizeWindowHint
+ | Qt::WindowTitleHint
+ | Qt::WindowSystemMenuHint
+ | Qt::WindowMinMaxButtonsHint
+ | Qt::WindowCloseButtonHint;
+ _editor = new MusEGui::VstNativeEditor(NULL, wflags);
+ _editor->open(this);
+
+ // TODO TEST Test if these might be helpful, especially opaque event.
+ // _editor->setBackgroundRole(QPalette::NoRole);
+ // _editor->setAttribute(Qt::WA_NoSystemBackground);
+ // _editor->setAttribute(Qt::WA_StaticContents);
+ // // This is absolutely required for speed! Otherwise painfully slow because of full background
+ // // filling, even when requesting small udpdates! Background is drawn by us.
+ // _editor->setAttribute(Qt::WA_OpaquePaintEvent);
+ // //_editor->setFrameStyle(QFrame::Raised | QFrame::StyledPanel);
+ }
+ }
+ else
+ {
+ if(_editor)
+ {
+ delete _editor;
+ //_editor = NULL; // No - done in editorDeleted.
+ }
+ }
+ _guiVisible = v;
+}
+
+//---------------------------------------------------------
+// editorOpened
+//---------------------------------------------------------
+
+void VstNativeSynthIF::editorOpened()
+{
+ _guiVisible = true;
+}
+
+//---------------------------------------------------------
+// editorClosed
+//---------------------------------------------------------
+
+void VstNativeSynthIF::editorClosed()
+{
+ _guiVisible = false;
+}
+
+//---------------------------------------------------------
+// editorDeleted
+//---------------------------------------------------------
+
+void VstNativeSynthIF::editorDeleted()
+{
+ _editor = NULL;
+}
+
+//---------------------------------------------------------
+// receiveEvent
+//---------------------------------------------------------
+
+MidiPlayEvent VstNativeSynthIF::receiveEvent()
+ {
+ return MidiPlayEvent();
+ }
+
+//---------------------------------------------------------
+// hasGui
+//---------------------------------------------------------
+
+bool VstNativeSynthIF::hasNativeGui() const
+ {
+ return _plugin->flags & effFlagsHasEditor;
+ }
+
+//---------------------------------------------------------
+// channels
+//---------------------------------------------------------
+
+int VstNativeSynthIF::channels() const
+ {
+ //return _plugin->numOutputs;
+ return _plugin->numOutputs > MAX_CHANNELS ? MAX_CHANNELS : _plugin->numOutputs ;
+ }
+
+int VstNativeSynthIF::totalOutChannels() const
+ {
+ return _plugin->numOutputs;
+ }
+
+int VstNativeSynthIF::totalInChannels() const
+ {
+ return _plugin->numInputs;
+ }
+
+//---------------------------------------------------------
+// deactivate3
+//---------------------------------------------------------
+
+void VstNativeSynthIF::deactivate3()
+ {
+ if(_editor)
+ {
+ delete _editor;
+ _editor = NULL;
+ _guiVisible = false;
+ }
+
+ deactivate();
+ if (_plugin) {
+ _plugin->dispatcher (_plugin, effClose, 0, 0, NULL, 0);
+ _plugin = NULL;
+ }
+ }
+
+//---------------------------------------------------------
+// queryPrograms
+//---------------------------------------------------------
+
+void VstNativeSynthIF::queryPrograms()
+{
+ char buf[256];
+ programs.clear();
+ int num_progs = _plugin->numPrograms;
+ int iOldIndex = dispatch(effGetProgram, 0, 0, NULL, 0.0f);
+ bool need_restore = false;
+ for(int prog = 0; prog < num_progs; ++prog)
+ {
+ buf[0] = 0;
+
+//#ifdef VST_SDK_SUPPORT
+ // value = category. -1 = regular linear.
+ if(dispatch(effGetProgramNameIndexed, prog, -1, buf, 0.0f) == 0)
+ {
+//#endif
+ dispatch(effSetProgram, 0, prog, NULL, 0.0f);
+ dispatch(effGetProgramName, 0, 0, buf, 0.0f);
+ need_restore = true;
+//#ifdef VST_SDK_SUPPORT
+ }
+//#endif
+
+ VST_Program p;
+ p.name = QString(buf);
+ //p.program = prog & 0x7f;
+ //p.bank = prog << 7;
+ p.program = prog;
+ programs.push_back(p);
+ }
+
+ // Restore current program.
+ if(need_restore) // && num_progs > 0)
+ {
+ dispatch(effSetProgram, 0, iOldIndex, NULL, 0.0f);
+ fprintf(stderr, "FIXME: VstNativeSynthIF::queryPrograms(): effGetProgramNameIndexed returned 0. Used ugly effSetProgram/effGetProgramName instead\n");
+ }
+}
+
+//---------------------------------------------------------
+// doSelectProgram
+//---------------------------------------------------------
+
+void VstNativeSynthIF::doSelectProgram(int bankH, int bankL, int prog)
+{
+ if(!_plugin)
+ return;
+
+#ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "VstNativeSynthIF::doSelectProgram bankH:%d bankL:%d prog:%d\n", bankH, bankL, prog);
+#endif
+
+ if(bankH == 0xff)
+ bankH = 0;
+ if(bankL == 0xff)
+ bankL = 0;
+ if(prog == 0xff)
+ prog = 0;
+
+ int p = (bankH << 14) + (bankL << 7) + prog;
+
+ if(p >= _plugin->numPrograms)
+ {
+ fprintf(stderr, "VstNativeSynthIF::doSelectProgram program:%d out of range\n", p);
+ return;
+ }
+
+ //for (unsigned short i = 0; i < instances(); ++i)
+ //{
+ // "host calls this before a new program (effSetProgram) is loaded"
+ //if(dispatch(effBeginSetProgram, 0, 0, NULL, 0.0f) == 1) // TESTED: Usually it did not acknowledge. So IGNORE it.
+ dispatch(effBeginSetProgram, 0, 0, NULL, 0.0f);
+ //{
+ dispatch(effSetProgram, 0, p, NULL, 0.0f);
+ //dispatch(effSetProgram, 0, prog, NULL, 0.0f);
+ // "host calls this after the new program (effSetProgram) has been loaded"
+ dispatch(effEndSetProgram, 0, 0, NULL, 0.0f);
+ //}
+ //else
+ // fprintf(stderr, "VstNativeSynthIF::doSelectProgram bankH:%d bankL:%d prog:%d Effect did not acknowledge effBeginSetProgram\n", bankH, bankL, prog);
+ //}
+
+ // TODO: Is this true of VSTs? See the similar section in dssihost.cpp // REMOVE Tim.
+ // "A plugin is permitted to re-write the values of its input control ports when select_program is called.
+ // The host should re-read the input control port values and update its own records appropriately.
+ // (This is the only circumstance in which a DSSI plugin is allowed to modify its own input ports.)" From dssi.h
+ // Need to update the automation value, otherwise it overwrites later with the last automation value.
+ if(id() != -1)
+ {
+ for(unsigned long k = 0; k < _synth->inControls(); ++k)
+ {
+ // We're in the audio thread context: no need to send a message, just modify directly.
+ synti->setPluginCtrlVal(genACnum(id(), k), _plugin->getParameter(_plugin, k));
+ }
+ }
+
+// // Reset parameters default value... // TODO ?
+// AEffect *pVstEffect = vst_effect(0);
+// if (pVstEffect) {
+// const qtractorPlugin::Params& params = qtractorPlugin::params();
+// qtractorPlugin::Params::ConstIterator param = params.constBegin();
+// for ( ; param != params.constEnd(); ++param) {
+// qtractorPluginParam *pParam = param.value();
+// float *pfValue = pParam->subject()->data();
+// *pfValue = pVstEffect->getParameter(pVstEffect, pParam->index());
+// pParam->setDefaultValue(*pfValue);
+// }
+// }
+
+}
+
+//---------------------------------------------------------
+// getPatchName
+//---------------------------------------------------------
+
+const char* VstNativeSynthIF::getPatchName(int /*chan*/, int prog, bool /*drum*/)
+{
+ unsigned long program = prog & 0x7f;
+ unsigned long lbank = (prog >> 8) & 0xff;
+ unsigned long hbank = (prog >> 16) & 0xff;
+
+ if (lbank == 0xff)
+ lbank = 0;
+ if (hbank == 0xff)
+ hbank = 0;
+ prog = (hbank << 14) + (lbank << 7) + program;
+
+ if(prog < _plugin->numPrograms)
+ {
+ for(std::vector<VST_Program>::const_iterator i = programs.begin(); i != programs.end(); ++i)
+ {
+ if(i->program == program)
+ return i->name.toLatin1().constData();
+ }
+ }
+ return "?";
+}
+
+//---------------------------------------------------------
+// populatePatchPopup
+//---------------------------------------------------------
+
+void VstNativeSynthIF::populatePatchPopup(MusEGui::PopupMenu* menu, int /*chan*/, bool /*drum*/)
+{
+ // The plugin can change the programs, patches etc.
+ // So make sure we're up to date by calling queryPrograms.
+ queryPrograms();
+
+ menu->clear();
+
+ for (std::vector<VST_Program>::const_iterator i = programs.begin(); i != programs.end(); ++i)
+ {
+ //int bank = i->bank;
+ int prog = i->program;
+ //int id = (bank << 7) + prog;
+
+ QAction *act = menu->addAction(i->name);
+ //act->setData(id);
+ act->setData(prog);
+ }
+
+}
+
+//---------------------------------------------------------
+// getParameter
+//---------------------------------------------------------
+
+float VstNativeSynthIF::getParameter(unsigned long idx) const
+ {
+ if(idx >= _synth->inControls())
+ {
+ fprintf(stderr, "VstNativeSynthIF::getParameter param number %lu out of range of ports:%lu\n", idx, _synth->inControls());
+ return 0.0;
+ }
+
+ return _plugin->getParameter(_plugin, idx);
+ }
+
+//---------------------------------------------------------
+// setParameter
+//---------------------------------------------------------
+
+void VstNativeSynthIF::setParameter(unsigned long idx, float value)
+ {
+ //_plugin->setParameter(_plugin, idx, value);
+ addScheduledControlEvent(idx, value, MusEGlobal::audio->curFrame());
+ }
+
+//---------------------------------------------------------
+// guiAutomationBegin
+//---------------------------------------------------------
+
+void VstNativeSynthIF::guiAutomationBegin(unsigned long param_idx)
+{
+ AutomationType at = AUTO_OFF;
+ MusECore::AudioTrack* t = track();
+ if(t)
+ at = t->automationType();
+
+ // FIXME TODO: This stuff should probably be sent as control FIFO events.
+ // And probably not safe - accessing from gui and audio threads...
+
+ if (at == AUTO_READ || at == AUTO_TOUCH || at == AUTO_WRITE)
+ enableController(param_idx, false);
+
+ int plug_id = id();
+
+ if(plug_id == -1)
+ return;
+
+ plug_id = MusECore::genACnum(plug_id, param_idx);
+
+ //if(params[param].type == GuiParam::GUI_SLIDER)
+ //{
+ //double val = ((Slider*)params[param].actuator)->value();
+ float val = param(param_idx);
+ // FIXME TODO:
+ //if (LADSPA_IS_HINT_LOGARITHMIC(params[param].hint))
+ // val = pow(10.0, val/20.0);
+ //else if (LADSPA_IS_HINT_INTEGER(params[param].hint))
+ // val = rint(val);
+ //plugin->setParam(param, val);
+ //((DoubleLabel*)params[param].label)->setValue(val);
+
+ if(t)
+ {
+ t->setPluginCtrlVal(plug_id, val);
+ t->startAutoRecord(plug_id, val);
+ }
+ //}
+// else if(params[param].type == GuiParam::GUI_SWITCH)
+// {
+// float val = (float)((CheckBox*)params[param].actuator)->isChecked();
+// plugin->setParam(param, val);
+//
+// if(t)
+// {
+// t->setPluginCtrlVal(plug_id, val);
+// t->startAutoRecord(plug_id, val);
+// }
+// }
+}
+
+//---------------------------------------------------------
+// guiAutomationEnd
+//---------------------------------------------------------
+
+void VstNativeSynthIF::guiAutomationEnd(unsigned long param_idx)
+{
+ AutomationType at = AUTO_OFF;
+ MusECore::AudioTrack* t = track();
+ if(t)
+ at = t->automationType();
+
+ // FIXME TODO: This stuff should probably be sent as control FIFO events.
+ // And probably not safe - accessing from gui and audio threads...
+
+ // Special for switch - don't enable controller until transport stopped.
+ if ((at == AUTO_OFF) ||
+ (at == AUTO_READ) ||
+ (at == AUTO_TOUCH)) // && (params[param].type != GuiParam::GUI_SWITCH || // FIXME TODO
+ // !MusEGlobal::audio->isPlaying()) ) )
+ enableController(param_idx, true);
+
+ int plug_id = id();
+ if(!t || plug_id == -1)
+ return;
+ plug_id = MusECore::genACnum(plug_id, param_idx);
+
+ //if(params[param].type == GuiParam::GUI_SLIDER)
+ //{
+ //double val = ((Slider*)params[param].actuator)->value();
+ float val = param(param_idx);
+ // FIXME TODO:
+ //if (LADSPA_IS_HINT_LOGARITHMIC(params[param].hint))
+ // val = pow(10.0, val/20.0);
+ //else if (LADSPA_IS_HINT_INTEGER(params[param].hint))
+ // val = rint(val);
+ t->stopAutoRecord(plug_id, val);
+ //}
+}
+
+//---------------------------------------------------------
+// guiControlEventHandler
+//---------------------------------------------------------
+
+int VstNativeSynthIF::guiControlChanged(unsigned long param_idx, float value)
+{
+ #ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "VstNativeSynthIF::guiControlChanged received oscControl port:%lu val:%f\n", port, value);
+ #endif
+
+ if(param_idx >= _synth->inControls())
+ {
+ fprintf(stderr, "VstNativeSynthIF::guiControlChanged: port number:%lu is out of range of index list size:%lu\n", param_idx, _synth->inControls());
+ return 0;
+ }
+
+ ControlEvent ce;
+ ce.unique = false; // Not used for native vst.
+ ce.fromGui = true; // It came form the plugin's own GUI.
+ ce.idx = param_idx;
+ ce.value = value;
+ // don't use timestamp(), because it's circular, which is making it impossible to deal
+ // with 'modulo' events which slip in 'under the wire' before processing the ring buffers.
+ ce.frame = MusEGlobal::audio->curFrame();
+
+ if(_controlFifo.put(ce))
+ {
+ fprintf(stderr, "VstNativeSynthIF::guiControlChanged: fifo overflow: in control number:%lu\n", param_idx);
+ }
+
+ // FIXME TODO: This stuff should probably be sent as control FIFO events.
+ // And probably not safe - accessing from gui and audio threads...
+
+ // Record automation:
+ // Take care of this immediately rather than in the fifo processing.
+ if(id() != -1)
+ {
+ unsigned long pid = genACnum(id(), param_idx);
+ AutomationType at = synti->automationType();
+
+ if ((at == AUTO_WRITE) ||
+ (at == AUTO_TOUCH && MusEGlobal::audio->isPlaying()))
+ enableController(param_idx, false);
+
+ synti->recordAutomation(pid, value);
+ }
+
+ return 0;
+}
+
+//---------------------------------------------------------
+// write
+//---------------------------------------------------------
+
+void VstNativeSynthIF::write(int level, Xml& xml) const
+{
+#ifdef VST_SDK_SUPPORT
+ if(_synth->hasChunks())
+ {
+ //---------------------------------------------
+ // dump current state of synth
+ //---------------------------------------------
+ fprintf(stderr, "%s: commencing chunk data dump, plugin api version=%d\n", name().toLatin1().constData(), _synth->vstVersion());
+ unsigned long len = 0;
+ void* p = 0;
+ len = dispatch(effGetChunk, 0, 0, &p, 0.0); // index 0: is bank 1: is program
+ if (len)
+ {
+ xml.tag(level++, "midistate version=\"%d\"", SYNTH_MIDI_STATE_SAVE_VERSION);
+ xml.nput(level++, "<event type=\"%d\"", Sysex);
+ // 10 = 2 bytes header + "VSTSAVE" + 1 byte flags (compression etc)
+ xml.nput(" datalen=\"%d\">\n", len+10);
+ xml.nput(level, "");
+ xml.nput("%02x %02x ", (char)MUSE_SYNTH_SYSEX_MFG_ID, (char)VST_NATIVE_SYNTH_UNIQUE_ID); // Wrap in a proper header
+ xml.nput("56 53 54 53 41 56 45 "); // embed a save marker "string 'VSTSAVE'
+ xml.nput("%02x ", (char)0); // No flags yet, only uncompressed supported for now. TODO
+ for (unsigned long int i = 0; i < len; ++i)
+ {
+ if (i && (((i+10) % 16) == 0))
+ {
+ xml.nput("\n");
+ xml.nput(level, "");
+ }
+ xml.nput("%02x ", ((char*)(p))[i] & 0xff);
+ }
+ xml.nput("\n");
+ xml.tag(level--, "/event");
+ xml.etag(level--, "midistate");
+ }
+ }
+#else
+ fprintf(stderr, "support for vst chunks not compiled in!\n");
+#endif
+
+ //---------------------------------------------
+ // dump current state of synth
+ //---------------------------------------------
+
+ int params = _plugin->numParams;
+ for (int i = 0; i < params; ++i)
+ {
+ float f = _plugin->getParameter(_plugin, i);
+ xml.floatTag(level, "param", f);
+ }
+}
+
+//---------------------------------------------------------
+// getData
+//---------------------------------------------------------
+
+void VstNativeSynthIF::setVstEvent(VstMidiEvent* event, int a, int b, int c, int d)
+{
+ event->type = kVstMidiType;
+ event->byteSize = 24;
+ event->deltaFrames = 0;
+ event->flags = 0;
+ event->detune = 0;
+ event->noteLength = 0;
+ event->noteOffset = 0;
+ event->reserved1 = 0;
+ event->reserved2 = 0;
+ event->noteOffVelocity = 0;
+ event->midiData[0] = a;
+ event->midiData[1] = b;
+ event->midiData[2] = c;
+ event->midiData[3] = d;
+}
+
+//---------------------------------------------------------
+// getData
+//---------------------------------------------------------
+
+bool VstNativeSynthIF::processEvent(const MusECore::MidiPlayEvent& e, VstMidiEvent* event)
+{
+ int type = e.type();
+ int chn = e.channel();
+ int a = e.dataA();
+ int b = e.dataB();
+
+ int len = e.len();
+ char ca[len + 2];
+
+ ca[0] = 0xF0;
+ memcpy(ca + 1, (const char*)e.data(), len);
+ ca[len + 1] = 0xF7;
+
+ len += 2;
+
+ #ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "VstNativeSynthIF::processEvent midi event type:%d chn:%d a:%d b:%d\n", type, chn, a, b);
+ #endif
+
+ switch(type)
+ {
+ case MusECore::ME_NOTEON:
+ #ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "VstNativeSynthIF::processEvent midi event is MusECore::ME_NOTEON\n");
+ #endif
+ setVstEvent(event, (type | chn) & 0xff, a & 0x7f, b & 0x7f);
+ break;
+ case MusECore::ME_NOTEOFF:
+ #ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "VstNativeSynthIF::processEvent midi event is MusECore::ME_NOTEOFF\n");
+ #endif
+ setVstEvent(event, (type | chn) & 0xff, a & 0x7f, 0);
+ break;
+ case MusECore::ME_PROGRAM:
+ {
+ #ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "VstNativeSynthIF::processEvent midi event is MusECore::ME_PROGRAM\n");
+ #endif
+
+ int bankH = (a >> 16) & 0xff;
+ int bankL = (a >> 8) & 0xff;
+ int prog = a & 0xff;
+ synti->_curBankH = bankH;
+ synti->_curBankL = bankL;
+ synti->_curProgram = prog;
+ doSelectProgram(bankH, bankL, prog);
+ return false; // Event pointer not filled. Return false.
+ }
+ break;
+ case MusECore::ME_CONTROLLER:
+ {
+ #ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "VstNativeSynthIF::processEvent midi event is MusECore::ME_CONTROLLER\n");
+ #endif
+
+ if((a == 0) || (a == 32))
+ return false;
+
+ if(a == MusECore::CTRL_PROGRAM)
+ {
+ #ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "VstNativeSynthIF::processEvent midi event is MusECore::ME_CONTROLLER, dataA is MusECore::CTRL_PROGRAM\n");
+ #endif
+
+ int bankH = (b >> 16) & 0xff;
+ int bankL = (b >> 8) & 0xff;
+ int prog = b & 0xff;
+
+ synti->_curBankH = bankH;
+ synti->_curBankL = bankL;
+ synti->_curProgram = prog;
+ doSelectProgram(bankH, bankL, prog);
+ return false; // Event pointer not filled. Return false.
+ }
+
+ if(a == MusECore::CTRL_PITCH)
+ {
+ #ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "VstNativeSynthIF::processEvent midi event is MusECore::ME_CONTROLLER, dataA is MusECore::CTRL_PITCH\n");
+ #endif
+ int v = b + 8192;
+ setVstEvent(event, (type | chn) & 0xff, v & 0x7f, (v >> 7) & 0x7f);
+ return true;
+ }
+
+ if(a == MusECore::CTRL_AFTERTOUCH)
+ {
+ #ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "VstNativeSynthIF::processEvent midi event is MusECore::ME_CONTROLLER, dataA is MusECore::CTRL_AFTERTOUCH\n");
+ #endif
+ setVstEvent(event, (type | chn) & 0xff, b & 0x7f);
+ return true;
+ }
+
+ if((a | 0xff) == MusECore::CTRL_POLYAFTER)
+ {
+ #ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "VstNativeSynthIF::processEvent midi event is MusECore::ME_CONTROLLER, dataA is MusECore::CTRL_POLYAFTER\n");
+ #endif
+ setVstEvent(event, (type | chn) & 0xff, a & 0x7f, b & 0x7f);
+ return true;
+ }
+
+ #ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "VstNativeSynthIF::processEvent midi event is MusECore::ME_CONTROLLER, dataA is:%d\n", a);
+ #endif
+
+ // Regular controller. Pass it on.
+ setVstEvent(event, (type | chn) & 0xff, a & 0x7f, b & 0x7f);
+
+ return true;
+
+// REMOVE Tim. Or keep. TODO For native vsts? Or not...
+//
+// const LADSPA_Descriptor* ld = dssi->LADSPA_Plugin;
+//
+// MusECore::ciMidiCtl2LadspaPort ip = synth->midiCtl2PortMap.find(a);
+// // Is it just a regular midi controller, not mapped to a LADSPA port (either by the plugin or by us)?
+// // NOTE: There's no way to tell which of these controllers is supported by the plugin.
+// // For example sustain footpedal or pitch bend may be supported, but not mapped to any LADSPA port.
+// if(ip == synth->midiCtl2PortMap.end())
+// {
+// int ctlnum = a;
+// if(MusECore::midiControllerType(a) != MusECore::MidiController::Controller7)
+// return false; // Event pointer not filled. Return false.
+// else
+// {
+// #ifdef VST_NATIVE_DEBUG
+// fprintf(stderr, "VstNativeSynthIF::processEvent non-ladspa midi event is Controller7. Current dataA:%d\n", a);
+// #endif
+// a &= 0x7f;
+// ctlnum = DSSI_CC_NUMBER(ctlnum);
+// }
+//
+// // Fill the event.
+// #ifdef VST_NATIVE_DEBUG
+// fprintf(stderr, "VstNativeSynthIF::processEvent non-ladspa filling midi event chn:%d dataA:%d dataB:%d\n", chn, a, b);
+// #endif
+// snd_seq_ev_clear(event);
+// event->queue = SND_SEQ_QUEUE_DIRECT;
+// snd_seq_ev_set_controller(event, chn, a, b);
+// return true;
+// }
+//
+// unsigned long k = ip->second;
+// unsigned long i = controls[k].idx;
+// int ctlnum = DSSI_NONE;
+// if(dssi->get_midi_controller_for_port)
+// ctlnum = dssi->get_midi_controller_for_port(handle, i);
+//
+// // No midi controller for the ladspa port? Send to ladspa control.
+// if(ctlnum == DSSI_NONE)
+// {
+// // Sanity check.
+// if(k > synth->_controlInPorts)
+// return false;
+//
+// // Simple but flawed solution: Start them at 0x60000 + 0x2000 = 0x62000. Max NRPN number is 0x3fff.
+// ctlnum = k + (MusECore::CTRL_NRPN14_OFFSET + 0x2000);
+// }
+// else
+// {
+// #ifdef VST_NATIVE_DEBUG
+// fprintf(stderr, "VstNativeSynthIF::processEvent plugin requests DSSI-style ctlnum:%x(h) %d(d) be mapped to control port:%lu...\n", ctlnum, ctlnum, i);
+// #endif
+//
+// int c = ctlnum;
+// // Can be both CC and NRPN! Prefer CC over NRPN.
+// if(DSSI_IS_CC(ctlnum))
+// {
+// ctlnum = DSSI_CC_NUMBER(c);
+//
+// #ifdef VST_NATIVE_DEBUG
+// fprintf(stderr, "VstNativeSynthIF::processEvent is CC ctlnum:%d\n", ctlnum);
+// #endif
+//
+// #ifdef VST_NATIVE_DEBUG
+// if(DSSI_IS_NRPN(ctlnum))
+// fprintf(stderr, "VstNativeSynthIF::processEvent is also NRPN control. Using CC.\n");
+// #endif
+// }
+// else
+// if(DSSI_IS_NRPN(ctlnum))
+// {
+// ctlnum = DSSI_NRPN_NUMBER(c) + MusECore::CTRL_NRPN14_OFFSET;
+//
+// #ifdef VST_NATIVE_DEBUG
+// fprintf(stderr, "VstNativeSynthIF::processEvent is NRPN ctlnum:%x(h) %d(d)\n", ctlnum, ctlnum);
+// #endif
+// }
+//
+// }
+//
+// float val = midi2LadspaValue(ld, i, ctlnum, b);
+//
+// #ifdef VST_NATIVE_DEBUG
+// fprintf(stderr, "VstNativeSynthIF::processEvent control port:%lu port:%lu dataA:%d Converting val from:%d to ladspa:%f\n", i, k, a, b, val);
+// #endif
+//
+// // Set the ladspa port value.
+// controls[k].val = val;
+//
+// // Need to update the automation value, otherwise it overwrites later with the last automation value.
+// if(id() != -1)
+// // We're in the audio thread context: no need to send a message, just modify directly.
+// synti->setPluginCtrlVal(genACnum(id(), k), val);
+//
+// // Since we absorbed the message as a ladspa control change, return false - the event is not filled.
+// return false;
+ }
+ break;
+ case MusECore::ME_PITCHBEND:
+ {
+ int v = a + 8192;
+ setVstEvent(event, (type | chn) & 0xff, v & 0x7f, (v >> 7) & 0x7f);
+ }
+ break;
+ case MusECore::ME_AFTERTOUCH:
+ setVstEvent(event, (type | chn) & 0xff, a & 0x7f);
+ break;
+ case MusECore::ME_POLYAFTER:
+ setVstEvent(event, (type | chn) & 0xff, a & 0x7f, b & 0x7f);
+ break;
+ case MusECore::ME_SYSEX:
+ {
+ #ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "VstNativeSynthIF::processEvent midi event is MusECore::ME_SYSEX\n");
+ #endif
+
+ const unsigned char* data = e.data();
+ if(e.len() >= 2)
+ {
+ if(data[0] == MUSE_SYNTH_SYSEX_MFG_ID)
+ {
+ if(data[1] == VST_NATIVE_SYNTH_UNIQUE_ID)
+ {
+ //if(e.len() >= 9)
+ if(e.len() >= 10)
+ {
+ if (QString((const char*)(data + 2)).startsWith("VSTSAVE"))
+ {
+ if(_synth->hasChunks())
+ {
+#ifdef VST_SDK_SUPPORT
+ int chunk_flags = data[9];
+ if(chunk_flags & VST_NATIVE_CHUNK_FLAG_COMPRESSED)
+ fprintf(stderr, "chunk flags:%x compressed chunks not supported yet.\n", chunk_flags);
+ else
+ {
+ fprintf(stderr, "%s: loading chunk from sysex!\n", name().toLatin1().constData());
+ // 10 = 2 bytes header + "VSTSAVE" + 1 byte flags (compression etc)
+ len = dispatch(effSetChunk, 0, e.len()-10, (void*)(data+10), 0.0); // index 0: is bank 1: is program
+ }
+#else
+ fprintf(stderr, "support for vst chunks not compiled in!\n");
+#endif
+ }
+ // Event not filled.
+ return false;
+ }
+ }
+ }
+ }
+ }
+
+ // DELETETHIS, 50 clean it up or fix it?
+ /*
+ // p3.3.39 Read the state of current bank and program and all input control values.
+ // TODO: Needs to be better. See write().
+ //else
+ if (QString((const char*)e.data()).startsWith("PARAMSAVE"))
+ {
+ #ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "VstNativeSynthIF::processEvent midi event is MusECore::ME_SYSEX PARAMSAVE\n");
+ #endif
+
+ unsigned long dlen = e.len() - 9; // Minus "PARAMSAVE"
+ if(dlen > 0)
+ {
+ //if(dlen < 2 * sizeof(unsigned long))
+ if(dlen < (2 + 2 * sizeof(unsigned long))) // Version major and minor bytes, bank and program.
+ fprintf(stderr, "VstNativeSynthIF::processEvent Error: PARAMSAVE data length does not include at least version major and minor, bank and program!\n");
+ else
+ {
+ // Not required, yet.
+ //char vmaj = *((char*)(e.data() + 9)); // After "PARAMSAVE"
+ //char vmin = *((char*)(e.data() + 10));
+
+ unsigned long* const ulp = (unsigned long*)(e.data() + 11); // After "PARAMSAVE" + version major and minor.
+ // TODO: TODO: Set plugin bank and program.
+ _curBank = ulp[0];
+ _curProgram = ulp[1];
+
+ dlen -= (2 + 2 * sizeof(unsigned long)); // After the version major and minor, bank and program.
+
+ if(dlen > 0)
+ {
+ if((dlen % sizeof(float)) != 0)
+ fprintf(stderr, "VstNativeSynthIF::processEvent Error: PARAMSAVE float data length not integral multiple of float size!\n");
+ else
+ {
+ const unsigned long n = dlen / sizeof(float);
+ if(n != synth->_controlInPorts)
+ fprintf(stderr, "VstNativeSynthIF::processEvent Warning: PARAMSAVE number of floats:%lu != number of controls:%lu\n", n, synth->_controlInPorts);
+
+ // Point to location after "PARAMSAVE", version major and minor, bank and progam.
+ float* const fp = (float*)(e.data() + 9 + 2 + 2 * sizeof(unsigned long));
+
+ for(unsigned long i = 0; i < synth->_controlInPorts && i < n; ++i)
+ {
+ const float v = fp[i];
+ controls[i].val = v;
+ }
+ }
+ }
+ }
+ }
+ // Event not filled.
+ return false;
+ }
+ */
+ //else
+ {
+ // FIXME TODO: Sysex support.
+ return false;
+
+ // NOTE: There is a limit on the size of a sysex. Got this:
+ // "VstNativeSynthIF::processEvent midi event is MusECore::ME_SYSEX"
+ // "WARNING: MIDI event of type ? decoded to 367 bytes, discarding"
+ // That might be ALSA doing that.
+// snd_seq_ev_clear(event);
+// event->queue = SND_SEQ_QUEUE_DIRECT;
+// snd_seq_ev_set_sysex(event, len,
+// (unsigned char*)ca);
+ }
+ }
+ break;
+ default:
+ if(MusEGlobal::debugMsg)
+ fprintf(stderr, "VstNativeSynthIF::processEvent midi event unknown type:%d\n", e.type());
+ // Event not filled.
+ return false;
+ break;
+ }
+
+ return true;
+}
+
+//---------------------------------------------------------
+// getData
+//---------------------------------------------------------
+
+iMPEvent VstNativeSynthIF::getData(MidiPort* /*mp*/, MPEventList* el, iMPEvent start_event, unsigned pos, int ports, unsigned nframes, float** buffer)
+{
+ // We may not be using nevents all at once - this will be just the maximum.
+ unsigned long nevents = el->size() + synti->eventFifo.getSize();
+ VstMidiEvent events[nevents];
+ char evbuf[sizeof(VstMidiEvent*) * nevents + sizeof(VstEvents)];
+ VstEvents *vst_events = (VstEvents*)evbuf;
+ vst_events->numEvents = 0;
+ vst_events->reserved = 0;
+
+ int frameOffset = MusEGlobal::audio->getFrameOffset();
+ unsigned long syncFrame = MusEGlobal::audio->curSyncFrame();
+
+ #ifdef VST_NATIVE_DEBUG_PROCESS
+ fprintf(stderr, "VstNativeSynthIF::getData: pos:%u ports:%d nframes:%u syncFrame:%lu nevents:%lu\n", pos, ports, nframes, syncFrame, nevents);
+ #endif
+
+ unsigned long nop, k;
+ nop = ((unsigned long) ports) > _synth->outPorts() ? _synth->outPorts() : ((unsigned long) ports);
+ unsigned long sample = 0;
+
+ // I read that some plugins do not like changing sample run length (compressors etc). Some are OK with it.
+ // TODO: In order to support this effectively, must be user selectable, per-plugin. ENABLED for now.
+ const bool usefixedrate = false;
+ unsigned long fixedsize = nframes;
+
+ // For now, the fixed size is clamped to the MusEGlobal::audio buffer size.
+ // TODO: We could later add slower processing over several cycles -
+ // so that users can select a small MusEGlobal::audio period but a larger control period.
+ if(fixedsize > nframes)
+ fixedsize = nframes;
+
+ unsigned long min_per = MusEGlobal::config.minControlProcessPeriod; // Must be power of 2 !
+ if(min_per > nframes)
+ min_per = nframes;
+
+ // Inform the host callback we are in the audio thread.
+ _inProcess = true;
+
+ #ifdef VST_NATIVE_DEBUG_PROCESS
+ fprintf(stderr, "VstNativeSynthIF::getData: Handling inputs...\n");
+ #endif
+
+ // Handle inputs...
+ if(!((MusECore::AudioTrack*)synti)->noInRoute())
+ {
+ RouteList* irl = ((MusECore::AudioTrack*)synti)->inRoutes();
+ iRoute i = irl->begin();
+ if(!i->track->isMidiTrack())
+ {
+ int ch = i->channel == -1 ? 0 : i->channel;
+ int remch = i->remoteChannel == -1 ? 0 : i->remoteChannel;
+ int chs = i->channels == -1 ? 0 : i->channels;
+
+ if((unsigned)ch < _synth->inPorts() && (unsigned)(ch + chs) <= _synth->inPorts())
+ {
+ int h = remch + chs;
+ for(int j = remch; j < h; ++j)
+ _iUsedIdx[j] = true;
+
+ ((MusECore::AudioTrack*)i->track)->copyData(pos, chs, ch, -1, nframes, &_audioInBuffers[remch]);
+ }
+ }
+
+ ++i;
+ for(; i != irl->end(); ++i)
+ {
+ if(i->track->isMidiTrack())
+ continue;
+
+ int ch = i->channel == -1 ? 0 : i->channel;
+ int remch = i->remoteChannel == -1 ? 0 : i->remoteChannel;
+ int chs = i->channels == -1 ? 0 : i->channels;
+
+ if((unsigned)ch < _synth->inPorts() && (unsigned)(ch + chs) <= _synth->inPorts())
+ {
+ bool u1 = _iUsedIdx[remch];
+ if(chs >= 2)
+ {
+ bool u2 = _iUsedIdx[remch + 1];
+ if(u1 && u2)
+ ((MusECore::AudioTrack*)i->track)->addData(pos, chs, ch, -1, nframes, &_audioInBuffers[remch]);
+ else
+ if(!u1 && !u2)
+ ((MusECore::AudioTrack*)i->track)->copyData(pos, chs, ch, -1, nframes, &_audioInBuffers[remch]);
+ else
+ {
+ if(u1)
+ ((MusECore::AudioTrack*)i->track)->addData(pos, 1, ch, 1, nframes, &_audioInBuffers[remch]);
+ else
+ ((MusECore::AudioTrack*)i->track)->copyData(pos, 1, ch, 1, nframes, &_audioInBuffers[remch]);
+
+ if(u2)
+ ((MusECore::AudioTrack*)i->track)->addData(pos, 1, ch + 1, 1, nframes, &_audioInBuffers[remch + 1]);
+ else
+ ((MusECore::AudioTrack*)i->track)->copyData(pos, 1, ch + 1, 1, nframes, &_audioInBuffers[remch + 1]);
+ }
+ }
+ else
+ {
+ if(u1)
+ ((MusECore::AudioTrack*)i->track)->addData(pos, 1, ch, -1, nframes, &_audioInBuffers[remch]);
+ else
+ ((MusECore::AudioTrack*)i->track)->copyData(pos, 1, ch, -1, nframes, &_audioInBuffers[remch]);
+ }
+
+ int h = remch + chs;
+ for(int j = remch; j < h; ++j)
+ _iUsedIdx[j] = true;
+ }
+ }
+ }
+
+ #ifdef VST_NATIVE_DEBUG_PROCESS
+ fprintf(stderr, "VstNativeSynthIF::getData: Processing automation control values...\n");
+ #endif
+
+ while(sample < nframes)
+ {
+ unsigned long nsamp = usefixedrate ? fixedsize : nframes - sample;
+
+ //
+ // Process automation control values, while also determining the maximum acceptable
+ // size of this run. Further processing, from FIFOs for example, can lower the size
+ // from there, but this section determines where the next highest maximum frame
+ // absolutely needs to be for smooth playback of the controller value stream...
+ //
+ if(id() != -1)
+ {
+ unsigned long frame = pos + sample;
+ AutomationType at = AUTO_OFF;
+ at = synti->automationType();
+ bool no_auto = !MusEGlobal::automation || at == AUTO_OFF;
+ AudioTrack* track = (static_cast<AudioTrack*>(synti));
+ int nextFrame;
+ const unsigned long in_ctrls = _synth->inControls();
+ for(unsigned long k = 0; k < in_ctrls; ++k)
+ {
+ //_controls[k].val = track->controller()->value(genACnum(id(), k), frame,
+ // no_auto || !_controls[k].enCtrl || !_controls[k].en2Ctrl,
+ // &nextFrame);
+ if(dispatch(effCanBeAutomated, k, 0, NULL, 0.0f) == 1)
+ _plugin->setParameter(_plugin, k, track->controller()->value(genACnum(id(), k), frame,
+ no_auto || !_controls[k].enCtrl || !_controls[k].en2Ctrl,
+ &nextFrame));
+#ifdef VST_NATIVE_DEBUG
+ else
+ fprintf(stderr, "VstNativeSynthIF::getData %s parameter:%lu cannot be automated\n", name().toLatin1().constData(), k);
+#endif
+
+#ifdef VST_NATIVE_DEBUG_PROCESS
+ fprintf(stderr, "VstNativeSynthIF::getData k:%lu sample:%lu frame:%lu nextFrame:%d nsamp:%lu \n", k, sample, frame, nextFrame, nsamp);
+#endif
+ if(MusEGlobal::audio->isPlaying() && !usefixedrate && nextFrame != -1)
+ {
+ // Returned value of nextFrame can be zero meaning caller replaces with some (constant) value.
+ unsigned long samps = (unsigned long)nextFrame;
+ if(samps > frame + min_per)
+ {
+ unsigned long diff = samps - frame;
+ unsigned long mask = min_per-1; // min_per must be power of 2
+ samps = diff & ~mask;
+ if((diff & mask) != 0)
+ samps += min_per;
+ }
+ else
+ samps = min_per;
+
+ if(samps < nsamp)
+ nsamp = samps;
+ }
+ }
+#ifdef VST_NATIVE_DEBUG_PROCESS
+ fprintf(stderr, "VstNativeSynthIF::getData sample:%lu nsamp:%lu\n", sample, nsamp);
+#endif
+ }
+
+ bool found = false;
+ unsigned long frame = 0;
+ unsigned long index = 0;
+ unsigned long evframe;
+ // Get all control ring buffer items valid for this time period...
+ while(!_controlFifo.isEmpty())
+ {
+ ControlEvent v = _controlFifo.peek();
+ // The events happened in the last period or even before that. Shift into this period with + n. This will sync with audio.
+ // If the events happened even before current frame - n, make sure they are counted immediately as zero-frame.
+ evframe = (syncFrame > v.frame + nframes) ? 0 : v.frame - syncFrame + nframes;
+
+ #ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "VstNativeSynthIF::getData found:%d evframe:%lu frame:%lu event frame:%lu idx:%lu val:%f unique:%d\n",
+ found, evframe, frame, v.frame, v.idx, v.value, v.unique);
+ #endif
+
+ // Protection. Observed this condition. Why? Supposed to be linear timestamps.
+ if(found && evframe < frame)
+ {
+ fprintf(stderr, "VstNativeSynthIF::getData *** Error: evframe:%lu < frame:%lu event: frame:%lu idx:%lu val:%f unique:%d\n",
+ evframe, frame, v.frame, v.idx, v.value, v.unique);
+
+ // No choice but to ignore it.
+ _controlFifo.remove(); // Done with the ring buffer's item. Remove it.
+ continue;
+ }
+
+ if(evframe >= nframes // Next events are for a later period.
+ || (!usefixedrate && !found && !v.unique && (evframe - sample >= nsamp)) // Next events are for a later run in this period. (Autom took prio.)
+ || (found && !v.unique && (evframe - sample >= min_per)) // Eat up events within minimum slice - they're too close.
+ || (usefixedrate && found && v.unique && v.idx == index)) // Special for dssi-vst: Fixed rate and must reply to all.
+ break;
+ _controlFifo.remove(); // Done with the ring buffer's item. Remove it.
+
+ if(v.idx >= _synth->inControls()) // Sanity check.
+ break;
+ found = true;
+ frame = evframe;
+ index = v.idx;
+ // Set the ladspa control port value.
+ //controls[v.idx].val = v.value;
+ if(dispatch(effCanBeAutomated, v.idx, 0, NULL, 0.0f) == 1)
+ _plugin->setParameter(_plugin, v.idx, v.value);
+#ifdef VST_NATIVE_DEBUG
+ else
+ fprintf(stderr, "VstNativeSynthIF::getData %s parameter:%lu cannot be automated\n", name().toLatin1().constData(), v.idx);
+#endif
+
+ // Need to update the automation value, otherwise it overwrites later with the last automation value.
+ if(id() != -1)
+ synti->setPluginCtrlVal(genACnum(id(), v.idx), v.value);
+ }
+
+ if(found && !usefixedrate) // If a control FIFO item was found, takes priority over automation controller stream.
+ nsamp = frame - sample;
+
+ if(sample + nsamp >= nframes) // Safety check.
+ nsamp = nframes - sample;
+
+ // TODO: Don't allow zero-length runs. This could/should be checked in the control loop instead.
+ // Note this means it is still possible to get stuck in the top loop (at least for a while).
+ if(nsamp == 0)
+ continue;
+
+ nevents = 0;
+ // Process event list events...
+ for(; start_event != el->end(); ++start_event)
+ {
+ #ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "VstNativeSynthIF::getData eventlist event time:%d pos:%u sample:%lu nsamp:%lu frameOffset:%d\n", start_event->time(), pos, sample, nsamp, frameOffset);
+ #endif
+
+ if(start_event->time() >= (pos + sample + nsamp + frameOffset)) // frameOffset? Test again...
+ {
+ #ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, " event is for future:%lu, breaking loop now\n", start_event->time() - frameOffset - pos - sample);
+ #endif
+ break;
+ }
+
+ // Update hardware state so knobs and boxes are updated. Optimize to avoid re-setting existing values.
+ // Same code as in MidiPort::sendEvent()
+ if(synti->midiPort() != -1)
+ {
+ MusECore::MidiPort* mp = &MusEGlobal::midiPorts[synti->midiPort()];
+ if(start_event->type() == MusECore::ME_CONTROLLER)
+ {
+ int da = start_event->dataA();
+ int db = start_event->dataB();
+ db = mp->limitValToInstrCtlRange(da, db);
+ if(!mp->setHwCtrlState(start_event->channel(), da, db))
+ continue;
+ }
+ else if(start_event->type() == MusECore::ME_PITCHBEND)
+ {
+ int da = mp->limitValToInstrCtlRange(MusECore::CTRL_PITCH, start_event->dataA());
+ if(!mp->setHwCtrlState(start_event->channel(), MusECore::CTRL_PITCH, da))
+ continue;
+ }
+ else if(start_event->type() == MusECore::ME_AFTERTOUCH)
+ {
+ int da = mp->limitValToInstrCtlRange(MusECore::CTRL_AFTERTOUCH, start_event->dataA());
+ if(!mp->setHwCtrlState(start_event->channel(), MusECore::CTRL_AFTERTOUCH, da))
+ continue;
+ }
+ else if(start_event->type() == MusECore::ME_POLYAFTER)
+ {
+ int ctl = (MusECore::CTRL_POLYAFTER & ~0xff) | (start_event->dataA() & 0x7f);
+ int db = mp->limitValToInstrCtlRange(ctl, start_event->dataB());
+ if(!mp->setHwCtrlState(start_event->channel(), ctl , db))
+ continue;
+ }
+ else if(start_event->type() == MusECore::ME_PROGRAM)
+ {
+ if(!mp->setHwCtrlState(start_event->channel(), MusECore::CTRL_PROGRAM, start_event->dataA()))
+ continue;
+ }
+ }
+
+ // Returns false if the event was not filled. It was handled, but some other way.
+ if(processEvent(*start_event, &events[nevents]))
+ {
+ // Time-stamp the event.
+ int ft = start_event->time() - frameOffset - pos - sample;
+ if(ft < 0)
+ ft = 0;
+
+ if (ft >= int(nsamp))
+ {
+ fprintf(stderr, "VstNativeSynthIF::getData: eventlist event time:%d out of range. pos:%d offset:%d ft:%d sample:%lu nsamp:%lu\n", start_event->time(), pos, frameOffset, ft, sample, nsamp);
+ ft = nsamp - 1;
+ }
+
+ #ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "VstNativeSynthIF::getData eventlist: ft:%d current nevents:%lu\n", ft, nevents);
+ #endif
+
+ vst_events->events[nevents] = (VstEvent*)&events[nevents];
+ events[nevents].deltaFrames = ft;
+ ++nevents;
+ }
+ }
+
+ // Now process putEvent events...
+ while(!synti->eventFifo.isEmpty())
+ {
+ MusECore::MidiPlayEvent e = synti->eventFifo.peek();
+
+ #ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "VstNativeSynthIF::getData eventFifo event time:%d\n", e.time());
+ #endif
+
+ if(e.time() >= (pos + sample + nsamp + frameOffset))
+ break;
+
+ synti->eventFifo.remove(); // Done with ring buffer's event. Remove it.
+ // Returns false if the event was not filled. It was handled, but some other way.
+ if(processEvent(e, &events[nevents]))
+ {
+ // Time-stamp the event.
+ int ft = e.time() - frameOffset - pos - sample;
+ if(ft < 0)
+ ft = 0;
+ if (ft >= int(nsamp))
+ {
+ fprintf(stderr, "VstNativeSynthIF::getData: eventFifo event time:%d out of range. pos:%d offset:%d ft:%d sample:%lu nsamp:%lu\n", e.time(), pos, frameOffset, ft, sample, nsamp);
+ ft = nsamp - 1;
+ }
+ vst_events->events[nevents] = (VstEvent*)&events[nevents];
+ events[nevents].deltaFrames = ft;
+
+ ++nevents;
+ }
+ }
+
+ #ifdef VST_NATIVE_DEBUG_PROCESS
+ fprintf(stderr, "VstNativeSynthIF::getData: Connecting and running. sample:%lu nsamp:%lu nevents:%lu\n", sample, nsamp, nevents);
+ #endif
+
+ // Set the events pointer.
+ if(nevents > 0)
+ {
+ vst_events->numEvents = nevents;
+ dispatch(effProcessEvents, 0, 0, vst_events, 0.0f);
+ }
+
+ float* in_bufs[_synth->inPorts()];
+ float* out_bufs[_synth->outPorts()];
+
+ k = 0;
+ // Connect the given buffers directly to the ports, up to a max of synth ports.
+ for(; k < nop; ++k)
+ out_bufs[k] = buffer[k] + sample;
+ // Connect the remaining ports to some local buffers (not used yet).
+ const unsigned long op =_synth->outPorts();
+ for(; k < op; ++k)
+ out_bufs[k] = _audioOutBuffers[k] + sample;
+ // Connect all inputs either to some local buffers, or a silence buffer.
+ const unsigned long ip =_synth->inPorts();
+ for(k = 0; k < ip; ++k)
+ {
+ if(_iUsedIdx[k])
+ {
+ _iUsedIdx[k] = false; // Reset
+ in_bufs[k] = _audioInBuffers[k] + sample;
+ }
+ else
+ in_bufs[k] = _audioInSilenceBuf + sample;
+ }
+
+ // Run the synth for a period of time. This processes events and gets/fills our local buffers...
+ if((_plugin->flags & effFlagsCanReplacing) && _plugin->processReplacing)
+ {
+ _plugin->processReplacing(_plugin, in_bufs, out_bufs, nsamp);
+ }
+#if 0 //ifdef VST_NATIVE_FORCE_DEPRECATED
+ else
+ {
+ // TODO: This is not right, we don't accumulate data here. Depricated now, and frowned upon anyway...
+ if(_plugin->process)
+ _plugin->process(_plugin, in_bufs, out_bufs, nsamp);
+ }
+#endif
+
+ sample += nsamp;
+ }
+
+ // Inform the host callback we will be no longer in the audio thread.
+ _inProcess = true;
+
+ return start_event;
+}
+
+//---------------------------------------------------------
+// putEvent
+//---------------------------------------------------------
+
+bool VstNativeSynthIF::putEvent(const MidiPlayEvent& ev)
+ {
+ #ifdef VST_NATIVE_DEBUG
+ fprintf(stderr, "VstNativeSynthIF::putEvent midi event time:%d chn:%d a:%d b:%d\n", ev.time(), ev.channel(), ev.dataA(), ev.dataB());
+ #endif
+
+ if (MusEGlobal::midiOutputTrace)
+ ev.dump();
+ return synti->eventFifo.put(ev);
+ }
+
+
+//--------------------------------
+// Methods for PluginIBase:
+//--------------------------------
+
+unsigned long VstNativeSynthIF::pluginID() { return (_plugin) ? _plugin->uniqueID : 0; }
+int VstNativeSynthIF::id() { return MAX_PLUGINS; } // Set for special block reserved for synth.
+QString VstNativeSynthIF::pluginLabel() const { return _synth ? QString(_synth->name()) : QString(); } // FIXME Maybe wrong
+QString VstNativeSynthIF::lib() const { return _synth ? _synth->completeBaseName() : QString(); }
+QString VstNativeSynthIF::dirPath() const { return _synth ? _synth->absolutePath() : QString(); }
+QString VstNativeSynthIF::fileName() const { return _synth ? _synth->fileName() : QString(); }
+void VstNativeSynthIF::enableController(unsigned long i, bool v) { _controls[i].enCtrl = v; }
+bool VstNativeSynthIF::controllerEnabled(unsigned long i) const { return _controls[i].enCtrl;}
+void VstNativeSynthIF::enable2Controller(unsigned long i, bool v) { _controls[i].en2Ctrl = v; }
+bool VstNativeSynthIF::controllerEnabled2(unsigned long i) const { return _controls[i].en2Ctrl; }
+void VstNativeSynthIF::enableAllControllers(bool v)
+{
+ if(!_synth)
+ return;
+ for(unsigned long i = 0; i < _synth->inControls(); ++i)
+ _controls[i].enCtrl = v;
+}
+void VstNativeSynthIF::enable2AllControllers(bool v)
+{
+ if(!_synth)
+ return;
+ for(unsigned long i = 0; i < _synth->inControls(); ++i)
+ _controls[i].en2Ctrl = v;
+}
+void VstNativeSynthIF::updateControllers() { }
+void VstNativeSynthIF::activate()
+{
+ //for (unsigned short i = 0; i < instances(); ++i) {
+ // dispatch(i, effMainsChanged, 0, 1, NULL, 0.0f);
+ dispatch(effMainsChanged, 0, 1, NULL, 0.0f);
+//#ifdef VST_SDK_SUPPORT
+ //dispatch(i, effStartProcess, 0, 0, NULL, 0.0f);
+ dispatch(effStartProcess, 0, 0, NULL, 0.0f);
+//#endif
+ //}
+
+// REMOVE Tim. Or keep? From PluginI::activate().
+// if (initControlValues) {
+// for (unsigned long i = 0; i < controlPorts; ++i) {
+// controls[i].val = controls[i].tmpVal;
+// }
+// }
+// else {
+// // get initial control values from plugin
+// for (unsigned long i = 0; i < controlPorts; ++i) {
+// controls[i].tmpVal = controls[i].val;
+// }
+// }
+}
+void VstNativeSynthIF::deactivate()
+{
+ //for (unsigned short i = 0; i < instances(); ++i) {
+//#ifdef VST_SDK_SUPPORT
+ //dispatch(i, effStopProcess, 0, 0, NULL, 0.0f);
+ dispatch(effStopProcess, 0, 0, NULL, 0.0f);
+//#endif
+ //dispatch(i, effMainsChanged, 0, 0, NULL, 0.0f);
+ dispatch(effMainsChanged, 0, 0, NULL, 0.0f);
+ //}
+}
+
+unsigned long VstNativeSynthIF::parameters() const { return _synth ? _synth->inControls() : 0; }
+unsigned long VstNativeSynthIF::parametersOut() const { return 0; }
+void VstNativeSynthIF::setParam(unsigned long i, float val) { setParameter(i, val); }
+float VstNativeSynthIF::param(unsigned long i) const { return getParameter(i); }
+float VstNativeSynthIF::paramOut(unsigned long) const { return 0.0; }
+const char* VstNativeSynthIF::paramName(unsigned long i)
+{
+ if(!_plugin)
+ return 0;
+ static char buf[256];
+ buf[0] = 0;
+ dispatch(effGetParamName, i, 0, buf, 0);
+ return buf;
+}
+
+const char* VstNativeSynthIF::paramOutName(unsigned long) { return 0; }
+LADSPA_PortRangeHint VstNativeSynthIF::range(unsigned long /*i*/)
+{
+ LADSPA_PortRangeHint h;
+ // FIXME TODO:
+ h.HintDescriptor = 0;
+ h.LowerBound = 0.0;
+ h.UpperBound = 1.0;
+ return h;
+}
+LADSPA_PortRangeHint VstNativeSynthIF::rangeOut(unsigned long)
+{
+ // There are no output controls.
+ LADSPA_PortRangeHint h;
+ h.HintDescriptor = 0;
+ h.LowerBound = 0.0;
+ h.UpperBound = 1.0;
+ return h;
+}
+// FIXME TODO:
+CtrlValueType VstNativeSynthIF::ctrlValueType(unsigned long /*i*/) const { return VAL_LINEAR; }
+CtrlList::Mode VstNativeSynthIF::ctrlMode(unsigned long /*i*/) const { return CtrlList::INTERPOLATE; };
+
+} // namespace MusECore
+
+#else // VST_NATIVE_SUPPORT
+namespace MusECore {
+void initVST_Native() {}
+} // namespace MusECore
+#endif
+
diff --git a/muse2/muse/vst_native.h b/muse2/muse/vst_native.h
new file mode 100644
index 00000000..3b877cd8
--- /dev/null
+++ b/muse2/muse/vst_native.h
@@ -0,0 +1,281 @@
+//=========================================================
+// MusE
+// Linux Music Editor
+//
+// vst_native.h
+// (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net)
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; version 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+//=========================================================
+
+#ifndef __VST_NATIVE_H__
+#define __VST_NATIVE_H__
+
+#include "config.h"
+
+// Make sure this number is unique among all the MESS synths and DSSI and VST host synths.
+#define VST_NATIVE_SYNTH_UNIQUE_ID 9
+// Midistate sysex initialization command.
+#define VST_NATIVE_INIT_DATA_CMD 1
+
+#define VST_NATIVE_PARAMSAVE_VERSION_MAJOR 0
+#define VST_NATIVE_PARAMSAVE_VERSION_MINOR 1
+
+#define VST_NATIVE_CHUNK_FLAG_COMPRESSED 1
+
+#ifdef VST_NATIVE_SUPPORT
+
+class VstNativeSynthIF;
+
+typedef class VstNativeSynthIF VSTPlugin;
+
+#include "aeffectx.h"
+
+//#define VST_2_3_EXTENSIONS // TODO Detect this ?
+#define VST_2_4_EXTENSIONS // TODO Detect this ?
+#define VST_NATIVE_FORCE_DEPRECATED // TODO On or off?
+
+#ifndef VST_SDK_SUPPORT
+#ifndef effCanBeAutomated
+#define effCanBeAutomated 26
+#endif
+#ifndef effGetProgramNameIndexed
+#define effGetProgramNameIndexed 29
+#endif
+#ifndef effBeginSetProgram
+#define effBeginSetProgram 67
+#endif
+#ifndef effEndSetProgram
+#define effEndSetProgram 68
+#endif
+#ifndef effStartProcess
+#define effStartProcess 71
+#endif
+#ifndef effStopProcess
+#define effStopProcess 72
+#endif
+#endif
+
+#if defined(VST_2_4_EXTENSIONS)
+#ifdef VST_SDK_SUPPORT
+typedef long VstInt32;
+typedef long VstIntPtr;
+#else
+typedef int32_t VstInt32;
+typedef intptr_t VstIntPtr;
+#endif
+#define VSTCALLBACK
+#endif
+
+#include "vst_native_editor.h"
+#include "synth.h"
+#include "plugin.h"
+#include "midictrl.h"
+
+#endif // VST_NATIVE_SUPPORT
+
+namespace MusEGui {
+class PopupMenu;
+}
+
+namespace MusECore {
+
+#ifdef VST_NATIVE_SUPPORT
+
+struct VstRect{
+ short top;
+ short left;
+ short bottom;
+ short right;
+};
+
+struct VST_Program {
+ //unsigned long bank;
+ unsigned long program;
+ QString name;
+};
+
+
+//---------------------------------------------------------
+// VstNativeSynth
+//---------------------------------------------------------
+
+class VstNativeSynth : public Synth {
+ enum VstPluginFlags
+ {
+ canSendVstEvents = 1 << 0,
+ canSendVstMidiEvents = 1 << 1,
+ canSendVstTimeInfo = 1 << 2,
+ canReceiveVstEvents = 1 << 3,
+ canReceiveVstMidiEvents = 1 << 4,
+ canReceiveVstTimeInfo = 1 << 5,
+ canProcessOffline = 1 << 6,
+ canUseAsInsert = 1 << 7,
+ canUseAsSend = 1 << 8,
+ canMixDryWet = 1 << 9,
+ canMidiProgramNames = 1 << 10
+ };
+
+ void* _handle;
+ int _vst_version;
+ unsigned int _flags;
+
+ unsigned long /*_portCount,*/ _inports, _outports, _controlInPorts; //, _controlOutPorts;
+ std::vector<unsigned long> iIdx; // Audio input index to port number.
+ std::vector<unsigned long> oIdx; // Audio output index to port number.
+ std::vector<unsigned long> rpIdx; // Port number to control input index. Item is -1 if it's not a control input.
+ MusECore::MidiCtl2LadspaPortMap midiCtl2PortMap; // Maps midi controller numbers to vst port numbers.
+ MusECore::MidiCtl2LadspaPortMap port2MidiCtlMap; // Maps vst port numbers to midi controller numbers.
+ bool _hasGui;
+ bool _inPlaceCapable;
+ bool _hasChunks;
+
+ public:
+ VstNativeSynth(const QFileInfo& fi, AEffect* plugin, const QString& label, const QString& desc, const QString& maker, const QString& ver);
+
+ virtual ~VstNativeSynth() {}
+ virtual Type synthType() const { return VST_NATIVE_SYNTH; }
+ virtual void incInstances(int val);
+ virtual AEffect* instantiate();
+ virtual SynthIF* createSIF(SynthI*);
+ unsigned long inPorts() const { return _inports; }
+ unsigned long outPorts() const { return _outports; }
+ unsigned long inControls() const { return _controlInPorts; }
+ //unsigned long outControls() const { return _controlOutPorts; }
+
+ int vstVersion() const { return _vst_version; }
+ bool hasChunks() const { return _hasChunks; }
+ const std::vector<unsigned long>* getRpIdx() { return &rpIdx; }
+ };
+
+//---------------------------------------------------------
+// VstNativeSynthIF
+// VSTi synthesizer instance
+//---------------------------------------------------------
+
+class VstNativeSynthIF : public SynthIF
+ {
+ friend class VstNativeSynth;
+ friend class MusEGui::VstNativeEditor;
+
+ VstNativeSynth* _synth;
+ AEffect* _plugin;
+ MusEGui::VstNativeEditor* _editor;
+ bool _guiVisible;
+ bool _inProcess; // To inform the callback of the 'process level' - are we in the audio thread?
+
+ Port* _controls;
+ float** _audioOutBuffers;
+ float** _audioInBuffers;
+ std::vector<unsigned long> _iUsedIdx; // During process, tells whether an audio input port was used by any input routes.
+ float* _audioInSilenceBuf; // Just all zeros all the time, so we don't have to clear for silence.
+ //float** _audioInSilenceBufs; // Just all zeros all the time, so we don't have to clear for silence.
+
+ std::vector<VST_Program> programs;
+ void queryPrograms();
+ void doSelectProgram(int bankH, int bankL, int prog);
+ bool processEvent(const MusECore::MidiPlayEvent&, VstMidiEvent*);
+ void setVstEvent(VstMidiEvent* event, int a = 0, int b = 0, int c = 0, int d = 0);
+
+ void editorDeleted();
+ void editorOpened();
+ void editorClosed();
+
+ public:
+ VstNativeSynthIF(SynthI* s);
+ virtual ~VstNativeSynthIF();
+
+ virtual bool init(Synth*);
+
+ AEffect* plugin() const { return _plugin; }
+ VstIntPtr hostCallback(VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt);
+ VstIntPtr dispatch(VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt) const {
+ if(_plugin) return _plugin->dispatcher(_plugin, opcode, index, value, ptr, opt); return 0; }
+ void idleEditor();
+ bool resizeEditor(int w, int h);
+
+ virtual bool initGui() { return true; };
+ virtual void guiHeartBeat() { }
+ virtual bool guiVisible() const;
+ virtual void showGui(bool);
+ virtual bool hasGui() const { return true; }
+ virtual bool nativeGuiVisible() const;
+ virtual void showNativeGui(bool v);
+ virtual bool hasNativeGui() const;
+ virtual void getGeometry(int*x, int*y, int*w, int*h) const { *x=0;*y=0;*w=0;*h=0; }
+ virtual void setGeometry(int, int, int, int) {}
+ virtual void getNativeGeometry(int*x, int*y, int*w, int*h) const { *x=0;*y=0;*w=0;*h=0; }
+ virtual void setNativeGeometry(int, int, int, int) {}
+ virtual void preProcessAlways() { };
+ virtual iMPEvent getData(MidiPort*, MPEventList*, iMPEvent, unsigned pos, int ports, unsigned nframes, float** buffer) ;
+ virtual bool putEvent(const MidiPlayEvent& ev);
+ virtual MidiPlayEvent receiveEvent();
+ virtual int eventsPending() const { return 0; }
+ virtual int channels() const;
+ virtual int totalOutChannels() const;
+ virtual int totalInChannels() const;
+ virtual void deactivate3();
+ virtual const char* getPatchName(int chan, int prog, bool drum);
+ virtual void populatePatchPopup(MusEGui::PopupMenu* menu, int chan, bool drum);
+ virtual void write(int level, Xml& xml) const;
+ virtual float getParameter(unsigned long idx) const;
+ virtual void setParameter(unsigned long idx, float value);
+ virtual int getControllerInfo(int, const char**, int*, int*, int*, int*) { return 0; }
+
+ virtual void guiAutomationBegin(unsigned long param_idx);
+ virtual void guiAutomationEnd(unsigned long param_idx);
+ virtual int guiControlChanged(unsigned long param_idx, float value);
+
+ //-------------------------
+ // Methods for PluginIBase:
+ //-------------------------
+ unsigned long pluginID();
+ int id();
+ QString pluginLabel() const;
+ QString lib() const;
+ QString dirPath() const;
+ QString fileName() const;
+ void enableController(unsigned long i, bool v = true);
+ bool controllerEnabled(unsigned long i) const;
+ void enable2Controller(unsigned long i, bool v = true);
+ bool controllerEnabled2(unsigned long i) const;
+ void enableAllControllers(bool v = true);
+ void enable2AllControllers(bool v = true);
+ void updateControllers();
+ void activate();
+ void deactivate();
+
+ unsigned long parameters() const;
+ unsigned long parametersOut() const;
+ void setParam(unsigned long i, float val);
+ float param(unsigned long i) const;
+ float paramOut(unsigned long i) const;
+ const char* paramName(unsigned long i);
+ const char* paramOutName(unsigned long i);
+ LADSPA_PortRangeHint range(unsigned long i);
+ LADSPA_PortRangeHint rangeOut(unsigned long i);
+ CtrlValueType ctrlValueType(unsigned long i) const;
+ CtrlList::Mode ctrlMode(unsigned long i) const;
+ };
+
+#endif // VST_NATIVE_SUPPORT
+
+extern void initVST_Native();
+
+} // namespace MusECore
+
+#endif
+
diff --git a/muse2/muse/widgets/CMakeLists.txt b/muse2/muse/widgets/CMakeLists.txt
index ed72e1a9..8c407525 100644
--- a/muse2/muse/widgets/CMakeLists.txt
+++ b/muse2/muse/widgets/CMakeLists.txt
@@ -105,6 +105,7 @@ QT4_WRAP_CPP (widget_mocs
view.h
vscale.h
visibletracks.h
+ vst_native_editor.h
warn_bad_timing.h
)
@@ -231,6 +232,7 @@ file (GLOB widgets_source_files
view.cpp
vscale.cpp
visibletracks.cpp
+ vst_native_editor.cpp
warn_bad_timing.cpp
)
diff --git a/muse2/muse/widgets/vst_native_editor.cpp b/muse2/muse/widgets/vst_native_editor.cpp
new file mode 100644
index 00000000..8f739041
--- /dev/null
+++ b/muse2/muse/widgets/vst_native_editor.cpp
@@ -0,0 +1,223 @@
+//=========================================================
+// MusE
+// Linux Music Editor
+// vst_native_editor.cpp
+// (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net)
+// Some of the editor window coding was adapted from QTractor (by rncbc aka Rui Nuno Capela)
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; version 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+//=========================================================
+
+#include "vst_native_editor.h"
+#include "vst_native.h"
+
+#include <QtGlobal>
+#if defined(Q_WS_X11)
+#include <QX11Info>
+#endif
+
+namespace MusEGui {
+
+//---------------------------------------------------------------------
+// Helpers for editor widget.
+//---------------------------------------------------------------------
+
+#if defined(Q_WS_X11)
+
+static bool g_bXError = false;
+
+static int tempXErrorHandler ( Display *, XErrorEvent * )
+{
+ g_bXError = true;
+ return 0;
+}
+
+static XEventProc getXEventProc ( Display *pDisplay, Window w )
+{
+ int iSize;
+ unsigned long iBytes, iCount;
+ unsigned char *pData;
+ XEventProc eventProc = NULL;
+ Atom aType, aName = XInternAtom(pDisplay, "_XEventProc", false);
+
+ g_bXError = false;
+ XErrorHandler oldErrorHandler = XSetErrorHandler(tempXErrorHandler);
+ XGetWindowProperty(pDisplay, w, aName, 0, 1, false,
+ AnyPropertyType, &aType, &iSize, &iCount, &iBytes, &pData);
+ if (g_bXError == false && iCount == 1)
+ eventProc = (XEventProc) (pData);
+ XSetErrorHandler(oldErrorHandler);
+
+ return eventProc;
+}
+
+static Window getXChildWindow ( Display *pDisplay, Window w )
+{
+ Window wRoot, wParent, *pwChildren;
+ unsigned int iChildren = 0;
+
+ XQueryTree(pDisplay, w, &wRoot, &wParent, &pwChildren, &iChildren);
+
+ return (iChildren > 0 ? pwChildren[0] : 0);
+}
+
+#endif // Q_WS_X11
+
+VstNativeEditor::VstNativeEditor(QWidget *parent, Qt::WindowFlags wflags)
+ : QWidget(parent, wflags),
+ #if defined(Q_WS_X11)
+ _display(QX11Info::display()),
+ _vstEditor(0),
+ _vstEventProc(0),
+ _buttonPress(false),
+ #endif
+ _sif(0)
+{
+ setAttribute(Qt::WA_DeleteOnClose);
+}
+
+VstNativeEditor::~VstNativeEditor()
+{
+ if(_sif)
+ {
+ _sif->dispatch(effEditClose, 0, 0, NULL, 0.0f);
+ _sif->editorDeleted();
+ _sif = NULL;
+ }
+}
+
+//---------------------------------------------------------------------
+// open
+//---------------------------------------------------------------------
+
+void VstNativeEditor::open(MusECore::VstNativeSynthIF* sif)
+{
+ _sif = sif;
+
+ // Start the proper (child) editor...
+ long value = 0;
+ void *ptr = (void *) winId();
+#if defined(Q_WS_X11)
+ value = (long) _display;
+#endif
+
+ MusECore::VstRect* pRect;
+ if(_sif->dispatch(effEditGetRect, 0, 0, &pRect, 0.0f))
+ {
+ int w = pRect->right - pRect->left;
+ int h = pRect->bottom - pRect->top;
+ if (w > 0 && h > 0)
+ QWidget::setFixedSize(w, h);
+ }
+
+ _sif->dispatch(effEditOpen, 0, value, ptr, 0.0f);
+
+#if defined(Q_WS_X11)
+ _vstEditor = getXChildWindow(_display, (Window) winId());
+ if(_vstEditor)
+ _vstEventProc = getXEventProc(_display, _vstEditor);
+#endif
+
+ if(_sif->track())
+ {
+ QString title = _sif->track()->name() + ":" + _sif->pluginLabel();
+ setWindowTitle(title);
+ }
+
+ //_sif->editorOpened();
+ if(!isVisible())
+ show();
+ raise();
+ activateWindow();
+ _sif->idleEditor();
+}
+
+#if defined(Q_WS_X11)
+
+//---------------------------------------------------------------------
+// x11EventFilter
+//---------------------------------------------------------------------
+
+bool VstNativeEditor::x11EventFilter(XEvent *pEvent)
+{
+ if(_vstEventProc && pEvent->xany.window == _vstEditor)
+ {
+ // Avoid mouse tracking events...
+ switch (pEvent->xany.type) {
+ case ButtonPress:
+ _buttonPress = true;
+ break;
+ case ButtonRelease:
+ _buttonPress = false;
+ break;
+ case MotionNotify:
+ if(!_buttonPress)
+ return false;
+ // Fall thru...
+ default:
+ break;
+ }
+ // Process as intended...
+ (*_vstEventProc)(pEvent);
+ return true;
+ }
+ else
+ return false;
+}
+
+#endif
+
+//---------------------------------------------------------------------
+// showEvent
+//---------------------------------------------------------------------
+
+void VstNativeEditor::showEvent(QShowEvent *pShowEvent)
+{
+ QWidget::showEvent(pShowEvent);
+
+ if(_sif)
+ _sif->editorOpened();
+}
+
+//---------------------------------------------------------------------
+// closeEvent
+//---------------------------------------------------------------------
+
+void VstNativeEditor::closeEvent(QCloseEvent *pCloseEvent)
+{
+ if(_sif)
+ _sif->editorClosed();
+
+ QWidget::closeEvent(pCloseEvent);
+}
+
+//---------------------------------------------------------------------
+// moveEvent
+//---------------------------------------------------------------------
+
+void VstNativeEditor::moveEvent(QMoveEvent *pMoveEvent)
+{
+ QWidget::moveEvent(pMoveEvent);
+#if defined(Q_WS_X11)
+ if(_vstEditor)
+ {
+ XMoveWindow(_display, _vstEditor, 0, 0);
+ //QWidget::update(); // REMOVE Tim. Or keep? Commented in Qtractor.
+ }
+#endif
+}
+
+} // namespace MusEGui
diff --git a/muse2/muse/widgets/vst_native_editor.h b/muse2/muse/widgets/vst_native_editor.h
new file mode 100644
index 00000000..1f591275
--- /dev/null
+++ b/muse2/muse/widgets/vst_native_editor.h
@@ -0,0 +1,91 @@
+//=========================================================
+// MusE
+// Linux Music Editor
+// vst_native_editor.h
+// (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net)
+// Some of the editor window coding was adapted from QTractor (by rncbc aka Rui Nuno Capela)
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; version 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+//=========================================================
+
+#ifndef __VST_NATIVE_EDITOR_H__
+#define __VST_NATIVE_EDITOR_H__
+
+//#include "vst_native.h"
+//#include <QtGlobal>
+#include <QWidget>
+
+#if defined(Q_WS_X11)
+#include <QX11Info>
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#undef Bool
+#undef Status
+#undef None
+#undef KeyPress
+#undef KeyRelease
+#undef FocusIn
+#undef FocusOut
+#undef Type
+#undef FontChange
+#undef CursorShape
+#undef Unsorted
+typedef void (*XEventProc)(XEvent *);
+#endif
+
+namespace MusECore {
+class VstNativeSynthIF;
+}
+
+namespace MusEGui {
+
+class VstNativeEditor : public QWidget
+{
+ Q_OBJECT
+
+#if defined(Q_WS_X11)
+ Display* _display;
+ Window _vstEditor;
+ XEventProc _vstEventProc;
+ bool _buttonPress;
+#endif
+
+ MusECore::VstNativeSynthIF* _sif;
+
+protected:
+
+ virtual void showEvent(QShowEvent *pShowEvent);
+ virtual void closeEvent(QCloseEvent *pCloseEvent);
+ virtual void moveEvent(QMoveEvent *pMoveEvent);
+
+public:
+ VstNativeEditor(QWidget *parent, Qt::WindowFlags wflags = 0);
+ ~VstNativeEditor();
+
+ void open(MusECore::VstNativeSynthIF* sif);
+ //void close();
+
+#if defined(Q_WS_X11)
+ // Local X11 event filter.
+ bool x11EventFilter(XEvent *pEvent);
+#endif
+
+ //MusECore::VstNativeSynthIF* sif() const { return _sif; }
+};
+
+} // namespace MusEGui
+
+#endif