diff options
Diffstat (limited to 'muse2/muse')
-rw-r--r-- | muse2/muse/ctrl.cpp | 3 | ||||
-rw-r--r-- | muse2/muse/ctrl/ctrlcanvas.cpp | 61 | ||||
-rw-r--r-- | muse2/muse/ctrl/ctrlpanel.cpp | 2 | ||||
-rw-r--r-- | muse2/muse/driver/jack.cpp | 3 | ||||
-rw-r--r-- | muse2/muse/driver/jackmidi.cpp | 11 | ||||
-rw-r--r-- | muse2/muse/dssihost.cpp | 246 | ||||
-rw-r--r-- | muse2/muse/dssihost.h | 10 | ||||
-rw-r--r-- | muse2/muse/evdata.h | 2 | ||||
-rw-r--r-- | muse2/muse/instruments/minstrument.cpp | 42 | ||||
-rw-r--r-- | muse2/muse/instruments/minstrument.h | 3 | ||||
-rw-r--r-- | muse2/muse/midi.cpp | 3 | ||||
-rw-r--r-- | muse2/muse/midi.h | 6 | ||||
-rw-r--r-- | muse2/muse/midiport.cpp | 55 | ||||
-rw-r--r-- | muse2/muse/node.cpp | 50 | ||||
-rw-r--r-- | muse2/muse/synth.cpp | 44 | ||||
-rw-r--r-- | muse2/muse/synth.h | 20 | ||||
-rw-r--r-- | muse2/muse/ticksynth.cpp | 4 | ||||
-rw-r--r-- | muse2/muse/waveedit/waveview.cpp | 3 | ||||
-rw-r--r-- | muse2/muse/widgets/mtrackinfo.cpp | 111 | ||||
-rw-r--r-- | muse2/muse/widgets/mtrackinfo.h | 1 | ||||
-rw-r--r-- | muse2/muse/widgets/routepopup.cpp | 16 |
21 files changed, 492 insertions, 204 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 d1d6b2fa..678cd823 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; @@ -570,9 +588,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) @@ -643,6 +662,9 @@ void CtrlCanvas::updateSelections() void CtrlCanvas::viewMousePressEvent(QMouseEvent* event) { + if(!_controller) // p4.0.27 + return; + start = event->pos(); Tool activeTool = tool; @@ -654,9 +676,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) { @@ -748,6 +770,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; @@ -820,6 +845,7 @@ void CtrlCanvas::viewMouseReleaseEvent(QMouseEvent* event) lasso.setRect(-1, -1, -1, -1); case DRAG_LASSO: + if(_controller) // p4.0.27 { ///if (!ctrlKey) /// deselectAll(); @@ -866,6 +892,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; @@ -967,6 +996,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(); @@ -1033,6 +1065,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(); @@ -1096,6 +1131,9 @@ void CtrlCanvas::changeVal(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, @@ -1304,6 +1342,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; @@ -1483,6 +1524,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; @@ -1626,6 +1670,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; @@ -1750,7 +1797,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/waveedit/waveview.cpp b/muse2/muse/waveedit/waveview.cpp index 1c7f74f2..cd798c40 100644 --- a/muse2/muse/waveedit/waveview.cpp +++ b/muse2/muse/waveedit/waveview.cpp @@ -348,7 +348,8 @@ void WaveView::setPos(int idx, unsigned val, bool adjustScrollbar) x = opos; } pos[idx] = val; - redraw(QRect(x, 0, w, height())); + //redraw(QRect(x, 0, w, height())); + redraw(QRect(x-1, 0, w+2, height())); // p4.0.28 From Canvas::draw (is otherwise identical). Fix for corruption. } //--------------------------------------------------------- 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); |