summaryrefslogtreecommitdiff
path: root/muse2/muse
diff options
context:
space:
mode:
authorTim E. Real <termtech@rogers.com>2011-06-19 01:26:26 +0000
committerTim E. Real <termtech@rogers.com>2011-06-19 01:26:26 +0000
commit02941424387a064301372c12bda3e8c3ab3fee45 (patch)
treee4cdea4400d8a34060a1c4d2b32da8e7ef46ab90 /muse2/muse
parent448d81436ed8c2bc501506c2663a9bdb8c0dd023 (diff)
Major work, all synthesizers. Other fixes. Please see ChangeLog.
Diffstat (limited to 'muse2/muse')
-rw-r--r--muse2/muse/ctrl.cpp3
-rw-r--r--muse2/muse/ctrl/ctrlcanvas.cpp61
-rw-r--r--muse2/muse/ctrl/ctrlpanel.cpp2
-rw-r--r--muse2/muse/driver/jack.cpp3
-rw-r--r--muse2/muse/driver/jackmidi.cpp11
-rw-r--r--muse2/muse/dssihost.cpp246
-rw-r--r--muse2/muse/dssihost.h10
-rw-r--r--muse2/muse/evdata.h2
-rw-r--r--muse2/muse/instruments/minstrument.cpp42
-rw-r--r--muse2/muse/instruments/minstrument.h3
-rw-r--r--muse2/muse/midi.cpp3
-rw-r--r--muse2/muse/midi.h6
-rw-r--r--muse2/muse/midiport.cpp55
-rw-r--r--muse2/muse/node.cpp50
-rw-r--r--muse2/muse/synth.cpp44
-rw-r--r--muse2/muse/synth.h20
-rw-r--r--muse2/muse/ticksynth.cpp4
-rw-r--r--muse2/muse/widgets/mtrackinfo.cpp111
-rw-r--r--muse2/muse/widgets/mtrackinfo.h1
-rw-r--r--muse2/muse/widgets/routepopup.cpp16
20 files changed, 490 insertions, 203 deletions
diff --git a/muse2/muse/ctrl.cpp b/muse2/muse/ctrl.cpp
index 922da337..65a04ba1 100644
--- a/muse2/muse/ctrl.cpp
+++ b/muse2/muse/ctrl.cpp
@@ -264,7 +264,6 @@ void CtrlList::read(Xml& xml)
}
*/
- // Added by Tim. p3.3.6
//printf("CtrlList::read tag:%s\n", tag.toLatin1().constData());
int len = tag.length();
@@ -320,7 +319,6 @@ void CtrlList::read(Xml& xml)
break;
}
- // Added by Tim. p3.3.6
//printf("CtrlList::read i:%d len:%d fs:%s frame %d: vs:%s val %f \n", i, len, fs.toLatin1().constData(), frame, vs.toLatin1().constData(), val);
add(frame, val);
@@ -333,7 +331,6 @@ void CtrlList::read(Xml& xml)
case Xml::TagEnd:
if (xml.s1() == "controller")
{
- // Added by Tim. p3.3.6
//printf("CtrlList::read _id:%d _curVal:%f\n", _id, _curVal);
return;
diff --git a/muse2/muse/ctrl/ctrlcanvas.cpp b/muse2/muse/ctrl/ctrlcanvas.cpp
index f2f335cf..ceffee68 100644
--- a/muse2/muse/ctrl/ctrlcanvas.cpp
+++ b/muse2/muse/ctrl/ctrlcanvas.cpp
@@ -308,7 +308,9 @@ void CtrlCanvas::setPos(int idx, unsigned val, bool adjustScrollbar)
void CtrlCanvas::setMidiController(int num)
{
_cnum = num;
- partControllers(curPart, _cnum, &_dnum, &_didx, &_controller, &ctrl);
+ //if(curPart)
+ partControllers(curPart, _cnum, &_dnum, &_didx, &_controller, &ctrl);
+
if(_panel)
{
if(_cnum == CTRL_VELOCITY)
@@ -474,6 +476,9 @@ void CtrlCanvas::songChanged(int type)
//return;
}
+ if(!curPart) // p4.0.27
+ return;
+
if(type & (SC_CONFIG | SC_DRUMMAP | SC_PART_MODIFIED | SC_EVENT_INSERTED | SC_EVENT_REMOVED | SC_EVENT_MODIFIED)) // p4.0.18
updateItems();
else
@@ -500,6 +505,19 @@ void CtrlCanvas::partControllers(const MidiPart* part, int num, int* dnum, int*
}
else
{
+ if(!part) // p4.0.27
+ {
+ if(mcvl)
+ *mcvl = 0;
+ if(mc)
+ *mc = 0;
+ if(dnum)
+ *dnum = 0;
+ if(didx)
+ *didx = 0;
+ return;
+ }
+
MidiTrack* mt = part->track();
MidiPort* mp;
int di;
@@ -667,9 +685,10 @@ void CtrlCanvas::updateItems()
MidiPart* part = (MidiPart*)(p->second);
EventList* el = part->events();
- MidiController* mc;
+ //MidiController* mc;
MidiCtrlValList* mcvl;
- partControllers(part, _cnum, 0, 0, &mc, &mcvl);
+ //partControllers(part, _cnum, 0, 0, &mc, &mcvl);
+ partControllers(part, _cnum, 0, 0, 0, &mcvl);
unsigned len = part->lenTick();
for (iEvent i = el->begin(); i != el->end(); ++i)
@@ -740,6 +759,9 @@ void CtrlCanvas::updateSelections()
void CtrlCanvas::viewMousePressEvent(QMouseEvent* event)
{
+ if(!_controller) // p4.0.27
+ return;
+
start = event->pos();
Tool activeTool = tool;
@@ -751,9 +773,9 @@ void CtrlCanvas::viewMousePressEvent(QMouseEvent* event)
switch (activeTool) {
case PointerTool:
- drag = DRAG_LASSO_START;
-
+ if(curPart) // p4.0.27
{
+ drag = DRAG_LASSO_START;
bool do_redraw = false;
if (!ctrlKey)
{
@@ -846,6 +868,9 @@ void CtrlCanvas::viewMousePressEvent(QMouseEvent* event)
void CtrlCanvas::viewMouseMoveEvent(QMouseEvent* event)
{
+ if(!_controller) // p4.0.27
+ return;
+
QPoint pos = event->pos();
QPoint dist = pos - start;
bool moving = dist.y() >= 3 || dist.y() <= 3 || dist.x() >= 3 || dist.x() <= 3;
@@ -918,6 +943,7 @@ void CtrlCanvas::viewMouseReleaseEvent(QMouseEvent* event)
lasso.setRect(-1, -1, -1, -1);
case DRAG_LASSO:
+ if(_controller) // p4.0.27
{
///if (!ctrlKey)
/// deselectAll();
@@ -967,6 +993,9 @@ void CtrlCanvas::viewMouseReleaseEvent(QMouseEvent* event)
void CtrlCanvas::newValRamp(int x1, int y1, int x2, int y2)
{
+ if(!curPart || !_controller) // p4.0.27
+ return;
+
if(x2 - x1 < 0)
{
int a = x1;
@@ -1088,6 +1117,9 @@ void CtrlCanvas::newValRamp(int x1, int y1, int x2, int y2)
void CtrlCanvas::changeValRamp(int x1, int y1, int x2, int y2)
{
+ if(!curPart || !_controller) // p4.0.27
+ return;
+
int h = height();
bool changed = false;
int type = _controller->num();
@@ -1178,6 +1210,9 @@ void CtrlCanvas::changeValRamp(int x1, int y1, int x2, int y2)
void CtrlCanvas::changeVal(int x1, int x2, int y)
{
+ if(!curPart || !_controller) // p4.0.27
+ return;
+
bool changed = false;
int newval = computeVal(_controller, y, height());
int type = _controller->num();
@@ -1409,6 +1444,9 @@ void CtrlCanvas::newVal(int x1, int x2, int y)
void CtrlCanvas::newVal(int x1, int y)
{
+ if(!curPart || !_controller) // p4.0.27
+ return;
+
int xx1 = editor->rasterVal1(x1);
int xx2 = editor->rasterVal2(x1);
// If x1 happens to lie directly on a raster, xx1 will equal xx2,
@@ -1620,6 +1658,9 @@ void CtrlCanvas::newVal(int x1, int y)
void CtrlCanvas::newVal(int x1, int y1, int x2, int y2)
{
+ if(!curPart || !_controller) // p4.0.27
+ return;
+
if(x2 - x1 < 0)
{
int a = x1;
@@ -1821,6 +1862,9 @@ void CtrlCanvas::newVal(int x1, int y1, int x2, int y2)
void CtrlCanvas::deleteVal(int x1, int x2, int)
{
+ if(!curPart) // p4.0.27
+ return;
+
if(x2 - x1 < 0)
{
int a = x1;
@@ -1971,6 +2015,9 @@ void CtrlCanvas::pdrawItems(QPainter& p, const QRect& rect, const MidiPart* part
}
else
{
+ if(!part) // p4.0.27
+ return;
+
MidiTrack* mt = part->track();
MidiPort* mp;
@@ -2095,7 +2142,9 @@ void CtrlCanvas::pdrawItems(QPainter& p, const QRect& rect, const MidiPart* part
void CtrlCanvas::pdraw(QPainter& p, const QRect& rect)
{
-
+ if(!_controller) // p4.0.27
+ return;
+
int x = rect.x() - 1; // compensate for 3 pixel line width
int y = rect.y();
int w = rect.width() + 2;
diff --git a/muse2/muse/ctrl/ctrlpanel.cpp b/muse2/muse/ctrl/ctrlpanel.cpp
index b23ce855..b70c4d65 100644
--- a/muse2/muse/ctrl/ctrlpanel.cpp
+++ b/muse2/muse/ctrl/ctrlpanel.cpp
@@ -946,7 +946,7 @@ void CtrlPanel::ctrlRightClicked(const QPoint& p, int /*id*/)
// _knob->selectFaceColor(true);
//if(_dnum == -1)
// return;
- if(!editor->curCanvasPart())
+ if(!editor->curCanvasPart() || !_ctrl)
return;
int cdi = editor->curDrumInstrument();
diff --git a/muse2/muse/driver/jack.cpp b/muse2/muse/driver/jack.cpp
index c4d7a8ca..4857ede5 100644
--- a/muse2/muse/driver/jack.cpp
+++ b/muse2/muse/driver/jack.cpp
@@ -75,7 +75,8 @@ inline bool checkJackClient(jack_client_t* _client)
bool checkAudioDevice()
{
if (audioDevice == NULL) {
- printf("Muse:checkAudioDevice: no audioDevice\n");
+ if(debugMsg)
+ printf("Muse:checkAudioDevice: no audioDevice\n");
return false;
}
return true;
diff --git a/muse2/muse/driver/jackmidi.cpp b/muse2/muse/driver/jackmidi.cpp
index 1765fabb..e514af7a 100644
--- a/muse2/muse/driver/jackmidi.cpp
+++ b/muse2/muse/driver/jackmidi.cpp
@@ -1061,7 +1061,8 @@ void MidiJackDevice::eventReceived(jack_midi_event_t* ev)
// For now, do not accept if the last byte is not EOX, meaning it's a chunk with more chunks to follow.
if(*(((unsigned char*)ev->buffer) + ev->size - 1) != ME_SYSEX_END)
{
- printf("MidiJackDevice::eventReceived sysex chunks not supported!\n");
+ if(debugMsg)
+ printf("MidiJackDevice::eventReceived sysex chunks not supported!\n");
return;
}
@@ -1092,15 +1093,17 @@ void MidiJackDevice::eventReceived(jack_midi_event_t* ev)
//break;
// return;
default:
- printf("MidiJackDevice::eventReceived unsupported system event 0x%02x\n", type);
+ if(debugMsg)
+ printf("MidiJackDevice::eventReceived unsupported system event 0x%02x\n", type);
return;
}
}
//return;
break;
default:
- printf("MidiJackDevice::eventReceived unknown event 0x%02x\n", type);
- //printf("MidiJackDevice::eventReceived unknown event 0x%02x size:%d buf:0x%02x 0x%02x 0x%02x ...0x%02x\n", type, ev->size, *(ev->buffer), *(ev->buffer + 1), *(ev->buffer + 2), *(ev->buffer + (ev->size - 1)));
+ if(debugMsg)
+ printf("MidiJackDevice::eventReceived unknown event 0x%02x\n", type);
+ //printf("MidiJackDevice::eventReceived unknown event 0x%02x size:%d buf:0x%02x 0x%02x 0x%02x ...0x%02x\n", type, ev->size, *(ev->buffer), *(ev->buffer + 1), *(ev->buffer + 2), *(ev->buffer + (ev->size - 1)));
return;
}
diff --git a/muse2/muse/dssihost.cpp b/muse2/muse/dssihost.cpp
index 2384ed02..fd33a135 100644
--- a/muse2/muse/dssihost.cpp
+++ b/muse2/muse/dssihost.cpp
@@ -24,8 +24,8 @@
// Turn on debugging messages
//#define DSSI_DEBUG
-// Support vst state saving/loading with vst chunks. Requires patches to DSSI and DSSI-vst!
-//#define DSSI_VST_CHUNK_SUPPORT
+// Support vst state saving/loading with vst chunks.
+//#define DSSI_VST_CHUNK_SUPPORT
#include <string.h>
#include <signal.h>
@@ -1350,6 +1350,13 @@ DssiSynthIF::~DssiSynthIF()
delete[] controlsOut;
}
+int DssiSynthIF::oldMidiStateHeader(const unsigned char** data) const
+{
+ unsigned char const d[2] = {MUSE_SYNTH_SYSEX_MFG_ID, DSSI_SYNTH_UNIQUE_ID};
+ *data = &d[0];
+ return 2;
+}
+
//---------------------------------------------------------
// getParameter
//---------------------------------------------------------
@@ -1433,38 +1440,45 @@ void DssiSynthIF::write(int level, Xml& xml) const
//bool vstsaved = false;
#ifdef DSSI_VST_CHUNK_SUPPORT
- //---------------------------------------------
- // dump current state of synth
- //---------------------------------------------
- printf("dumping DSSI custom data! %d\n", synth->dssi->getCustomData);
-
- // this is only needed and supported if
- // we are talking to a VST plugin at the other end.
- std::string name = synth->dssi->LADSPA_Plugin->Name;
- if ((name.length()> 4) && name.substr(name.length() - 4) == " VST")
- {
- printf("is vst plugin, commencing data dump, apiversion=%d!\n", synth->dssi->DSSI_API_Version);
- unsigned long len = 0;
- void* p = 0;
- synth->dssi->getCustomData(handle,&p, &len);
- if (len) {
- xml.tag(level++, "midistate");
- xml.nput(level++, "<event type=\"%d\"", Sysex);
- xml.nput(" datalen=\"%d\">\n", len+7 /*VSTSAVE*/);
- xml.nput(level, "");
- xml.nput("56 53 54 53 41 56 45 "); // embed a save marker "string 'VSTSAVE'
- for (long unsigned int i = 0; i < len; ++i) {
- if (i && (((i+7) % 16) == 0)) {
- xml.nput("\n");
- xml.nput(level, "");
- }
- xml.nput("%02x ", ((char*)(p))[i] & 0xff);
- }
- xml.nput("\n");
- xml.tag(level--, "/event");
- xml.etag(level--, "midistate");
- //vstsaved = true;
- }
+ if(synth->dssi->getCustomData)
+ {
+ //---------------------------------------------
+ // dump current state of synth
+ //---------------------------------------------
+ printf("dumping DSSI custom data! %p\n", synth->dssi->getCustomData);
+
+ // this is only needed and supported if
+ // we are talking to a VST plugin at the other end.
+ std::string name = synth->dssi->LADSPA_Plugin->Name;
+ if ((name.length()> 4) && name.substr(name.length() - 4) == " VST")
+ {
+ printf("is vst plugin, commencing data dump, apiversion=%d!\n", synth->dssi->DSSI_API_Version);
+ unsigned long len = 0;
+ void* p = 0;
+ synth->dssi->getCustomData(handle,&p, &len);
+ if (len) {
+ //xml.tag(level++, "midistate");
+ xml.tag(level++, "midistate version=\"%d\"", SYNTH_MIDI_STATE_SAVE_VERSION); // p4.0.27
+ xml.nput(level++, "<event type=\"%d\"", Sysex);
+ //xml.nput(" datalen=\"%d\">\n", len+7 /*VSTSAVE*/);
+ xml.nput(" datalen=\"%d\">\n", len+9 /* 2 bytes header + "VSTSAVE" */);
+ xml.nput(level, "");
+ xml.nput("%02x %02x ", (char)MUSE_SYNTH_SYSEX_MFG_ID, (char)DSSI_SYNTH_UNIQUE_ID); // p4.0.27 Wrap in a proper header
+ xml.nput("56 53 54 53 41 56 45 "); // embed a save marker "string 'VSTSAVE'
+ for (long unsigned int i = 0; i < len; ++i) {
+ //if (i && (((i+7) % 16) == 0)) {
+ if (i && (((i+9) % 16) == 0)) {
+ xml.nput("\n");
+ xml.nput(level, "");
+ }
+ xml.nput("%02x ", ((char*)(p))[i] & 0xff);
+ }
+ xml.nput("\n");
+ xml.tag(level--, "/event");
+ xml.etag(level--, "midistate");
+ //vstsaved = true;
+ }
+ }
}
#else
printf("support for vst chunks not compiled in!\n");
@@ -1922,88 +1936,110 @@ bool DssiSynthIF::processEvent(const MidiPlayEvent& e, snd_seq_event_t* event)
event->queue = SND_SEQ_QUEUE_DIRECT;
snd_seq_ev_set_chanpress(event, chn, a);
break;
- case ME_SYSEX:
- #ifdef DSSI_DEBUG
- fprintf(stderr, "DssiSynthIF::processEvent midi event is ME_SYSEX\n");
- #endif
-
- if (QString((const char*)e.data()).startsWith("VSTSAVE")) {
-#ifdef DSSI_VST_CHUNK_SUPPORT
- printf("loading chunk from sysex %s!\n", e.data()+7);
- dssi->setCustomData(handle, e.data()+7 /* len of str*/,e.len()-7);
-#else
- printf("support for vst chunks not compiled in!\n");
-#endif
- // Event not filled.
- return false;
- }
- /*
- // p3.3.39 Read the state of current bank and program and all input control values.
- // TODO: Needs to be better. See write().
- else
- if (QString((const char*)e.data()).startsWith("PARAMSAVE"))
+ case ME_SYSEX:
{
#ifdef DSSI_DEBUG
- fprintf(stderr, "DssiSynthIF::processEvent midi event is ME_SYSEX PARAMSAVE\n");
+ fprintf(stderr, "DssiSynthIF::processEvent midi event is ME_SYSEX\n");
#endif
- unsigned long dlen = e.len() - 9; // Minus "PARAMSAVE"
- if(dlen > 0)
+ // Changed p4.0.27
+ const unsigned char* data = e.data();
+ if(e.len() >= 2)
{
- //if(dlen < 2 * sizeof(unsigned long))
- if(dlen < (2 + 2 * sizeof(unsigned long))) // Version major and minor bytes, bank and program.
- printf("DssiSynthIF::processEvent Error: PARAMSAVE data length does not include at least version major and minor, bank and program!\n");
- else
+ if(data[0] == MUSE_SYNTH_SYSEX_MFG_ID)
{
- // 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(data[1] == DSSI_SYNTH_UNIQUE_ID)
{
- if((dlen % sizeof(float)) != 0)
- printf("DssiSynthIF::processEvent Error: PARAMSAVE float data length not integral multiple of float size!\n");
- else
+ if(e.len() >= 9)
{
- const unsigned long n = dlen / sizeof(float);
- if(n != synth->_controlInPorts)
- printf("DssiSynthIF::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)
+ //if (QString((const char*)e.data()).startsWith("VSTSAVE")) {
+ if (QString((const char*)(data + 2)).startsWith("VSTSAVE")) {
+#ifdef DSSI_VST_CHUNK_SUPPORT
+ if(dssi->setCustomData)
+ {
+ //printf("loading chunk from sysex %s!\n", e.data()+7);
+ printf("loading chunk from sysex %s!\n", data+9);
+ //dssi->setCustomData(handle, e.data()+7 /* len of str*/,e.len()-7);
+ dssi->setCustomData(handle, (unsigned char*)(data+9) /* len of str*/,e.len()-9);
+ }
+#else
+ printf("support for vst chunks not compiled in!\n");
+#endif
+ // Event not filled.
+ return false;
+ }
+ }
+ }
+ }
+ }
+ /*
+ // p3.3.39 Read the state of current bank and program and all input control values.
+ // TODO: Needs to be better. See write().
+ //else
+ if (QString((const char*)e.data()).startsWith("PARAMSAVE"))
+ {
+ #ifdef DSSI_DEBUG
+ fprintf(stderr, "DssiSynthIF::processEvent midi event is ME_SYSEX PARAMSAVE\n");
+ #endif
+
+ unsigned long dlen = e.len() - 9; // Minus "PARAMSAVE"
+ if(dlen > 0)
+ {
+ //if(dlen < 2 * sizeof(unsigned long))
+ if(dlen < (2 + 2 * sizeof(unsigned long))) // Version major and minor bytes, bank and program.
+ printf("DssiSynthIF::processEvent Error: PARAMSAVE data length does not include at least version major and minor, bank and program!\n");
+ else
+ {
+ // Not required, yet.
+ //char vmaj = *((char*)(e.data() + 9)); // After "PARAMSAVE"
+ //char vmin = *((char*)(e.data() + 10));
+
+ unsigned long* const ulp = (unsigned long*)(e.data() + 11); // After "PARAMSAVE" + version major and minor.
+ // TODO: TODO: Set plugin bank and program.
+ _curBank = ulp[0];
+ _curProgram = ulp[1];
+
+ dlen -= (2 + 2 * sizeof(unsigned long)); // After the version major and minor, bank and program.
+
+ if(dlen > 0)
+ {
+ if((dlen % sizeof(float)) != 0)
+ printf("DssiSynthIF::processEvent Error: PARAMSAVE float data length not integral multiple of float size!\n");
+ else
{
- const float v = fp[i];
- controls[i].val = v;
+ const unsigned long n = dlen / sizeof(float);
+ if(n != synth->_controlInPorts)
+ printf("DssiSynthIF::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
- {
- // NOTE: There is a limit on the size of a sysex. Got this:
- // "DssiSynthIF::processEvent midi event is ME_SYSEX"
- // "WARNING: MIDI event of type ? decoded to 367 bytes, discarding"
- // That might be ALSA doing that.
- snd_seq_ev_clear(event);
- event->queue = SND_SEQ_QUEUE_DIRECT;
- snd_seq_ev_set_sysex(event, len,
- //(unsigned char*)ba.data());
- (unsigned char*)ca);
- }
+ // Event not filled.
+ return false;
+ }
+ */
+ //else
+ {
+ // NOTE: There is a limit on the size of a sysex. Got this:
+ // "DssiSynthIF::processEvent midi event is ME_SYSEX"
+ // "WARNING: MIDI event of type ? decoded to 367 bytes, discarding"
+ // That might be ALSA doing that.
+ snd_seq_ev_clear(event);
+ event->queue = SND_SEQ_QUEUE_DIRECT;
+ snd_seq_ev_set_sysex(event, len,
+ //(unsigned char*)ba.data());
+ (unsigned char*)ca);
+ }
+ }
break;
default:
if(debugMsg)
@@ -3251,7 +3287,7 @@ int DssiSynthIF::oscMidi(int a, int b, int c)
MidiPlayEvent event(0, port, channel, a, b, c);
#ifdef DSSI_DEBUG
- fprintf(stderr, "DssiSynthIF::oscMidi midi event chn:%d a:%d b:%d\n", event.channel(), event.dataA(), event.dataB());
+ printf(stderr, "DssiSynthIF::oscMidi midi event chn:%d a:%d b:%d\n", event.channel(), event.dataA(), event.dataB());
#endif
midiPorts[port].sendEvent(event);
diff --git a/muse2/muse/dssihost.h b/muse2/muse/dssihost.h
index b917bbf6..d46cb570 100644
--- a/muse2/muse/dssihost.h
+++ b/muse2/muse/dssihost.h
@@ -27,6 +27,11 @@
#include <map>
#include <string>
+// Make sure this number is unique among all the MESS synths and DSSI host synth.
+#define DSSI_SYNTH_UNIQUE_ID 8
+// Midistate sysex initialization command.
+#define DSSI_INIT_DATA_CMD 1
+
#ifdef OSC_SUPPORT
#include <lo/lo.h>
#include "osc.h"
@@ -150,11 +155,14 @@ class DssiSynthIF : public SynthIF, public PluginIBase
protected:
//int guiPid;
//QProcess* guiQProc;
-
+
public:
DssiSynthIF(SynthI* s);
//DssiSynthIF();
+ // This is only a kludge required to support old songs' midistates. Do not use in any new synth.
+ virtual int oldMidiStateHeader(const unsigned char** data) const;
+
virtual ~DssiSynthIF();
virtual DssiSynth* dssiSynth() { return synth; }
diff --git a/muse2/muse/evdata.h b/muse2/muse/evdata.h
index 59155d48..b8c2bde4 100644
--- a/muse2/muse/evdata.h
+++ b/muse2/muse/evdata.h
@@ -57,6 +57,8 @@ class EvData {
}
}
void setData(const unsigned char* p, int l) {
+ if(data)
+ delete[] data; // p4.0.27
data = new unsigned char[l];
memcpy(data, p, l);
dataLen = l;
diff --git a/muse2/muse/instruments/minstrument.cpp b/muse2/muse/instruments/minstrument.cpp
index 10cb3ec2..613e5bdf 100644
--- a/muse2/muse/instruments/minstrument.cpp
+++ b/muse2/muse/instruments/minstrument.cpp
@@ -333,6 +333,7 @@ void removeMidiInstrument(const MidiInstrument* instr)
void MidiInstrument::init()
{
+ _tmpMidiStateVersion = 1; // Assume old version. readMidiState will overwrite anyway.
_nullvalue = -1;
_initScript = 0;
_midiInit = new EventList();
@@ -675,9 +676,44 @@ void Patch::write(int level, Xml& xml)
//---------------------------------------------------------
void MidiInstrument::readMidiState(Xml& xml)
- {
- _midiState->read(xml, "midistate", true);
- }
+{
+ ///_midiState->read(xml, "midistate", true);
+
+ // p4.0.27 A kludge to support old midistates by wrapping them in the proper header.
+ _tmpMidiStateVersion = 1; // Assume old (unmarked) first version 1.
+ for (;;)
+ {
+ Xml::Token token = xml.parse();
+ const QString tag = xml.s1();
+ switch (token)
+ {
+ case Xml::Error:
+ case Xml::End:
+ return;
+ case Xml::TagStart:
+ if (tag == "event")
+ {
+ Event e(Note);
+ e.read(xml);
+ _midiState->add(e);
+ }
+ else
+ xml.unknown("midistate");
+ break;
+ case Xml::Attribut:
+ if(tag == "version")
+ _tmpMidiStateVersion = xml.s2().toInt();
+ else
+ xml.unknown("MidiInstrument");
+ break;
+ case Xml::TagEnd:
+ if(tag == "midistate")
+ return;
+ default:
+ break;
+ }
+ }
+}
//---------------------------------------------------------
// read
diff --git a/muse2/muse/instruments/minstrument.h b/muse2/muse/instruments/minstrument.h
index 15942537..dacd17fc 100644
--- a/muse2/muse/instruments/minstrument.h
+++ b/muse2/muse/instruments/minstrument.h
@@ -77,6 +77,9 @@ class MidiInstrument {
EventList* _midiInit;
EventList* _midiReset;
EventList* _midiState;
+ // Set when loading midi state in SynthI::read, to indicate version
+ // to SynthI::initInstance, which is called later.
+ int _tmpMidiStateVersion;
char* _initScript;
QString _name;
QString _filePath;
diff --git a/muse2/muse/midi.cpp b/muse2/muse/midi.cpp
index 39ae7874..6abcaa81 100644
--- a/muse2/muse/midi.cpp
+++ b/muse2/muse/midi.cpp
@@ -150,7 +150,8 @@ QString nameSysex(unsigned int len, const unsigned char* buf)
case 0x43: s = "Yamaha: "; break;
case 0x44: s = "Casio"; break;
case 0x45: s = "Akai"; break;
- case 0x7c: s = "MusE Soft Synth"; break;
+ //case 0x7c: s = "MusE Soft Synth"; break;
+ case MUSE_SYNTH_SYSEX_MFG_ID: s = "MusE Soft Synth"; break; // p4.0.27
case 0x7d: s = "Educational Use"; break;
case 0x7e: s = "Universal: Non Real Time"; break;
case 0x7f: s = "Universal: Real Time"; break;
diff --git a/muse2/muse/midi.h b/muse2/muse/midi.h
index ab649377..8250a0e3 100644
--- a/muse2/muse/midi.h
+++ b/muse2/muse/midi.h
@@ -62,6 +62,12 @@ extern const unsigned int mmcLocateMsgLen;
QString nameSysex(unsigned int len, const unsigned char* buf);
QString midiMetaName(int);
+// Use these in all the synths and their guis.
+// Did this here for ease, since they all include this file.
+//
+// A special MusE soft synth sysex manufacturer ID.
+#define MUSE_SYNTH_SYSEX_MFG_ID 0x7c
+
class EventList;
class MPEventList;
class MidiTrack;
diff --git a/muse2/muse/midiport.cpp b/muse2/muse/midiport.cpp
index 7c4f73fb..553c8e31 100644
--- a/muse2/muse/midiport.cpp
+++ b/muse2/muse/midiport.cpp
@@ -334,6 +334,56 @@ const QString& MidiPort::portname() const
void MidiPort::tryCtrlInitVal(int chan, int ctl, int val)
{
+ // p4.0.27
+ // Look for an initial value in the song for this midi controller, on this midi channel...
+ //for(iMidiCtrlValList i = _controller->begin(); i != _controller->end(); ++i)
+ iMidiCtrlValList i = _controller->find(chan, ctl);
+ if(i != _controller->end())
+ {
+ //int channel = i->first >> 24;
+ //int cntrl = i->first & 0xffffff;
+ //if(channel == chan && cntrl == ctl)
+ int v = i->second->value(0); // Value at tick 0.
+ if(v != CTRL_VAL_UNKNOWN)
+ {
+ if(_device)
+ {
+ ///#ifdef DSSI_SUPPORT
+
+ // Not for dssi synths...
+ ///if(!_device->isSynti() || (dynamic_cast<DssiSynthIF*>(((SynthI*)_device)->sif()) == 0))
+ ///{
+
+ ///#endif
+
+ //_device->putEvent(MidiPlayEvent(0, portno(), channel,
+ // ME_CONTROLLER, cntrl, v));
+ // Retry added. Use default attempts and delay. p4.0.15
+ _device->putEventWithRetry(MidiPlayEvent(0, portno(), chan,
+ ME_CONTROLLER, ctl, v));
+ //if(_device->putEventWithRetry(MidiPlayEvent(0, portno(), chan,
+ // ME_CONTROLLER, ctl, v)))
+ // return;
+
+ ///#ifdef DSSI_SUPPORT
+
+ ///}
+
+ ///#endif
+
+ }
+
+ // Set it once so the 'last HW value' is set, and control knobs are positioned at the value...
+ setHwCtrlState(chan, ctl, v);
+ // Set it again so that control labels show 'off'...
+ //setHwCtrlState(chan, ctl, CTRL_VAL_UNKNOWN);
+ //setHwCtrlStates(chan, ctl, CTRL_VAL_UNKNOWN, v);
+
+ return;
+ }
+ }
+
+ // No initial value was found in the song for this midi controller on this midi channel. Try the instrument...
if(_instrument)
{
MidiControllerList* cl = _instrument->controller();
@@ -370,6 +420,7 @@ void MidiPort::tryCtrlInitVal(int chan, int ctl, int val)
}
}
+ // No initial value was found in the song or instrument for this midi controller. Just send the given value.
if(_device)
{
//MidiPlayEvent ev(song->cpos(), portno(), chan, ME_CONTROLLER, ctl, val);
@@ -749,8 +800,8 @@ bool MidiPort::sendEvent(const MidiPlayEvent& ev, bool forceSend)
// }
// printf("set HW Ctrl State ch:%d 0x%x 0x%x\n", ev.channel(), ev.dataA(), ev.dataB());
if(!setHwCtrlState(ev.channel(), da, db)) {
- if (debugMsg)
- printf("setHwCtrlState failed\n");
+ if (debugMsg && forceSend)
+ printf("sendEvent: State already set. Forcing anyway...\n");
if (!forceSend)
return false;
}
diff --git a/muse2/muse/node.cpp b/muse2/muse/node.cpp
index 06dbbc8d..da3874f0 100644
--- a/muse2/muse/node.cpp
+++ b/muse2/muse/node.cpp
@@ -1889,35 +1889,43 @@ void AudioTrack::setChannels(int n)
void AudioTrack::setTotalOutChannels(int num)
{
- if(num == _totalOutChannels)
- return;
-
+ //if(num == _totalOutChannels)
+ // return;
+ // p4.0.27 Fixes crash if file loaded with track channels less than synth channels.
int chans = _totalOutChannels;
- // Number of allocated buffers is always MAX_CHANNELS or more, even if _totalOutChannels is less.
- if(chans < MAX_CHANNELS)
- chans = MAX_CHANNELS;
- for(int i = 0; i < chans; ++i)
+ if(num != chans)
{
- if(outBuffers[i])
- free(outBuffers[i]);
- }
- delete[] outBuffers;
- _totalOutChannels = num;
- chans = num;
- // Number of allocated buffers is always MAX_CHANNELS or more, even if _totalOutChannels is less.
- if(chans < MAX_CHANNELS)
- chans = MAX_CHANNELS;
+ //int chans = _totalOutChannels;
+ // Number of allocated buffers is always MAX_CHANNELS or more, even if _totalOutChannels is less.
+ if(chans < MAX_CHANNELS)
+ chans = MAX_CHANNELS;
+ for(int i = 0; i < chans; ++i)
+ {
+ if(outBuffers[i])
+ free(outBuffers[i]);
+ }
+ delete[] outBuffers;
- outBuffers = new float*[chans];
- for (int i = 0; i < chans; ++i)
- posix_memalign((void**)&outBuffers[i], 16, sizeof(float) * segmentSize);
-
+ _totalOutChannels = num;
+ chans = num;
+ // Number of allocated buffers is always MAX_CHANNELS or more, even if _totalOutChannels is less.
+ if(chans < MAX_CHANNELS)
+ chans = MAX_CHANNELS;
+
+ outBuffers = new float*[chans];
+ for (int i = 0; i < chans; ++i)
+ posix_memalign((void**)&outBuffers[i], 16, sizeof(float) * segmentSize);
+
+ //chans = num;
+ // Limit the actual track (meters, copying etc, all 'normal' operation) to two-channel stereo.
+ //if(chans > MAX_CHANNELS)
+ // chans = MAX_CHANNELS;
+ }
chans = num;
// Limit the actual track (meters, copying etc, all 'normal' operation) to two-channel stereo.
if(chans > MAX_CHANNELS)
chans = MAX_CHANNELS;
-
setChannels(chans);
}
diff --git a/muse2/muse/synth.cpp b/muse2/muse/synth.cpp
index 4f43a02a..f8946a9f 100644
--- a/muse2/muse/synth.cpp
+++ b/muse2/muse/synth.cpp
@@ -36,6 +36,7 @@
#include "midictrl.h"
//#include "stringparam.h"
#include "popupmenu.h"
+#include "globaldefs.h"
std::vector<Synth*> synthis; // array of available synthis
@@ -429,10 +430,34 @@ bool SynthI::initInstance(Synth* s, const QString& instanceName)
cl->add(c);
}
+ // Restore the midi state...
EventList* iel = midiState();
if (!iel->empty()) {
for (iEvent i = iel->begin(); i != iel->end(); ++i) {
Event ev = i->second;
+
+ // p4.0.27 A kludge to support old midistates by wrapping them in the proper header.
+ if(ev.type() == Sysex && _tmpMidiStateVersion < SYNTH_MIDI_STATE_SAVE_VERSION)
+ {
+ int len = ev.dataLen();
+ if(len > 0)
+ {
+ const unsigned char* data = ev.data();
+ const unsigned char* hdr;
+ // Get the unique header for the synth.
+ int hdrsz = _sif->oldMidiStateHeader(&hdr);
+ if(hdrsz > 0)
+ {
+ int newlen = hdrsz + len;
+ unsigned char* d = new unsigned char[newlen];
+ memcpy(d, hdr, hdrsz);
+ memcpy(d + hdrsz, data, len);
+ ev.setData(d, newlen);
+ delete[] d;
+ }
+ }
+ }
+
MidiPlayEvent pev(0, 0, 0, ev);
if (_sif->putEvent(pev))
break; // try later
@@ -702,7 +727,8 @@ void MessSynthIF::write(int level, Xml& xml) const
const unsigned char* p;
_mess->getInitData(&len, &p);
if (len) {
- xml.tag(level++, "midistate");
+ ///xml.tag(level++, "midistate");
+ xml.tag(level++, "midistate version=\"%d\"", SYNTH_MIDI_STATE_SAVE_VERSION);
xml.nput(level++, "<event type=\"%d\"", Sysex);
xml.nput(" datalen=\"%d\">\n", len);
xml.nput(level, "");
@@ -816,6 +842,7 @@ void SynthI::read(Xml& xml)
if (initInstance(s, name()))
return;
song->insertTrack0(this, -1);
+
if (port != -1 && port < MIDI_PORTS)
midiPorts[port].setMidiDevice(this);
@@ -1033,3 +1060,18 @@ bool MessSynthIF::putEvent(const MidiPlayEvent& ev)
return true;
}
+//unsigned long MessSynthIF::uniqueID() const
+//{
+// return _mess ? _mess->uniqueID() : 0;
+//}
+
+//MidiPlayEvent& MessSynthIF::wrapOldMidiStateVersion(MidiPlayEvent& e) const
+//{
+// return _mess ? _mess->wrapOldMidiStateVersion(e) : e;
+//}
+
+int MessSynthIF::oldMidiStateHeader(const unsigned char** data) const
+{
+ return _mess ? _mess->oldMidiStateHeader(data) : 0;
+}
+
diff --git a/muse2/muse/synth.h b/muse2/muse/synth.h
index 88fa70b8..0c48a231 100644
--- a/muse2/muse/synth.h
+++ b/muse2/muse/synth.h
@@ -22,6 +22,9 @@
#include <QFileInfo>
+// Current version of saved midistate data.
+#define SYNTH_MIDI_STATE_SAVE_VERSION 2
+
//class QMenu;
class PopupMenu;
@@ -105,17 +108,25 @@ class Mess;
//---------------------------------------------------------
// SynthIF
// synth instance interface
+// NOTICE: If implementing sysex support, be sure to make a unique ID and use
+// it to filter out unrecognized sysexes. Headers should be constructed as:
+// MUSE_SYNTH_SYSEX_MFG_ID The MusE SoftSynth Manufacturer ID byte (0x7C) found in midi.h
+// 0xNN The synth's unique ID byte
//---------------------------------------------------------
class SynthIF {
+
protected:
SynthI* synti;
-
+
public:
//SynthIF() {}
SynthIF(SynthI* s) { synti = s; }
virtual ~SynthIF() {}
+ // This is only a kludge required to support old songs' midistates. Do not use in any new synth.
+ virtual int oldMidiStateHeader(const unsigned char** /*data*/) const { return 0; }
+
virtual bool initGui() = 0;
virtual void guiHeartBeat() = 0;
virtual bool guiVisible() const = 0;
@@ -280,6 +291,10 @@ class SynthI : public AudioTrack, public MidiDevice,
//---------------------------------------------------------
// MessSynthIF
// mess synthesizer instance
+// NOTICE: If implementing sysex support, be sure to make a unique ID and use
+// it to filter out unrecognized sysexes. Headers should be constructed as:
+// MUSE_SYNTH_SYSEX_MFG_ID The MusE SoftSynth Manufacturer ID byte (0x7C) found in midi.h
+// 0xNN The synth's unique ID byte
//---------------------------------------------------------
class MessSynthIF : public SynthIF {
@@ -290,6 +305,9 @@ class MessSynthIF : public SynthIF {
MessSynthIF(SynthI* s) : SynthIF(s) { _mess = 0; }
virtual ~MessSynthIF() { }
+ // This is only a kludge required to support old songs' midistates. Do not use in any new synth.
+ virtual int oldMidiStateHeader(const unsigned char** data) const;
+
virtual bool initGui() { return true; }
virtual void guiHeartBeat() { }
virtual bool guiVisible() const { return false; }
diff --git a/muse2/muse/ticksynth.cpp b/muse2/muse/ticksynth.cpp
index 7456b856..90e01417 100644
--- a/muse2/muse/ticksynth.cpp
+++ b/muse2/muse/ticksynth.cpp
@@ -12,7 +12,9 @@
//#include <QMenu>
#include "popupmenu.h"
-// Added by Tim. p3.3.18
+// If sysex support is ever added, make sure this number is unique among all the MESS synths.
+//#define METRONOME_UNIQUE_ID 7
+
//#define METRONOME_DEBUG
MetronomeSynthI* metronome = 0;
diff --git a/muse2/muse/widgets/mtrackinfo.cpp b/muse2/muse/widgets/mtrackinfo.cpp
index 6dd02931..c5cdac0b 100644
--- a/muse2/muse/widgets/mtrackinfo.cpp
+++ b/muse2/muse/widgets/mtrackinfo.cpp
@@ -65,6 +65,7 @@ MidiTrackInfo::MidiTrackInfo(QWidget* parent, Track* sel_track) : QWidget(parent
{
setupUi(this);
_midiDetect = false;
+ heartBeatCounter = 0;
selected = sel_track;
@@ -327,54 +328,72 @@ void MidiTrackInfo::heartBeat()
}
}
else
- if(program != nprogram)
{
- program = nprogram;
-
- //int hb, lb, pr;
- //if (program == CTRL_VAL_UNKNOWN) {
- // hb = lb = pr = 0;
- // iPatch->setText("---");
- // }
- //else
- //{
- MidiInstrument* instr = mp->instrument();
- QString name = instr->getPatchName(outChannel, program, song->mtype(), track->type() == Track::DRUM);
- if(iPatch->text() != name)
- iPatch->setText(name);
-
- int hb = ((program >> 16) & 0xff) + 1;
- if (hb == 0x100)
- hb = 0;
- int lb = ((program >> 8) & 0xff) + 1;
- if (lb == 0x100)
- lb = 0;
- int pr = (program & 0xff) + 1;
- if (pr == 0x100)
- pr = 0;
- //}
-
- //printf("Arranger::midiTrackInfoHeartBeat setting program\n");
-
- if(iHBank->value() != hb)
- {
- iHBank->blockSignals(true);
- iHBank->setValue(hb);
- iHBank->blockSignals(false);
- }
- if(iLBank->value() != lb)
- {
- iLBank->blockSignals(true);
- iLBank->setValue(lb);
- iLBank->blockSignals(false);
- }
- if(iProgram->value() != pr)
- {
- iProgram->blockSignals(true);
- iProgram->setValue(pr);
- iProgram->blockSignals(false);
- }
+ // p4.0.27 The optimizing below, to avoid repeatedly calling getPatchName, generally worked OK.
+ // But Fluidsynth revealed a flaw. When loading a song, updateTrackInfo is called which correctly
+ // sets program = nprogram. But a synth will not receive midistate sysexes until later.
+ // With Fluidsynth, that messed up our optimizing because the soundfont has not loaded yet.
+ // fluid_synth_get_channel_preset returns 0 in FluidSynth::getPatchName which returns <unknown>
+ // when asked by updateTrackInfo, which then sets the patch box text to <unknown>. Then nothing
+ // happens here because program = nprogram.
+ // I don't like the idea of calling getPatchName here at a high rate.
+ // So force an update of program at a slower rate here.
+ //
+ // The alternative is to have a system where the synth can signal the host when a change has happened.
+ // Or an 'isValidPatch' function, or make getPatchName (and several others) return 0, so that updateTrackInfo
+ // can ignore it. Oops. No! Even if we make updateTrackInfo ignore it, then the same thing happens here.
+ // Thats is, program = nprogram but the text is still wrong. Not much choice but to do this for now...
+ if(++heartBeatCounter >= 20)
+ heartBeatCounter = 0;
+ if(program != nprogram || heartBeatCounter == 0)
+ {
+ program = nprogram;
+
+ //int hb, lb, pr;
+ //if (program == CTRL_VAL_UNKNOWN) {
+ // hb = lb = pr = 0;
+ // iPatch->setText("---");
+ // }
+ //else
+ //{
+ MidiInstrument* instr = mp->instrument();
+ QString name = instr->getPatchName(outChannel, program, song->mtype(), track->type() == Track::DRUM);
+ if(iPatch->text() != name)
+ iPatch->setText(name);
+
+ int hb = ((program >> 16) & 0xff) + 1;
+ if (hb == 0x100)
+ hb = 0;
+ int lb = ((program >> 8) & 0xff) + 1;
+ if (lb == 0x100)
+ lb = 0;
+ int pr = (program & 0xff) + 1;
+ if (pr == 0x100)
+ pr = 0;
+ //}
+
+ //printf("Arranger::midiTrackInfoHeartBeat setting program\n");
+ if(iHBank->value() != hb)
+ {
+ iHBank->blockSignals(true);
+ iHBank->setValue(hb);
+ iHBank->blockSignals(false);
+ }
+ if(iLBank->value() != lb)
+ {
+ iLBank->blockSignals(true);
+ iLBank->setValue(lb);
+ iLBank->blockSignals(false);
+ }
+ if(iProgram->value() != pr)
+ {
+ iProgram->blockSignals(true);
+ iProgram->setValue(pr);
+ iProgram->blockSignals(false);
+ }
+
+ }
}
MidiController* mc = mp->midiController(CTRL_VOLUME);
diff --git a/muse2/muse/widgets/mtrackinfo.h b/muse2/muse/widgets/mtrackinfo.h
index ed229ad6..20fd3a69 100644
--- a/muse2/muse/widgets/mtrackinfo.h
+++ b/muse2/muse/widgets/mtrackinfo.h
@@ -21,6 +21,7 @@ class MidiTrackInfo : public QWidget, public Ui::MidiTrackInfoBase
Track* selected;
bool _midiDetect;
int program, pan, volume;
+ int heartBeatCounter;
private slots:
void iOutputChannelChanged(int);
diff --git a/muse2/muse/widgets/routepopup.cpp b/muse2/muse/widgets/routepopup.cpp
index 910d693d..0afbbdb6 100644
--- a/muse2/muse/widgets/routepopup.cpp
+++ b/muse2/muse/widgets/routepopup.cpp
@@ -848,7 +848,7 @@ void RoutePopupMenu::popupActivated(QAction* action)
//if(!(md->rwFlags() & 2))
//if(!(md->rwFlags() & (gIsOutRoutingPopupMenu ? 1 : 2)))
if(md && !(md->rwFlags() & (_isOutMenu ? 1 : 2)))
- return;
+ return;
int chmask = 0;
ciRoute iir = rl->begin();
@@ -1058,7 +1058,9 @@ void RoutePopupMenu::prepare()
for( ; pi < MIDI_PORTS; ++pi)
{
MidiDevice* md = midiPorts[pi].device();
- if(md && !md->isSynti() && (md->rwFlags() & 2))
+ //if(md && !md->isSynti() && (md->rwFlags() & 2))
+ //if(md && (md->rwFlags() & 2)) // p4.0.27
+ if(md && (md->rwFlags() & 2 || md->isSynti()) ) // p4.0.27
break;
}
if(pi == MIDI_PORTS)
@@ -1085,10 +1087,12 @@ void RoutePopupMenu::prepare()
// continue;
// Do not list synth devices!
- if(md && md->isSynti())
- continue;
-
- if(md && !(md->rwFlags() & 2))
+ ///if(md && md->isSynti())
+ /// continue;
+ ///if(md && !(md->rwFlags() & 2))
+ /// continue;
+ // p4.0.27 Go ahead. Synths esp MESS send out stuff.
+ if( md && !(md->rwFlags() & 2) && !md->isSynti() )
continue;
//printf("MusE::prepareRoutingPopupMenu adding submenu portnum:%d\n", i);