diff options
34 files changed, 605 insertions, 309 deletions
diff --git a/muse2/ChangeLog b/muse2/ChangeLog index 081649b8..9928262c 100644 --- a/muse2/ChangeLog +++ b/muse2/ChangeLog @@ -1,5 +1,25 @@ 09.02.2011: - fixed regression with Bounce to File and Bounce to Track (rj) + [[[ By Tim. Marked as p4.0.15 ... + - Removed MidiDevice::nextPlayEvent, and instead of erasing 'already played events' at top of + Audio::processMidi(), let each device erase played events immediately after playing them. + Still a bit uneasy with this one, but nextPlayEvent was causing some problems. + This required forced event erasure, if the track is off, in SynthI::preProcessAlways(), + which may need more tweaking for other non-processed circumstances. + - Added MidiDevice::putEventWithRetry() which will wait and retry sending an event. + So far it is only used in MidiPort::setMidiDevice(), which runs in the GUI thread where + it can wait a bit. + - Increased playback MidiFifo size from 512 to 2100, to accommodate large block sends, + such as in MidiInstrument::reset() which sends 2048 events at once. + Created new recording MidiRecFifo with reduced size of 160 instead of previous 512. + Replaced SynthI::putFifo and MidiJackDevice::eventFifo with unified MidiDevice::eventFifo. + TODO: Allow ALSA devices to also make use of it, in case events cannot be delivered. + - Added a timestamp to OscControlFifo events. To be used for better, faster OSC processing - TODO. + - Fixed DSSI processing timing in DssiSynthIF::getData(). Should be much better now, tested OK. + - Fixed Jack Midi 'lost event' messages due to full buffers. Now it waits until next cycle if buffer is full. + Slight mod: SysEx events forced to be lost, since it is most likely that the sysex is too big and Jack Midi + could never allocate enough space for it, meaning the event would linger around forever attempting to + be sent, tying up other events. ]]] 08.02.2011: - made muse compilable under gcc-4.6. ptrdiff_t type requires inclusion of cstddef. (Orcan) 31.01.2011: diff --git a/muse2/awl/posedit.cpp b/muse2/awl/posedit.cpp index 3d4abaab..e9cddd44 100644 --- a/muse2/awl/posedit.cpp +++ b/muse2/awl/posedit.cpp @@ -93,7 +93,7 @@ bool PosEdit::event(QEvent* event) QKeyEvent* ke = static_cast<QKeyEvent*>(event); if (ke->key() == Qt::Key_Return) { - //printf("key press event Return\n"); // REMOVE Tim. + //printf("key press event Return\n"); //enterPressed(); finishEdit(); emit returnPressed(); @@ -103,7 +103,7 @@ bool PosEdit::event(QEvent* event) if (ke->key() == Qt::Key_Escape) { - //printf("key press event Escape\n"); // REMOVE Tim. + //printf("key press event Escape\n"); if(lineEdit()) lineEdit()->undo(); // "By default, isAccepted() is set to true, but don't rely on this as subclasses may @@ -363,8 +363,8 @@ void PosEdit::fixup(QString& input) const QValidator::State PosEdit::validate(QString& s,int& /*i*/) const { - //printf("validate string:%s int:%d\n", s.toLatin1().data(), i); // REMOVE Tim. - //printf("validate string:%s\n", s.toLatin1().data()); // REMOVE Tim. + //printf("validate string:%s int:%d\n", s.toLatin1().data(), i); + //printf("validate string:%s\n", s.toLatin1().data()); QStringList sl = s.split(_smpte ? ':' : '.'); QValidator::State state; @@ -436,7 +436,7 @@ QValidator::State PosEdit::validate(QString& s,int& /*i*/) const int bm = tm / tb; validator->setRange(1, 9999); - //printf("validate substring 0:%s\n", sl[0].toLatin1().data()); // REMOVE Tim. + //printf("validate substring 0:%s\n", sl[0].toLatin1().data()); // Special hack because validator says 0000 is intermediate. if(sl[0] == "0000") return QValidator::Invalid; @@ -447,7 +447,7 @@ QValidator::State PosEdit::validate(QString& s,int& /*i*/) const rv = state; validator->setRange(1, bm); - //printf("validate substring 1:%s\n", sl[1].toLatin1().data()); // REMOVE Tim. + //printf("validate substring 1:%s\n", sl[1].toLatin1().data()); // Special hack because validator says 00 is intermediate. if(sl[1] == "00") return QValidator::Invalid; @@ -458,7 +458,7 @@ QValidator::State PosEdit::validate(QString& s,int& /*i*/) const rv = state; validator->setRange(0, tb-1); - //printf("validate substring 2:%s\n", sl[2].toLatin1().data()); // REMOVE Tim. + //printf("validate substring 2:%s\n", sl[2].toLatin1().data()); state = validator->validate(sl[2], dpos); if(state == QValidator::Invalid) return state; diff --git a/muse2/muse/app.cpp b/muse2/muse/app.cpp index c12e5914..2f3d32ed 100644 --- a/muse2/muse/app.cpp +++ b/muse2/muse/app.cpp @@ -2231,7 +2231,7 @@ void MusE::updateRouteMenus(Track* track, QObject* master) imm->second.type == Route::TRACK_ROUTE && imm->second.track->type() == Track::AUDIO_INPUT) return; #if 0 - printf("imm route:\n"); // REMOVE Tim. + printf("imm route:\n"); imm->second.dump(); if(track->isMidiTrack()) { @@ -2256,19 +2256,19 @@ void MusE::updateRouteMenus(Track* track, QObject* master) iRoute ir = mprl->begin(); for(; ir != mprl->end(); ++ir) { - printf("mp route:\n"); // REMOVE Tim. - ir->dump(); // REMOVE Tim. + printf("mp route:\n"); + ir->dump(); ///if(aRoute.type == Route::TRACK_ROUTE) // Is the map route a track route? { if(ir->type == Route::TRACK_ROUTE && ir->track == aRoute.track) // Is the track route a midi port route? //&& (ir->channel & chbit) == chbit) //&& (ir->channel & tchbit)) // Is the exact channel mask bit(s) set? { - printf("track matches\n"); // REMOVE Tim. + printf("track matches\n"); if(ir->channel & tchbit) { found = true; - printf("found: bit matches\n"); // REMOVE Tim. + printf("found: bit matches\n"); } break; } @@ -2286,10 +2286,10 @@ void MusE::updateRouteMenus(Track* track, QObject* master) //if(pup->isItemChecked(imm->first) != (irl != rl->end())) // pup->setItemChecked(imm->first, irl != rl->end()); QAction* act = pup->findActionFromData(imm->first); - //printf("set act checked to:%d\n", ir != mprl->end()); // REMOVE Tim. + //printf("set act checked to:%d\n", ir != mprl->end()); //if(act && act->isChecked() != (ir != mprl->end())) // act->setChecked(ir != mprl->end()); - printf("set act checked to:%d\n", found); // REMOVE Tim. + printf("set act checked to:%d\n", found); if(act && act->isChecked() != found) act->setChecked(found); diff --git a/muse2/muse/appearance.cpp b/muse2/muse/appearance.cpp index 7b0383b5..df81e3b1 100644 --- a/muse2/muse/appearance.cpp +++ b/muse2/muse/appearance.cpp @@ -488,8 +488,8 @@ void Appearance::resetValues() themeComboBox->clear(); QString cs = muse->style()->objectName(); - //printf("Appearance::resetValues style:%s\n", cs.toAscii().data()); // REMOVE Tim - //printf("Appearance::resetValues App styleSheet:%s\n", qApp->styleSheet().toAscii().data()); // REMOVE Tim + //printf("Appearance::resetValues style:%s\n", cs.toAscii().data()); + //printf("Appearance::resetValues App styleSheet:%s\n", qApp->styleSheet().toAscii().data()); cs = cs.toLower(); themeComboBox->insertItems(0, QStyleFactory::keys()); diff --git a/muse2/muse/arranger/arranger.cpp b/muse2/muse/arranger/arranger.cpp index 38583d48..9b0de8c8 100644 --- a/muse2/muse/arranger/arranger.cpp +++ b/muse2/muse/arranger/arranger.cpp @@ -918,7 +918,7 @@ QSize WidgetStack::minimumSizeHint() const s = s.expandedTo(ss); } } - //printf("WidgetStack::minimumSizeHint width:%d height:%d\n", s.width(), s.height()); // REMOVE Tim. + //printf("WidgetStack::minimumSizeHint width:%d height:%d\n", s.width(), s.height()); return s; } @@ -1054,13 +1054,13 @@ void Arranger::switchInfo(int n) /* QSize WidgetStack::minimumSize() const { - printf("WidgetStack::minimumSize\n"); // REMOVE Tim. + printf("WidgetStack::minimumSize\n"); return minimumSizeHint(); } int WidgetStack::minimumHeight() const { - printf("WidgetStack::minimumHeight\n"); // REMOVE Tim. + printf("WidgetStack::minimumHeight\n"); return minimumSizeHint().height(); } */ diff --git a/muse2/muse/arranger/pcanvas.cpp b/muse2/muse/arranger/pcanvas.cpp index 9c970c52..5658f7e6 100644 --- a/muse2/muse/arranger/pcanvas.cpp +++ b/muse2/muse/arranger/pcanvas.cpp @@ -1731,7 +1731,7 @@ void PartCanvas::drawWavePart(QPainter& p, // // combine multi channels into one waveform // - //printf("PartCanvas::drawWavePart i:%d ex:%d\n", i, ex); // REMOVE Tim. + //printf("PartCanvas::drawWavePart i:%d ex:%d\n", i, ex); for (; i < ex; i++) { SampleV sa[channels]; diff --git a/muse2/muse/audio.cpp b/muse2/muse/audio.cpp index 1b62f15c..f541a10c 100644 --- a/muse2/muse/audio.cpp +++ b/muse2/muse/audio.cpp @@ -695,8 +695,12 @@ void Audio::processMsg(AudioMsg* msg) break; case SEQM_RESET_DEVICES: - for (int i = 0; i < MIDI_PORTS; ++i) - midiPorts[i].instrument()->reset(i, song->mtype()); + //printf("Audio::processMsg SEQM_RESET_DEVICES\n"); + for (int i = 0; i < MIDI_PORTS; ++i) + { + if(!midiPorts[i].device()) continue; // p4.0.15 + midiPorts[i].instrument()->reset(i, song->mtype()); + } break; case SEQM_INIT_DEVICES: initDevices(); diff --git a/muse2/muse/driver/jackmidi.cpp b/muse2/muse/driver/jackmidi.cpp index d401c7e1..c950e096 100644 --- a/muse2/muse/driver/jackmidi.cpp +++ b/muse2/muse/driver/jackmidi.cpp @@ -1150,7 +1150,8 @@ void MidiJackDevice::collectMidiEvents() bool MidiJackDevice::putEvent(const MidiPlayEvent& ev) { - if(!_writeEnable) + //if(!_writeEnable) + if(!_writeEnable || !_out_client_jackport) // p4.0.15 //return true; return false; @@ -1225,7 +1226,9 @@ bool MidiJackDevice::queueEvent(const MidiPlayEvent& e) unsigned char* p = jack_midi_event_reserve(pb, ft, 3); if (p == 0) { - fprintf(stderr, "MidiJackDevice::queueEvent #1: buffer overflow, event lost\n"); + #ifdef JACK_MIDI_DEBUG + fprintf(stderr, "MidiJackDevice::queueEvent NOTE CTL PAT or PB: buffer overflow, stopping until next cycle\n"); + #endif return false; } p[0] = e.type() | e.channel(); @@ -1243,7 +1246,9 @@ bool MidiJackDevice::queueEvent(const MidiPlayEvent& e) unsigned char* p = jack_midi_event_reserve(pb, ft, 2); if (p == 0) { - fprintf(stderr, "MidiJackDevice::queueEvent #2: buffer overflow, event lost\n"); + #ifdef JACK_MIDI_DEBUG + fprintf(stderr, "MidiJackDevice::queueEvent PROG or AT: buffer overflow, stopping until next cycle\n"); + #endif return false; } p[0] = e.type() | e.channel(); @@ -1260,8 +1265,16 @@ bool MidiJackDevice::queueEvent(const MidiPlayEvent& e) int len = e.len(); unsigned char* p = jack_midi_event_reserve(pb, ft, len+2); if (p == 0) { - fprintf(stderr, "MidiJackDevice::queueEvent #3: buffer overflow, event lost\n"); - return false; + fprintf(stderr, "MidiJackDevice::queueEvent ME_SYSEX: buffer overflow, sysex too big, event lost\n"); + + //return false; + // Changed to true. Absorb the sysex if it is too big, to avoid attempting + // to resend repeatedly. If the sysex is too big, it would just stay in the + // list and never be processed, because Jack could never reserve enough space. + // Other types of events should be OK since they are small and can be resent + // next cycle. p4.0.15 Tim. + // FIXME: We really need to chunk sysex events properly. It's tough. Investigating... + return true; } p[0] = 0xf0; p[len+1] = 0xf7; @@ -1273,8 +1286,10 @@ bool MidiJackDevice::queueEvent(const MidiPlayEvent& e) case ME_START: case ME_CONTINUE: case ME_STOP: - printf("MidiJackDevice::queueEvent: event type %x not supported\n", e.type()); - return false; + if(debugMsg) + printf("MidiJackDevice::queueEvent: event type %x not supported\n", e.type()); + //return false; + return true; // Absorb the event. Don't want it hanging around in the list. FIXME: Support these? p4.0.15 Tim. break; } @@ -1283,9 +1298,10 @@ bool MidiJackDevice::queueEvent(const MidiPlayEvent& e) //--------------------------------------------------------- // processEvent +// return true if successful //--------------------------------------------------------- -void MidiJackDevice::processEvent(const MidiPlayEvent& event) +bool MidiJackDevice::processEvent(const MidiPlayEvent& event) { //int frameOffset = audio->getFrameOffset(); //unsigned pos = audio->pos().frame(); @@ -1302,12 +1318,15 @@ void MidiJackDevice::processEvent(const MidiPlayEvent& event) // Just do this 'standard midi 64T timing thing' for now until we figure out more precise external timings. // Does require relatively short audio buffers, in order to catch the resolution, but buffer <= 256 should be OK... // Tested OK so far with 128. - if(extSyncFlag.value()) + ///if(extSyncFlag.value()) + // p4.0.15 Or, is the event marked to be played immediately? + // Nothing to do but stamp the event to be queued for frame 0+. + if(t == 0 || extSyncFlag.value()) t = audio->getFrameOffset() + audio->pos().frame(); //t = frameOffset + pos; #ifdef JACK_MIDI_DEBUG - printf("MidiJackDevice::processEvent time:%d type:%d ch:%d A:%d B:%d\n", event.time(), event.type(), event.channel(), event.dataA(), event.dataB()); + //printf("MidiJackDevice::processEvent time:%d type:%d ch:%d A:%d B:%d\n", t, event.type(), chn, a, b); #endif if(event.type() == ME_PROGRAM) @@ -1318,15 +1337,18 @@ void MidiJackDevice::processEvent(const MidiPlayEvent& event) int lb = (a >> 8) & 0xff; int pr = a & 0x7f; - // p3.3.44 //printf("MidiJackDevice::processEvent ME_PROGRAM time:%d type:%d ch:%d A:%d B:%d hb:%d lb:%d pr:%d\n", // event.time(), event.type(), event.channel(), event.dataA(), event.dataB(), hb, lb, pr); if (hb != 0xff) - queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HBANK, hb)); + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HBANK, hb))) + return false; // p4.0.15 Inform that processing the event in general failed. Ditto all below... if (lb != 0xff) - queueEvent(MidiPlayEvent(t+1, port, chn, ME_CONTROLLER, CTRL_LBANK, lb)); - queueEvent(MidiPlayEvent(t+2, port, chn, ME_PROGRAM, pr, 0)); + if(!queueEvent(MidiPlayEvent(t+1, port, chn, ME_CONTROLLER, CTRL_LBANK, lb))) + return false; + if(!queueEvent(MidiPlayEvent(t+2, port, chn, ME_PROGRAM, pr, 0))) + return false; + // } } else @@ -1336,7 +1358,8 @@ void MidiJackDevice::processEvent(const MidiPlayEvent& event) // p3.3.44 //printf("MidiJackDevice::processEvent ME_PITCHBEND v:%d time:%d type:%d ch:%d A:%d B:%d\n", v, event.time(), event.type(), event.channel(), event.dataA(), event.dataB()); - queueEvent(MidiPlayEvent(t, port, chn, ME_PITCHBEND, v & 0x7f, (v >> 7) & 0x7f)); + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_PITCHBEND, v & 0x7f, (v >> 7) & 0x7f))) + return false; } else if(event.type() == ME_CONTROLLER) @@ -1365,7 +1388,8 @@ void MidiJackDevice::processEvent(const MidiPlayEvent& event) // p3.3.44 //printf("MidiJackDevice::processEvent CTRL_PITCH v:%d time:%d type:%d ch:%d A:%d B:%d\n", v, event.time(), event.type(), event.channel(), event.dataA(), event.dataB()); - queueEvent(MidiPlayEvent(t, port, chn, ME_PITCHBEND, v & 0x7f, (v >> 7) & 0x7f)); + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_PITCHBEND, v & 0x7f, (v >> 7) & 0x7f))) + return false; } else if (a == CTRL_PROGRAM) { @@ -1380,28 +1404,36 @@ void MidiJackDevice::processEvent(const MidiPlayEvent& event) // event.time(), event.type(), event.channel(), event.dataA(), event.dataB(), hb, lb, pr); if (hb != 0xff) - queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HBANK, hb)); + { + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HBANK, hb))) + return false; + } if (lb != 0xff) - queueEvent(MidiPlayEvent(t+1, port, chn, ME_CONTROLLER, CTRL_LBANK, lb)); - queueEvent(MidiPlayEvent(t+2, port, chn, ME_PROGRAM, pr, 0)); + { + if(!queueEvent(MidiPlayEvent(t+1, port, chn, ME_CONTROLLER, CTRL_LBANK, lb))) + return false; + } + if(!queueEvent(MidiPlayEvent(t+2, port, chn, ME_PROGRAM, pr, 0))) + return false; + // } } - /* - else if (a == CTRL_MASTER_VOLUME) + else if (a == CTRL_MASTER_VOLUME) // Enabled p4.0.15 Tim. { unsigned char sysex[] = { 0x7f, 0x7f, 0x04, 0x01, 0x00, 0x00 }; - sysex[1] = deviceId(); + //sysex[1] = deviceId(); TODO FIXME p4.0.15 Grab the ID from midi port sync info. sysex[4] = b & 0x7f; sysex[5] = (b >> 7) & 0x7f; - queueEvent(MidiPlayEvent(t, port, ME_SYSEX, sysex, 6)); + if(!queueEvent(MidiPlayEvent(t, port, ME_SYSEX, sysex, 6))) + return false; } - */ else if (a < CTRL_14_OFFSET) { // 7 Bit Controller - queueEvent(event); //queueEvent(museport, MidiPlayEvent(t, port, chn, event)); + if(!queueEvent(event)) + return false; } else if (a < CTRL_RPN_OFFSET) { // 14 bit high resolution controller @@ -1409,46 +1441,62 @@ void MidiJackDevice::processEvent(const MidiPlayEvent& event) int ctrlL = a & 0x7f; int dataH = (b >> 7) & 0x7f; int dataL = b & 0x7f; - queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, ctrlH, dataH)); - queueEvent(MidiPlayEvent(t+1, port, chn, ME_CONTROLLER, ctrlL, dataL)); + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, ctrlH, dataH))) + return false; + if(!queueEvent(MidiPlayEvent(t+1, port, chn, ME_CONTROLLER, ctrlL, dataL))) + return false; } else if (a < CTRL_NRPN_OFFSET) { // RPN 7-Bit Controller int ctrlH = (a >> 8) & 0x7f; int ctrlL = a & 0x7f; - queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HRPN, ctrlH)); - queueEvent(MidiPlayEvent(t+1, port, chn, ME_CONTROLLER, CTRL_LRPN, ctrlL)); - queueEvent(MidiPlayEvent(t+2, port, chn, ME_CONTROLLER, CTRL_HDATA, b)); + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HRPN, ctrlH))) + return false; + if(!queueEvent(MidiPlayEvent(t+1, port, chn, ME_CONTROLLER, CTRL_LRPN, ctrlL))) + return false; + if(!queueEvent(MidiPlayEvent(t+2, port, chn, ME_CONTROLLER, CTRL_HDATA, b))) + return false; t += 3; // Select null parameters so that subsequent data controller events do not upset the last *RPN controller. //sendNullRPNParams(chn, false); if(nvh != 0xff) { - queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HRPN, nvh & 0x7f)); + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HRPN, nvh & 0x7f))) + return false; t += 1; } if(nvl != 0xff) - queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LRPN, nvl & 0x7f)); + { + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LRPN, nvl & 0x7f))) + return false; + } } //else if (a < CTRL_RPN14_OFFSET) else if (a < CTRL_INTERNAL_OFFSET) { // NRPN 7-Bit Controller int ctrlH = (a >> 8) & 0x7f; int ctrlL = a & 0x7f; - queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HNRPN, ctrlH)); - queueEvent(MidiPlayEvent(t+1, port, chn, ME_CONTROLLER, CTRL_LNRPN, ctrlL)); - queueEvent(MidiPlayEvent(t+2, port, chn, ME_CONTROLLER, CTRL_HDATA, b)); + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HNRPN, ctrlH))) + return false; + if(!queueEvent(MidiPlayEvent(t+1, port, chn, ME_CONTROLLER, CTRL_LNRPN, ctrlL))) + return false; + if(!queueEvent(MidiPlayEvent(t+2, port, chn, ME_CONTROLLER, CTRL_HDATA, b))) + return false; t += 3; //sendNullRPNParams(chn, true); if(nvh != 0xff) { - queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HNRPN, nvh & 0x7f)); + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HNRPN, nvh & 0x7f))) + return false; t += 1; } if(nvl != 0xff) - queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LNRPN, nvl & 0x7f)); + { + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LNRPN, nvl & 0x7f))) + return false; + } } else if (a < CTRL_NRPN14_OFFSET) { // RPN14 Controller @@ -1456,20 +1504,28 @@ void MidiJackDevice::processEvent(const MidiPlayEvent& event) int ctrlL = a & 0x7f; int dataH = (b >> 7) & 0x7f; int dataL = b & 0x7f; - queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HRPN, ctrlH)); - queueEvent(MidiPlayEvent(t+1, port, chn, ME_CONTROLLER, CTRL_LRPN, ctrlL)); - queueEvent(MidiPlayEvent(t+2, port, chn, ME_CONTROLLER, CTRL_HDATA, dataH)); - queueEvent(MidiPlayEvent(t+3, port, chn, ME_CONTROLLER, CTRL_LDATA, dataL)); + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HRPN, ctrlH))) + return false; + if(!queueEvent(MidiPlayEvent(t+1, port, chn, ME_CONTROLLER, CTRL_LRPN, ctrlL))) + return false; + if(!queueEvent(MidiPlayEvent(t+2, port, chn, ME_CONTROLLER, CTRL_HDATA, dataH))) + return false; + if(!queueEvent(MidiPlayEvent(t+3, port, chn, ME_CONTROLLER, CTRL_LDATA, dataL))) + return false; t += 4; //sendNullRPNParams(chn, false); if(nvh != 0xff) { - queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HRPN, nvh & 0x7f)); + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HRPN, nvh & 0x7f))) + return false; t += 1; } if(nvl != 0xff) - queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LRPN, nvl & 0x7f)); + { + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LRPN, nvl & 0x7f))) + return false; + } } else if (a < CTRL_NONE_OFFSET) { // NRPN14 Controller @@ -1477,31 +1533,44 @@ void MidiJackDevice::processEvent(const MidiPlayEvent& event) int ctrlL = a & 0x7f; int dataH = (b >> 7) & 0x7f; int dataL = b & 0x7f; - queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HNRPN, ctrlH)); - queueEvent(MidiPlayEvent(t+1, port, chn, ME_CONTROLLER, CTRL_LNRPN, ctrlL)); - queueEvent(MidiPlayEvent(t+2, port, chn, ME_CONTROLLER, CTRL_HDATA, dataH)); - queueEvent(MidiPlayEvent(t+3, port, chn, ME_CONTROLLER, CTRL_LDATA, dataL)); + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HNRPN, ctrlH))) + return false; + if(!queueEvent(MidiPlayEvent(t+1, port, chn, ME_CONTROLLER, CTRL_LNRPN, ctrlL))) + return false; + if(!queueEvent(MidiPlayEvent(t+2, port, chn, ME_CONTROLLER, CTRL_HDATA, dataH))) + return false; + if(!queueEvent(MidiPlayEvent(t+3, port, chn, ME_CONTROLLER, CTRL_LDATA, dataL))) + return false; t += 4; //sendNullRPNParams(chn, true); if(nvh != 0xff) { - queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HNRPN, nvh & 0x7f)); + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HNRPN, nvh & 0x7f))) + return false; t += 1; } if(nvl != 0xff) - queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LNRPN, nvl & 0x7f)); + { + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LNRPN, nvl & 0x7f))) + return false; + } } else { - printf("MidiJackDevice::processEvent: unknown controller type 0x%x\n", a); + if(debugMsg) + printf("MidiJackDevice::processEvent: unknown controller type 0x%x\n", a); + //return false; // Just ignore it. } } else { - queueEvent(event); //queueEvent(MidiPlayEvent(t, port, chn, event)); + if(!queueEvent(event)) + return false; } + + return true; } //--------------------------------------------------------- @@ -1511,40 +1580,58 @@ void MidiJackDevice::processEvent(const MidiPlayEvent& event) void MidiJackDevice::processMidi() { //if(!_client_jackport) - if(!_out_client_jackport) // p3.3.55 - return; + //if(!_out_client_jackport) // p3.3.55 + // return; + //void* port_buf = jack_port_get_buffer(_client_jackport, segmentSize); - void* port_buf = jack_port_get_buffer(_out_client_jackport, segmentSize); // p3.3.55 - jack_midi_clear_buffer(port_buf); + //void* port_buf = jack_port_get_buffer(_out_client_jackport, segmentSize); // p3.3.55 + void* port_buf = 0; + if(_out_client_jackport && _writeEnable) // p4.0.15 + { + port_buf = jack_port_get_buffer(_out_client_jackport, segmentSize); + jack_midi_clear_buffer(port_buf); + } while(!eventFifo.isEmpty()) { - MidiPlayEvent e(eventFifo.get()); - int evTime = e.time(); - // Is event marked to be played immediately? - if(evTime == 0) - { + ///MidiPlayEvent e(eventFifo.get()); + MidiPlayEvent e(eventFifo.peek()); // p4.0.15 + + ///int evTime = e.time(); + // Is event marked to be played immediately? p4.0.15 Moved into processEvent(). + ///if(evTime == 0) + ///{ // Nothing to do but stamp the event to be queued for frame 0+. //e.setTime(frameOffset + pos); - e.setTime(audio->getFrameOffset() + audio->pos().frame()); - } + /// e.setTime(audio->getFrameOffset() + audio->pos().frame()); + ///} - #ifdef JACK_MIDI_DEBUG - printf("MidiJackDevice::processMidi eventFifo time:%d type:%d ch:%d A:%d B:%d\n", e.time(), e.type(), e.channel(), e.dataA(), e.dataB()); - #endif + //#ifdef JACK_MIDI_DEBUG + //printf("MidiJackDevice::processMidi eventFifo time:%d type:%d ch:%d A:%d B:%d\n", e.time(), e.type(), e.channel(), e.dataA(), e.dataB()); + //#endif //el->insert(eventFifo.get()); //el->insert(e); - processEvent(e); + ///processEvent(e); + // p4.0.15 Try to process only until full, keep rest for next cycle. If no out client port or no write enable, eat up events. + if(port_buf && !processEvent(e)) + return; // Give up. The Jack buffer is full. Nothing left to do. + eventFifo.remove(); // Successfully processed event. Remove it from FIFO. } MPEventList* el = playEvents(); if(el->empty()) + { + //printf("MidiJackDevice::processMidi play events empty\n"); return; + } - iMPEvent i = nextPlayEvent(); + //printf("MidiJackDevice::processMidi play events:\n"); + ///iMPEvent i = nextPlayEvent(); + iMPEvent i = el->begin(); // p4.0.15 for(; i != el->end(); ++i) { + //printf("MidiJackDevice::processMidi playEvent time:%d type:%d ch:%d A:%d B:%d\n", i->time(), i->type(), i->channel(), i->dataA(), i->dataB()); // p3.3.39 Update hardware state so knobs and boxes are updated. Optimize to avoid re-setting existing values. // Same code as in MidiPort::sendEvent() if(_port != -1) @@ -1562,7 +1649,6 @@ void MidiJackDevice::processMidi() else if(i->type() == ME_PITCHBEND) { - // p3.3.44 //printf("MidiJackDevice::processMidi playEvents ME_PITCHBEND time:%d type:%d ch:%d A:%d B:%d\n", (*i).time(), (*i).type(), (*i).channel(), (*i).dataA(), (*i).dataB()); int da = mp->limitValToInstrCtlRange(CTRL_PITCH, i->dataA()); @@ -1581,10 +1667,26 @@ void MidiJackDevice::processMidi() } } - processEvent(*i); + ///processEvent(*i); + // p4.0.15 Try to process only until full, keep rest for next cycle. If no out client port or no write enable, eat up events. + if(port_buf && !processEvent(*i)) + { + //setNextPlayEvent(i); + //return; + break; + } } - setNextPlayEvent(i); + ///setNextPlayEvent(i); + // p4.0.15 We are done with these events. Let us erase them here instead of Audio::processMidi. + // That way we can simply set the next play event to the beginning. + // This also allows other events to be inserted without the problems caused by the next play event + // being at the 'end' iterator and not being *easily* set to some new place beginning of the newer insertions. + // The way that MPEventList sorts made it difficult to predict where the iterator of the first newly inserted items was. + // The erasure in Audio::processMidi was missing some events because of that. + el->erase(el->begin(), i); + // setNextPlayEvent(el->begin()); // Removed p4.0.15 Tim. + } //--------------------------------------------------------- diff --git a/muse2/muse/driver/jackmidi.h b/muse2/muse/driver/jackmidi.h index f7b5eb94..842cbc2d 100644 --- a/muse2/muse/driver/jackmidi.h +++ b/muse2/muse/driver/jackmidi.h @@ -19,7 +19,7 @@ #include "route.h" class QString; -class MidiFifo; +//class MidiFifo; class MidiRecordEvent; class MidiPlayEvent; //class RouteList; @@ -93,7 +93,7 @@ class MidiJackDevice : public MidiDevice { private: // fifo for midi events sent from gui // direct to midi port: - MidiFifo eventFifo; + //MidiFifo eventFifo; // Moved into MidiDevice p4.0.15 //static int _nextOutIdNum; //static int _nextInIdNum; @@ -109,7 +109,7 @@ class MidiJackDevice : public MidiDevice { virtual void close(); //bool putEvent(int*); - void processEvent(const MidiPlayEvent&); + bool processEvent(const MidiPlayEvent&); // Port is not midi port, it is the port(s) created for MusE. bool queueEvent(const MidiPlayEvent&); diff --git a/muse2/muse/dssihost.cpp b/muse2/muse/dssihost.cpp index 986abea1..d1d599f7 100644 --- a/muse2/muse/dssihost.cpp +++ b/muse2/muse/dssihost.cpp @@ -1728,83 +1728,6 @@ bool DssiSynthIF::processEvent(const MidiPlayEvent& e, snd_seq_event_t* event) // Since we absorbed the message as a ladspa control change, return false - the event is not filled. return false; //} - - // p3.3.39 Removed. - // "Hosts should not deliver through run_synth any MIDI controller events that have already - // been mapped to control port values." - // D'oh! My mistake, did not understand that the mapping is only a *request* that the app map MIDI - // controller events to a LADSPA port, and must do the conversion, not to actually *send* them via MIDI... - /* - else - { - switch(midiControllerType(a)) - { - case MidiController::Controller7: - #ifdef DSSI_DEBUG - //fprintf(stderr, "DssiSynthIF::processEvent midi event is Controller7. Changing to DSSI_CC type. Current dataA:%d\n", a); - fprintf(stderr, "DssiSynthIF::processEvent midi event is Controller7. Current dataA:%d\n", a); - #endif - //a = DSSI_CC(a); - a &= 0x7f; - ctlnum = DSSI_CC_NUMBER(ctlnum); - break; - case MidiController::NRPN14: - #ifdef DSSI_DEBUG - // fprintf(stderr, "DssiSynthIF::processEvent midi event is NRPN. Changing to DSSI_NRPN type. Current dataA:%d\n", a); - fprintf(stderr, "DssiSynthIF::processEvent midi event is NRPN. Current dataA:%d\n", a); - #endif - //a = DSSI_NRPN(a - CTRL_NRPN14_OFFSET); - a &= 0x3fff; - ctlnum = DSSI_NRPN_NUMBER(ctlnum); - break; - case MidiController::Controller14: - a &= 0x7f; - break; - case MidiController::Pitch: - #ifdef DSSI_DEBUG - fprintf(stderr, "DssiSynthIF::processEvent midi event is Pitch. DataA:%d\n", a); - #endif - a &= 0x3fff; - break; - case MidiController::Program: - #ifdef DSSI_DEBUG - fprintf(stderr, "DssiSynthIF::processEvent midi event is Program. DataA:%d\n", a); - #endif - a &= 0x3fff; - break; - case MidiController::RPN: - case MidiController::RPN14: - case MidiController::NRPN: - default: - #ifdef DSSI_DEBUG - fprintf(stderr, "DssiSynthIF::processEvent midi event is RPN, RPN14, or NRPN type. DataA:%d\n", a); - #endif - break; - } - - // Verify it's the same number. - if(ctlnum != a) - { - #ifdef DSSI_DEBUG - printf("DssiSynthIF::processEvent Error! ctlnum:%d != event dataA:%d\n", ctlnum, a); - #endif - // Event not filled. Return false. - - // TEMP: TODO: Turn on later - //return false; - } - - // Fill the event. - // FIXME: Darn! We get to this point, but no change in sound (later). Nothing happens, at least with LTS - - // which is the only one I found so far with midi controllers. - // Tried with/without converting to DSSI_CC and DSSI_NRPN. What could be wrong here? - #ifdef DSSI_DEBUG - printf("DssiSynthIF::processEvent filling event chn:%d dataA:%d dataB:%d\n", chn, a, b); - #endif - snd_seq_ev_set_controller(event, chn, a, b); - } - */ - } break; case ME_PITCHBEND: @@ -1910,7 +1833,7 @@ bool DssiSynthIF::processEvent(const MidiPlayEvent& e, snd_seq_event_t* event) //--------------------------------------------------------- //void DssiSynthIF::getData(MidiEventList* el, unsigned pos, int ch, unsigned samples, float** data) -iMPEvent DssiSynthIF::getData(MidiPort* /*mp*/, MPEventList* el, iMPEvent /*i*/, unsigned pos, int ports, unsigned n, float** buffer) +iMPEvent DssiSynthIF::getData(MidiPort* /*mp*/, MPEventList* el, iMPEvent i, unsigned pos, int ports, unsigned n, float** buffer) { //#ifdef DSSI_DEBUG // fprintf(stderr, "DssiSynthIF::getData elsize:%d pos:%d ports:%d samples:%d processed already?:%d\n", el->size(), pos, ports, n, synti->processed()); @@ -1920,11 +1843,11 @@ iMPEvent DssiSynthIF::getData(MidiPort* /*mp*/, MPEventList* el, iMPEvent /*i*/, // FIXME: Add 10(?) for good luck in case volatile size changes (increments) while we're processing. //unsigned long nevents = el->size(); - unsigned long nevents = el->size() + synti->putFifo.getSize() + 10; + unsigned long nevents = el->size() + synti->eventFifo.getSize() + 10; /* - while (!synti->putFifo.isEmpty()) { - MidiEvent event = synti->putFifo.get(); + while (!synti->eventFifo.isEmpty()) { + MidiEvent event = synti->eventFifo.get(); printf("Dssi: FIFO\n"); } */ @@ -1933,21 +1856,14 @@ iMPEvent DssiSynthIF::getData(MidiPort* /*mp*/, MPEventList* el, iMPEvent /*i*/, memset(events, 0, sizeof(events)); nevents = 0; - //int curPos = pos; - //unsigned endPos = pos + samples; unsigned endPos = pos + n; - //int off = pos; int frameOffset = audio->getFrameOffset(); - //iMidiEvent i = el->begin(); - iMPEvent i = el->begin(); + //iMPEvent i = el->begin(); // Removed p4.0.15 // Process event list events... for(; i != el->end(); ++i) { - //if(i->time() >= endPos) // Doesn't work, at least here in muse-1. The event times are all - // just slightly after the endPos, EVEN IF transport is stopped. - // So it misses all the notes. if(i->time() >= (endPos + frameOffset)) // NOTE: frameOffset? Tested, examined printouts of times: Seems OK for playback. break; @@ -1987,23 +1903,56 @@ iMPEvent DssiSynthIF::getData(MidiPort* /*mp*/, MPEventList* el, iMPEvent /*i*/, } if(processEvent(*i, &events[nevents])) + { + // Time-stamp the event. p4.0.15 Tim. + int ft = i->time() - frameOffset - pos; + if(ft < 0) + ft = 0; + if (ft >= (int)segmentSize) + { + printf("DssiSynthIF::getData: eventlist event time:%d out of range. pos:%d offset:%d ft:%d (seg=%d)\n", i->time(), pos, frameOffset, ft, segmentSize); + ///if (ft > (int)segmentSize) + ft = segmentSize - 1; + } + // "Each event is timestamped relative to the start of the block, (mis)using the ALSA "tick time" field as a frame count. + // The host is responsible for ensuring that events with differing timestamps are already ordered by time." - From dssi.h + events[nevents].time.tick = ft; + ++nevents; + } } // Now process putEvent events... - while(!synti->putFifo.isEmpty()) + while(!synti->eventFifo.isEmpty()) { - MidiPlayEvent e = synti->putFifo.get(); + MidiPlayEvent e = synti->eventFifo.get(); #ifdef DSSI_DEBUG - fprintf(stderr, "DssiSynthIF::getData putFifo event time:%d\n", e.time()); + fprintf(stderr, "DssiSynthIF::getData eventFifo event time:%d\n", e.time()); #endif - // Set to the current time. - // FIXME: FIXME: Wrong - we should be setting some kind of linear realtime wallclock here, not song pos. - e.setTime(pos); + // Maybe TODO: + //if(e.time() >= (endPos + frameOffset)) + // break; + if(processEvent(e, &events[nevents])) + { + // Time-stamp the event. p4.0.15 Tim. + int ft = e.time() - frameOffset - pos; + if(ft < 0) + ft = 0; + if (ft >= (int)segmentSize) + { + printf("DssiSynthIF::getData: eventFifo event time:%d out of range. pos:%d offset:%d ft:%d (seg=%d)\n", e.time(), pos, frameOffset, ft, segmentSize); + ///if (ft > (int)segmentSize) + ft = segmentSize - 1; + } + // "Each event is timestamped relative to the start of the block, (mis)using the ALSA "tick time" field as a frame count. + // The host is responsible for ensuring that events with differing timestamps are already ordered by time." - From dssi.h + events[nevents].time.tick = ft; + ++nevents; + } } // Now process OSC gui input control fifo events. @@ -2018,6 +1967,8 @@ iMPEvent DssiSynthIF::getData(MidiPort* /*mp*/, MPEventList* el, iMPEvent /*i*/, continue; // If there are 'events' in the fifo, get exactly one 'event' per control per process cycle... + // TODO: The OSC events are now time-stamped. Split up the processing below between parameter changes + // and get rid of this slooow control processing! p4.0.15 if(!cfifo->isEmpty()) { OscControlValue v = cfifo->get(); @@ -2041,40 +1992,9 @@ iMPEvent DssiSynthIF::getData(MidiPort* /*mp*/, MPEventList* el, iMPEvent /*i*/, } } #endif - -/* // This is from MESS... Tried this here, didn't work, need to re-adapt, try again. - int evTime = i->time(); - if(evTime == 0) - { - printf("DssiSynthIF::getData - time is 0!\n"); - //continue; - evTime=frameOffset; // will cause frame to be zero, problem? - } - - int frame = evTime - frameOffset; - - if(frame >= endPos) - { - printf("DssiSynthIF::getData frame > endPos!! frame = %d >= endPos %d, i->time() %d, frameOffset %d curPos=%d\n", frame, endPos, i->time(), frameOffset,curPos); - continue; - } - - if(frame > curPos) - { - if(frame < pos) - printf("DssiSynthIF::getData should not happen: missed event %d\n", pos -frame); - else - { -*/ - -/* - } - curPos = frame; - } -*/ -// } - el->erase(el->begin(), i); + ///el->erase(el->begin(), i); // Removed p4.0.15 Let SynthI::getData() do this. + //END: Process midi events //BEGIN: Run the synth @@ -2197,7 +2117,7 @@ bool DssiSynthIF::putEvent(const MidiPlayEvent& ev) if (midiOutputTrace) ev.dump(); - return synti->putFifo.put(ev); + return synti->eventFifo.put(ev); //return false; } @@ -2599,6 +2519,10 @@ int DssiSynthIF::oscControl(unsigned long port, float value) OscControlValue cv; //cv.idx = cport; cv.value = value; + // Time-stamp the event. Looks like no choice but to use the (possibly slow) call to gettimeofday via timestamp(), + // because these are asynchronous events arriving from OSC. timestamp() is more or less an estimate of the + // current frame. (This is exactly how ALSA events are treated when they arrive in our ALSA driver.) p4.0.15 Tim. + cv.frame = audio->timestamp(); if(cfifo->put(cv)) { fprintf(stderr, "DssiSynthIF::oscControl: fifo overflow: in control number:%ld\n", cport); diff --git a/muse2/muse/instruments/minstrument.cpp b/muse2/muse/instruments/minstrument.cpp index e39ee996..9e52498f 100644 --- a/muse2/muse/instruments/minstrument.cpp +++ b/muse2/muse/instruments/minstrument.cpp @@ -16,6 +16,9 @@ #include "minstrument.h" #include "midiport.h" +#include "mididev.h" // p4.0.15 +#include "audio.h" // p4.0.15 +#include "midi.h" // p4.0.15 #include "globals.h" #include "xml.h" #include "event.h" @@ -508,22 +511,39 @@ MidiInstrument& MidiInstrument::assign(const MidiInstrument& ins) //--------------------------------------------------------- void MidiInstrument::reset(int portNo, MType) +{ + MidiPort* port = &midiPorts[portNo]; + //if (port == 0) + // return; + if(port->device() == 0) // p4.0.15 { + //printf("MidiInstrument::reset port device is 0\n"); + return; + } MidiPlayEvent ev; ev.setType(0x90); - MidiPort* port = &midiPorts[portNo]; - if (port == 0) - return; ev.setPort(portNo); - for (int chan = 0; chan < MIDI_CHANNELS; ++chan) { + ev.setTime(0); // p4.0.15 + //ev.setTime(audio->getFrameOffset() + audio->pos().frame()); + + for (int chan = 0; chan < MIDI_CHANNELS; ++chan) + { ev.setChannel(chan); - for (int pitch = 0; pitch < 128; ++pitch) { + for (int pitch = 0; pitch < 128; ++pitch) + { ev.setA(pitch); ev.setB(0); + //printf("MidiInstrument::reset adding event channel:%d pitch:%d\n", chan, pitch); + //ev.dump(); + port->sendEvent(ev); - } + // Changed to use play events list instead of putEvent FIFO. + // These loops send 2048 events, which is more than our FIFO (or Jack buffer) can handle! p4.0.15 Tim. + // Nope, instead, increased FIFO sizes to accommodate. + //port->device()->playEvents()->add(ev); } } +} //--------------------------------------------------------- // readPatchGroup @@ -770,7 +790,7 @@ void MidiInstrument::write(int level, Xml& xml) xml.put(">"); // ------------- - // What about Init, Reset, State, and InitScript ? + // TODO: What about Init, Reset, State, and InitScript ? // ------------- //std::vector<PatchGroup>* pg = groups(); diff --git a/muse2/muse/main.cpp b/muse2/muse/main.cpp index 936a8aa3..fccf801d 100644 --- a/muse2/muse/main.cpp +++ b/muse2/muse/main.cpp @@ -107,10 +107,10 @@ class MuseApplication : public QApplication { bool notify(QObject* receiver, QEvent* event) { //if (event->type() == QEvent::KeyPress) - // printf("notify key press before app::notify accepted:%d\n", event->isAccepted()); // REMOVE Tim + // printf("notify key press before app::notify accepted:%d\n", event->isAccepted()); bool flag = QApplication::notify(receiver, event); if (event->type() == QEvent::KeyPress) { - //printf("notify key press after app::notify accepted:%d\n", event->isAccepted()); // REMOVE Tim + //printf("notify key press after app::notify accepted:%d\n", event->isAccepted()); QKeyEvent* ke = (QKeyEvent*)event; ///globalKeyState = ke->stateAfter(); globalKeyState = ke->modifiers(); diff --git a/muse2/muse/midi.cpp b/muse2/muse/midi.cpp index 87e9ff32..7087b342 100644 --- a/muse2/muse/midi.cpp +++ b/muse2/muse/midi.cpp @@ -945,12 +945,29 @@ void Audio::processMidi() for (iMidiDevice id = midiDevices.begin(); id != midiDevices.end(); ++id) { MidiDevice* md = *id; - MPEventList* playEvents = md->playEvents(); // // erase already played events: // - iMPEvent nextPlayEvent = md->nextPlayEvent(); - playEvents->erase(playEvents->begin(), nextPlayEvent); + ///MPEventList* playEvents = md->playEvents(); + ///iMPEvent nextPlayEvent = md->nextPlayEvent(); + + //if(md->playEvents()->size()) + //{ + //printf("Audio::processMidi before erase md play events size:%d nextPlayEvent isEnd:%d\n", md->playEvents()->size(), md->nextPlayEvent() == md->playEvents()->end()); + // printf("Audio::processMidi md play events size:%d first item:\n", md->playEvents()->size()); + // md->playEvents()->begin()->dump(); + //} + + // p4.0.15 Tim. Moved into MidiJackDevice::processMidi (for Jack), and MidiSeq::processTimerTick (for ALSA). + // Why do this here? Instead, let each device erase their just-played events. + ///playEvents->erase(playEvents->begin(), nextPlayEvent); + + //if(playEvents->size()) + //{ + // printf("Audio::processMidi after erase md play events size:%d nextPlayEvent isEnd:%d events:\n", playEvents->size(), nextPlayEvent == playEvents->end()); + //for(iMPEvent ie = playEvents->begin(); ie != playEvents->end(); ++ie) + // ie->dump(); + //} // klumsy hack for synti devices: if(md->isSynti()) @@ -974,9 +991,11 @@ void Audio::processMidi() md->beforeProcess(); } - MPEventList* playEvents = metronome->playEvents(); - iMPEvent nextPlayEvent = metronome->nextPlayEvent(); - playEvents->erase(playEvents->begin(), nextPlayEvent); + // p4.0.15 Tim. Moved into SynthI::getData. + // Why do this here? Instead, let each device erase their just-played events. + ///MPEventList* playEvents = metronome->playEvents(); + ///iMPEvent nextPlayEvent = metronome->nextPlayEvent(); + ///playEvents->erase(playEvents->begin(), nextPlayEvent); // p3.3.25 bool extsync = extSyncFlag.value(); @@ -1075,7 +1094,7 @@ void Audio::processMidi() if(!dev->sysexFIFOProcessed()) { // Set to the sysex fifo at first. - MidiFifo& rf = dev->recordEvents(MIDI_CHANNELS); + MidiRecFifo& rf = dev->recordEvents(MIDI_CHANNELS); // Get the frozen snapshot of the size. int count = dev->tmpRecordCount(MIDI_CHANNELS); @@ -1140,7 +1159,7 @@ void Audio::processMidi() } */ - MidiFifo& rf = dev->recordEvents(channel); + MidiRecFifo& rf = dev->recordEvents(channel); int count = dev->tmpRecordCount(channel); //for (iMREvent ie = el->begin(); ie != el->end(); ++ie) @@ -1343,11 +1362,14 @@ void Audio::processMidi() } } // Added by Tim. p3.3.8 - if(md) - { - - md->setNextPlayEvent(playEvents->begin()); - } + // Removed p4.0.15 + ///if(md) + /// md->setNextPlayEvent(playEvents->begin()); + //if(md) + //{ + // if(md->nextPlayEvent() != playEvents->begin()) + // printf("Audio::processMidi md->nextPlayEvent() != playEvents->begin()\n"); + //} } // @@ -1390,7 +1412,10 @@ void Audio::processMidi() playEvents->add(ev); } stuckNotes->erase(stuckNotes->begin(), k); - md->setNextPlayEvent(playEvents->begin()); + // Removed p4.0.15 Tim. + ///md->setNextPlayEvent(playEvents->begin()); + //if(md->nextPlayEvent() != playEvents->begin()) + // printf("Audio::processMidi clear notes: md->nextPlayEvent() != playEvents->begin()\n"); } //--------------------------------------------------- @@ -1473,10 +1498,24 @@ void Audio::processMidi() state = START_PLAY; } } - if (md) - md->setNextPlayEvent(playEvents->begin()); - if (audioClickFlag) - metronome->setNextPlayEvent(metronome->playEvents()->begin()); + // Removed p4.0.15 Tim. + ///if (md) + /// md->setNextPlayEvent(playEvents->begin()); + //if(md) + //{ + // if(md->nextPlayEvent() != playEvents->begin()) + // printf("Audio::processMidi metronome: md->nextPlayEvent() != playEvents->begin()\n"); + //} + + // Removed p4.0.15 Tim. + ///if (audioClickFlag) + /// metronome->setNextPlayEvent(metronome->playEvents()->begin()); + //if(audioClickFlag) + //{ + // if(metronome->nextPlayEvent() != metronome->playEvents()->begin()) + // printf("Audio::processMidi metronome: metronome->nextPlayEvent() != metronome->playEvents->begin()\n"); + //} + } if (state == STOP) { diff --git a/muse2/muse/midictrl.cpp b/muse2/muse/midictrl.cpp index b96fcda6..66f8d87e 100644 --- a/muse2/muse/midictrl.cpp +++ b/muse2/muse/midictrl.cpp @@ -68,8 +68,8 @@ MidiControllerList defaultMidiController; MidiController veloCtrl("Velocity", CTRL_VELOCITY, 0, 127, 0); static MidiController pitchCtrl("PitchBend", CTRL_PITCH, -8192, +8191, 0); static MidiController programCtrl("Program", CTRL_PROGRAM, 0, 0xffffff, 0); -// Removed p3.3.37 -//static MidiController mastervolCtrl("MasterVolume", CTRL_MASTER_VOLUME, 0, 0x3fff, 0x3000); +// Removed p3.3.37 Re-added p4.0.15 +static MidiController mastervolCtrl("MasterVolume", CTRL_MASTER_VOLUME, 0, 0x3fff, 0x3000); static MidiController volumeCtrl("MainVolume", CTRL_VOLUME, 0, 127, 100); static MidiController panCtrl("Pan", CTRL_PANPOT, -64, 63, 0); @@ -132,8 +132,8 @@ void initMidiController() defaultMidiController.add(&veloCtrl); defaultMidiController.add(&pitchCtrl); defaultMidiController.add(&programCtrl); - // Removed p3.3.37 - //defaultMidiController.add(&mastervolCtrl); + // Removed p3.3.37 Re-added p4.0.15 + defaultMidiController.add(&mastervolCtrl); defaultMidiController.add(&volumeCtrl); defaultMidiController.add(&panCtrl); } diff --git a/muse2/muse/midictrl.h b/muse2/muse/midictrl.h index 4c08fbe0..27f8e7be 100644 --- a/muse2/muse/midictrl.h +++ b/muse2/muse/midictrl.h @@ -68,7 +68,7 @@ const int CTRL_INTERNAL_OFFSET = 0x40000; const int CTRL_PITCH = CTRL_INTERNAL_OFFSET; const int CTRL_PROGRAM = CTRL_INTERNAL_OFFSET + 1; const int CTRL_VELOCITY = CTRL_INTERNAL_OFFSET + 2; -//const int CTRL_MASTER_VOLUME = CTRL_INTERNAL_OFFSET + 3; +const int CTRL_MASTER_VOLUME = CTRL_INTERNAL_OFFSET + 3; const int CTRL_VAL_UNKNOWN = 0x10000000; // used as unknown hwVal diff --git a/muse2/muse/mididev.cpp b/muse2/muse/mididev.cpp index 0aab9a71..10be79ef 100644 --- a/muse2/muse/mididev.cpp +++ b/muse2/muse/mididev.cpp @@ -73,7 +73,7 @@ void MidiDevice::init() _rwFlags = 3; _openFlags = 3; _port = -1; - _nextPlayEvent = _playEvents.begin(); + // _nextPlayEvent = _playEvents.begin(); // Removed p4.0.15 Tim. } //--------------------------------------------------------- @@ -443,9 +443,34 @@ bool MidiDevice::sendNullRPNParams(int chn, bool nrpn) } //--------------------------------------------------------- +// putEventWithRetry +// return true if event cannot be delivered +// This method will try to putEvent 'tries' times, waiting 'delayUs' microseconds between tries. +// NOTE: Since it waits, it should not be used in RT or other time-sensitive threads. p4.0.15 Tim. +//--------------------------------------------------------- + +bool MidiDevice::putEventWithRetry(const MidiPlayEvent& ev, int tries, long delayUs) +{ + // TODO: Er, probably not the best way to do this. + // Maybe try to correlate with actual audio buffer size instead of blind time delay. + for( ; tries > 0; --tries) + { + if(!putEvent(ev)) // Returns true if event cannot be delivered. + return false; + + bool sleepOk = -1; + while(sleepOk == -1) + sleepOk = usleep(delayUs); // FIXME: usleep is supposed to be depricated! + } + return true; +} + +//--------------------------------------------------------- // putEvent // return true if event cannot be delivered // TODO: retry on controller putMidiEvent +// (Note: Since putEvent is virtual and there are different versions, +// a retry facility is now found in putEventWithRetry. p4.0.15 Tim) //--------------------------------------------------------- bool MidiDevice::putEvent(const MidiPlayEvent& ev) diff --git a/muse2/muse/mididev.h b/muse2/muse/mididev.h index 16e834f2..6ac93729 100644 --- a/muse2/muse/mididev.h +++ b/muse2/muse/mididev.h @@ -28,7 +28,10 @@ class Xml; class MidiDevice { MPEventList _stuckNotes; MPEventList _playEvents; - iMPEvent _nextPlayEvent; + + // Removed p4.0.15 Tim. + //iMPEvent _nextPlayEvent; + ///MREventList _recordEvents; ///MREventList _recordEvents2; @@ -53,10 +56,14 @@ class MidiDevice { //bool _sysexWritingChunks; bool _sysexReadingChunks; + // Fifo for midi events sent from gui direct to midi port: + MidiFifo eventFifo; // p4.0.15 + // Recording fifo. //MidiFifo _recordFifo; // Recording fifos. To speed up processing, one per channel plus one special system 'channel' for channel-less events like sysex. - MidiFifo _recordFifo[MIDI_CHANNELS + 1]; + //MidiFifo _recordFifo[MIDI_CHANNELS + 1]; + MidiRecFifo _recordFifo[MIDI_CHANNELS + 1]; // p4.0.15 RouteList _inRoutes, _outRoutes; @@ -109,6 +116,9 @@ class MidiDevice { virtual void recordEvent(MidiRecordEvent&); virtual bool putEvent(const MidiPlayEvent&); + // This method will try to putEvent 'tries' times, waiting 'delayUs' microseconds between tries. + // Since it waits, it should not be used in RT or other time-sensitive threads. p4.0.15 + bool putEventWithRetry(const MidiPlayEvent&, int /*tries*/ = 2, long /*delayUs*/ = 50000); // 2 tries, 50 mS by default. // For Jack-based devices - called in Jack audio process callback virtual void collectMidiEvents() {} @@ -125,7 +135,7 @@ class MidiDevice { //int tmpRecordCount() { return _tmpRecordCount; } int tmpRecordCount(const unsigned int ch) { return _tmpRecordCount[ch]; } //MidiFifo& recordEvents() { return _recordFifo; } - MidiFifo& recordEvents(const unsigned int ch) { return _recordFifo[ch]; } + MidiRecFifo& recordEvents(const unsigned int ch) { return _recordFifo[ch]; } bool sysexFIFOProcessed() { return _sysexFIFOProcessed; } void setSysexFIFOProcessed(bool v) { _sysexFIFOProcessed = v; } //bool sysexWritingChunks() { return _sysexWritingChunks; } @@ -134,8 +144,10 @@ class MidiDevice { void setSysexReadingChunks(bool v) { _sysexReadingChunks = v; } //virtual void getEvents(unsigned /*from*/, unsigned /*to*/, int /*channel*/, MPEventList* /*dst*/); - iMPEvent nextPlayEvent() { return _nextPlayEvent; } - void setNextPlayEvent(iMPEvent i) { _nextPlayEvent = i; } + // Removed p4.0.15 Tim. + //iMPEvent nextPlayEvent() { return _nextPlayEvent; } + //void setNextPlayEvent(iMPEvent i) { _nextPlayEvent = i; } + bool sendNullRPNParams(int, bool); }; diff --git a/muse2/muse/midiedit/dcanvas.cpp b/muse2/muse/midiedit/dcanvas.cpp index fa89b8ad..623a2734 100644 --- a/muse2/muse/midiedit/dcanvas.cpp +++ b/muse2/muse/midiedit/dcanvas.cpp @@ -1050,7 +1050,7 @@ void DrumCanvas::dragEnterEvent(QDragEnterEvent* event) void DrumCanvas::dragMoveEvent(QDragMoveEvent*) { - //printf("drag move %x\n", this); // REMOVE Tim + //printf("drag move %x\n", this); //event->acceptProposedAction(); } @@ -1060,7 +1060,7 @@ void DrumCanvas::dragMoveEvent(QDragMoveEvent*) void DrumCanvas::dragLeaveEvent(QDragLeaveEvent*) { - //printf("drag leave\n"); // REMOVE Tim + //printf("drag leave\n"); //event->acceptProposedAction(); } @@ -1073,7 +1073,7 @@ void DrumCanvas::viewDropEvent(QDropEvent* event) { QString text; if (event->source() == this) { - printf("local DROP\n"); // REMOVE Tim + printf("local DROP\n"); //event->acceptProposedAction(); //event->ignore(); // TODO CHECK Tim. return; diff --git a/muse2/muse/midiedit/ecanvas.cpp b/muse2/muse/midiedit/ecanvas.cpp index 889657aa..c8ee91ef 100644 --- a/muse2/muse/midiedit/ecanvas.cpp +++ b/muse2/muse/midiedit/ecanvas.cpp @@ -518,7 +518,7 @@ void EventCanvas::viewDropEvent(QDropEvent* event) { QString text; if (event->source() == this) { - printf("local DROP\n"); // REMOVE Tim + printf("local DROP\n"); //event->acceptProposedAction(); //event->ignore(); // TODO CHECK Tim. return; diff --git a/muse2/muse/midiedit/prcanvas.cpp b/muse2/muse/midiedit/prcanvas.cpp index 4b280a16..a0ffdcaf 100644 --- a/muse2/muse/midiedit/prcanvas.cpp +++ b/muse2/muse/midiedit/prcanvas.cpp @@ -1584,7 +1584,7 @@ void PianoCanvas::viewDropEvent(QDropEvent* event) { QString text; if (event->source() == this) { - printf("local DROP\n"); // REMOVE Tim + printf("local DROP\n"); //event->acceptProposedAction(); //event->ignore(); // TODO CHECK Tim. return; diff --git a/muse2/muse/midiport.cpp b/muse2/muse/midiport.cpp index 66da871a..0220a353 100644 --- a/muse2/muse/midiport.cpp +++ b/muse2/muse/midiport.cpp @@ -181,8 +181,15 @@ void MidiPort::setMidiDevice(MidiDevice* dev) ///{ ///#endif // Note the addition of bias! - _device->putEvent(MidiPlayEvent(0, portno(), chan, + //_device->putEvent(MidiPlayEvent(0, portno(), chan, + // ME_CONTROLLER, ctl, mc->initVal() + mc->bias())); + // Retry added. Use default attempts and delay. p4.0.15 + _device->putEventWithRetry(MidiPlayEvent(0, portno(), chan, ME_CONTROLLER, ctl, mc->initVal() + mc->bias())); + //if(_device->putEventWithRetry(MidiPlayEvent(0, portno(), chan, + // ME_CONTROLLER, ctl, mc->initVal() + mc->bias()))) + // return; + ///#ifdef DSSI_SUPPORT ///} ///#endif @@ -214,8 +221,15 @@ void MidiPort::setMidiDevice(MidiDevice* dev) ///if(!_device->isSynti() || (dynamic_cast<DssiSynthIF*>(((SynthI*)_device)->sif()) == 0)) ///{ ///#endif - _device->putEvent(MidiPlayEvent(0, portno(), channel, - ME_CONTROLLER, cntrl, val)); + //_device->putEvent(MidiPlayEvent(0, portno(), channel, + // ME_CONTROLLER, cntrl, val)); + // Retry added. Use default attempts and delay. p4.0.15 + _device->putEventWithRetry(MidiPlayEvent(0, portno(), channel, + ME_CONTROLLER, cntrl, val)); + //if(_device->putEventWithRetry(MidiPlayEvent(0, portno(), channel, + // ME_CONTROLLER, cntrl, val))) + // return; + ///#ifdef DSSI_SUPPORT ///} ///#endif @@ -320,6 +334,8 @@ void MidiPort::tryCtrlInitVal(int chan, int ctl, int val) //MidiPlayEvent ev(song->cpos(), portno(), chan, ME_CONTROLLER, ctl, initval + mc->bias()); MidiPlayEvent ev(0, portno(), chan, ME_CONTROLLER, ctl, initval + mc->bias()); _device->putEvent(ev); + // Retry added. Use default attempts and delay. p4.0.15 + //_device->putEventWithRetry(ev); } // Set it once so the 'last HW value' is set, and control knobs are positioned at the value... //setHwCtrlState(chan, ctl, initval + mc->bias()); @@ -337,6 +353,8 @@ void MidiPort::tryCtrlInitVal(int chan, int ctl, int val) //MidiPlayEvent ev(song->cpos(), portno(), chan, ME_CONTROLLER, ctl, val); MidiPlayEvent ev(0, portno(), chan, ME_CONTROLLER, ctl, val); _device->putEvent(ev); + // Retry added. Use default attempts and delay. p4.0.15 + //_device->putEventWithRetry(ev); } // Set it once so the 'last HW value' is set, and control knobs are positioned at the value... //setHwCtrlState(chan, ctl, val); diff --git a/muse2/muse/midiseq.cpp b/muse2/muse/midiseq.cpp index 8aabcbbb..5e42d547 100644 --- a/muse2/muse/midiseq.cpp +++ b/muse2/muse/midiseq.cpp @@ -54,9 +54,11 @@ void MidiSeq::processMsg(const ThreadMsg* m) { AudioMsg* msg = (AudioMsg*)m; switch(msg->id) { - case MS_PROCESS: - audio->processMidi(); - break; + // This does not appear to be used anymore. Was sent in Audio::process1, + // now Audio::processMidi is called directly. p4.0.15 Tim. + //case MS_PROCESS: + // audio->processMidi(); + // break; case SEQM_SEEK: processSeek(); break; @@ -149,7 +151,7 @@ void MidiSeq::processStop() pel->add(ev); } sel->clear(); - md->setNextPlayEvent(pel->begin()); + //md->setNextPlayEvent(pel->begin()); // Removed p4.0.15 } } @@ -188,8 +190,11 @@ void MidiSeq::processSeek() } sel->clear(); } - else - el->erase(el->begin(), dev->nextPlayEvent()); + //else + // Removed p4.0.15 Device now leaves beginning pointing at next event, + // immediately after playing some notes. + // NOTE: This removal needs testing. I'm not sure about this. + // el->erase(el->begin(), dev->nextPlayEvent()); for (iMidiCtrlValList ivl = cll->begin(); ivl != cll->end(); ++ivl) { MidiCtrlValList* vl = ivl->second; @@ -208,7 +213,7 @@ void MidiSeq::processSeek() el->add(MidiPlayEvent(0, port, ivl->first >> 24, ME_CONTROLLER, vl->num(), imcv->second.val)); } } - dev->setNextPlayEvent(el->begin()); + //dev->setNextPlayEvent(el->begin()); // Removed p4.0.15 } } @@ -543,7 +548,7 @@ void MidiSeq::processTimerTick() return; } if (midiBusy) { - // we hit audio: midiSeq->msgProcess + // we hit audio: midiSeq->msgProcess (actually this has been audio->processMidi for some time now - Tim) // miss this timer tick return; } @@ -650,7 +655,6 @@ void MidiSeq::processTimerTick() printf("Dropped %d midi out clock(s). curTick:%d midiClock:%d div:%d\n", perr, curTick, midiClock, div); //} - // Keeping in mind how (receiving end) Phase Locked Loops (usually) operate... // Increment as if we had caught the timer exactly on the mark, even if the timer // has passed beyond the mark, or even beyond 2 * div. // If we missed some chances to send clock, resume the count where it would have been, @@ -690,7 +694,7 @@ void MidiSeq::processTimerTick() // for (iMidiDevice id = midiDevices.begin(); id != midiDevices.end(); ++id) { MidiDevice* md = *id; - // Is it a Jack midi device? p3.3.36 + // Is it a Jack midi device? They are iterated in Audio::processMidi. p3.3.36 //MidiJackDevice* mjd = dynamic_cast<MidiJackDevice*>(md); //if(mjd) if(md->deviceType() == MidiDevice::JACK_MIDI) @@ -702,7 +706,10 @@ void MidiSeq::processTimerTick() MPEventList* el = md->playEvents(); if (el->empty()) continue; - iMPEvent i = md->nextPlayEvent(); + + ///iMPEvent i = md->nextPlayEvent(); + iMPEvent i = el->begin(); // p4.0.15 Tim. + for (; i != el->end(); ++i) { // p3.3.25 // If syncing to external midi sync, we cannot use the tempo map. @@ -722,7 +729,15 @@ void MidiSeq::processTimerTick() break; } } - md->setNextPlayEvent(i); + ///md->setNextPlayEvent(i); + // p4.0.15 We are done with these events. Let us erase them here instead of Audio::processMidi. + // That way we can simply set the next play event to the beginning. + // This also allows other events to be inserted without the problems caused by the next play event + // being at the 'end' iterator and not being *easily* set to some new place beginning of the newer insertions. + // The way that MPEventList sorts made it difficult to predict where the iterator of the first newly inserted items was. + // The erasure in Audio::processMidi was missing some events because of that. + el->erase(el->begin(), i); + //md->setNextPlayEvent(el->begin()); // Removed p4.0.15 } } @@ -758,7 +773,8 @@ void MidiSeq::msgSetMidiDevice(MidiPort* port, MidiDevice* device) Thread::sendMsg(&msg); } -void MidiSeq::msgProcess() { msgMsg(MS_PROCESS); } +// This does not appear to be used anymore. Was called in Audio::process1, now Audio::processMidi is called directly. p4.0.15 Tim. +//void MidiSeq::msgProcess() { msgMsg(MS_PROCESS); } void MidiSeq::msgSeek() { msgMsg(SEQM_SEEK); } void MidiSeq::msgStop() { msgMsg(MS_STOP); } void MidiSeq::msgSetRtc() { msgMsg(MS_SET_RTC); } diff --git a/muse2/muse/mixer/amixer.cpp b/muse2/muse/mixer/amixer.cpp index e4a82ce8..f59a2187 100644 --- a/muse2/muse/mixer/amixer.cpp +++ b/muse2/muse/mixer/amixer.cpp @@ -226,7 +226,7 @@ AudioMixerApp::AudioMixerApp(QWidget* parent, MixerConfig* c) /* bool AudioMixerApp::event(QEvent* event) { - printf("AudioMixerApp::event type:%d\n", event->type()); // REMOVE Tim. + printf("AudioMixerApp::event type:%d\n", event->type()); // Let it do the layout now, before we emit. QMainWindow::event(event); @@ -246,7 +246,7 @@ void AudioMixerApp::setSizing() { //w += (*si)->frameGeometry().width(); //Strip* s = *si; - //printf("AudioMixerApp::setSizing width:%d frame width:%d\n", s->width(), s->frameWidth()); // REMOVE Tim + //printf("AudioMixerApp::setSizing width:%d frame width:%d\n", s->width(), s->frameWidth()); //w += s->width() + 2 * (s->frameWidth() + s->lineWidth() + s->midLineWidth()); //w += s->width() + 2 * s->frameWidth(); w += (*si)->width(); @@ -359,7 +359,7 @@ void AudioMixerApp::updateMixer(UpdateAction action) stripList.erase(ssi); } - //printf("AudioMixerApp::updateMixer STRIP_REMOVED\n"); // REMOVE Tim + //printf("AudioMixerApp::updateMixer STRIP_REMOVED\n"); //setMaximumWidth(STRIP_WIDTH * stripList.size() + __WIDTH_COMPENSATION); /// int w = computeWidth(); @@ -410,7 +410,7 @@ void AudioMixerApp::updateMixer(UpdateAction action) addStrip(*i, idx++); } - //printf("AudioMixerApp::updateMixer UPDATE_MIDI\n"); // REMOVE Tim + //printf("AudioMixerApp::updateMixer UPDATE_MIDI\n"); //setMaximumWidth(STRIP_WIDTH * stripList.size() + __WIDTH_COMPENSATION); /// int w = computeWidth(); @@ -500,7 +500,7 @@ void AudioMixerApp::updateMixer(UpdateAction action) addStrip(*i, idx++); } - //printf("AudioMixerApp::updateMixer other\n"); // REMOVE Tim + //printf("AudioMixerApp::updateMixer other\n"); //setMaximumWidth(STRIP_WIDTH * idx + __WIDTH_COMPENSATION); /// int w = computeWidth(); diff --git a/muse2/muse/mixer/astrip.cpp b/muse2/muse/mixer/astrip.cpp index b6a89ada..62695823 100644 --- a/muse2/muse/mixer/astrip.cpp +++ b/muse2/muse/mixer/astrip.cpp @@ -831,7 +831,7 @@ AudioStrip::AudioStrip(QWidget* parent, AudioTrack* at) pan = addKnob(0, 0, &panl); pan->setValue(t->pan()); - + //--------------------------------------------------- // mute, solo, record //--------------------------------------------------- diff --git a/muse2/muse/mpevent.cpp b/muse2/muse/mpevent.cpp index 9988c12b..5b4fc4cb 100644 --- a/muse2/muse/mpevent.cpp +++ b/muse2/muse/mpevent.cpp @@ -105,7 +105,6 @@ bool MEvent::operator<(const MEvent& e) const return map[channel()] < map[e.channel()]; } - //--------------------------------------------------------- // put // return true on fifo overflow @@ -158,3 +157,55 @@ void MidiFifo::remove() } +//--------------------------------------------------------- +// put +// return true on fifo overflow +//--------------------------------------------------------- + +bool MidiRecFifo::put(const MidiPlayEvent& event) + { + if (size < MIDI_REC_FIFO_SIZE) { + fifo[wIndex] = event; + wIndex = (wIndex + 1) % MIDI_REC_FIFO_SIZE; + // q_atomic_increment(&size); + ++size; + return false; + } + return true; + } + +//--------------------------------------------------------- +// get +//--------------------------------------------------------- + +MidiPlayEvent MidiRecFifo::get() + { + MidiPlayEvent event(fifo[rIndex]); + rIndex = (rIndex + 1) % MIDI_REC_FIFO_SIZE; + // q_atomic_decrement(&size); + --size; + return event; + } + +//--------------------------------------------------------- +// peek +//--------------------------------------------------------- + +const MidiPlayEvent& MidiRecFifo::peek(int n) + { + int idx = (rIndex + n) % MIDI_REC_FIFO_SIZE; + return fifo[idx]; + } + +//--------------------------------------------------------- +// remove +//--------------------------------------------------------- + +void MidiRecFifo::remove() + { + rIndex = (rIndex + 1) % MIDI_REC_FIFO_SIZE; + // q_atomic_decrement(&size); + --size; + } + + diff --git a/muse2/muse/mpevent.h b/muse2/muse/mpevent.h index 6df7b0c0..b9b21e08 100644 --- a/muse2/muse/mpevent.h +++ b/muse2/muse/mpevent.h @@ -14,7 +14,14 @@ #include "evdata.h" #include "memory.h" -#define MIDI_FIFO_SIZE 512 +// Play events ring buffer size +//#define MIDI_FIFO_SIZE 512 +// Increased. FE/6/11 p4.0.15 Tim. +#define MIDI_FIFO_SIZE 2100 + +// Record events ring buffer size +//#define MIDI_REC_FIFO_SIZE 512 +#define MIDI_REC_FIFO_SIZE 160 class Event; class EvData; @@ -132,8 +139,9 @@ class MidiPlayEvent : public MEvent { typedef std::multiset<MidiPlayEvent, std::less<MidiPlayEvent>, audioRTalloc<MidiPlayEvent> > MPEL; struct MPEventList : public MPEL { - void add(const MidiPlayEvent& ev) { MPEL::insert(ev); } - }; + //void add(const MidiPlayEvent& ev) { MPEL::insert(ev); } + iterator add(const MidiPlayEvent& ev) { return MPEL::insert(ev); } // p4.0.15 We need the iterator. +}; typedef MPEventList::iterator iMPEvent; typedef MPEventList::const_iterator ciMPEvent; @@ -170,9 +178,31 @@ class MidiFifo { public: MidiFifo() { clear(); } - bool put(const MidiPlayEvent& event); // returns true on fifo overflow + bool put(const MidiPlayEvent& /*event*/); // returns true on fifo overflow + MidiPlayEvent get(); + const MidiPlayEvent& peek(int = 0); + void remove(); + bool isEmpty() const { return size == 0; } + void clear() { size = 0, wIndex = 0, rIndex = 0; } + int getSize() const { return size; } + }; + +//--------------------------------------------------------- +// MidiRecFifo +// (Same as MidiFifo, but with a smaller size.) +//--------------------------------------------------------- + +class MidiRecFifo { + MidiPlayEvent fifo[MIDI_REC_FIFO_SIZE]; + volatile int size; + int wIndex; + int rIndex; + + public: + MidiRecFifo() { clear(); } + bool put(const MidiPlayEvent& /*event*/); // returns true on fifo overflow MidiPlayEvent get(); - const MidiPlayEvent& peek(int n = 0); + const MidiPlayEvent& peek(int = 0); void remove(); bool isEmpty() const { return size == 0; } void clear() { size = 0, wIndex = 0, rIndex = 0; } diff --git a/muse2/muse/osc.cpp b/muse2/muse/osc.cpp index 26cd3a8a..c5ff182d 100644 --- a/muse2/muse/osc.cpp +++ b/muse2/muse/osc.cpp @@ -184,6 +184,7 @@ int oscMessageHandler(const char* path, const char* types, lo_arg** argv, continue; //DssiSynthIF* instance = (DssiSynthIF*)synti->sif(); + // TODO: Fix this dynamic cast - it may be a slowdown. DssiSynthIF* instance = dynamic_cast<DssiSynthIF*>(synti->sif()); if(!instance) break; diff --git a/muse2/muse/osc.h b/muse2/muse/osc.h index 8e093e2c..7d9afa92 100644 --- a/muse2/muse/osc.h +++ b/muse2/muse/osc.h @@ -48,7 +48,7 @@ struct OscControlValue { //int idx; float value; - // maybe timestamp, too ? + int frame; // Added p4.0.15 }; //--------------------------------------------------------- diff --git a/muse2/muse/plugin.cpp b/muse2/muse/plugin.cpp index 5bacf092..179708ea 100644 --- a/muse2/muse/plugin.cpp +++ b/muse2/muse/plugin.cpp @@ -2454,6 +2454,10 @@ int PluginI::oscControl(unsigned long port, float value) OscControlValue cv; //cv.idx = cport; cv.value = value; + // Time-stamp the event. Looks like no choice but to use the (possibly slow) call to gettimeofday via timestamp(), + // because these are asynchronous events arriving from OSC. timestamp() is more or less an estimate of the + // current frame. (This is exactly how ALSA events are treated when they arrive in our ALSA driver.) p4.0.15 Tim. + cv.frame = audio->timestamp(); if(cfifo->put(cv)) { fprintf(stderr, "PluginI::oscControl: fifo overflow: in control number:%ld\n", cport); diff --git a/muse2/muse/route.cpp b/muse2/muse/route.cpp index 92c32317..05d25eee 100644 --- a/muse2/muse/route.cpp +++ b/muse2/muse/route.cpp @@ -366,12 +366,12 @@ void addRoute(Route src, Route dst) for ( ; ir != outRoutes->end(); ++ir) { //if (*i == dst) // route already there - ir->dump(); // REMOVE Tim. + ir->dump(); if (ir->type == Route::TRACK_ROUTE && ir->track == dst.track) // Does a route to the track exist? { //#ifdef ROUTE_DEBUG fprintf(stderr, "addRoute: src midi port:%d dst audio in track:%s out route already exists. ir->channel:%d |= dst.channel:%d\n", - src.midiPort, dst.track->name().toLatin1().constData(), ir->channel, dst.channel); // REMOVE Tim. + src.midiPort, dst.track->name().toLatin1().constData(), ir->channel, dst.channel); //#endif ir->channel |= dst.channel; // Bitwise OR the desired channel bit with the existing bit mask. break; @@ -389,7 +389,7 @@ void addRoute(Route src, Route dst) if (ir->type == Route::MIDI_PORT_ROUTE && ir->midiPort == src.midiPort) // Does a route to the midi port exist? { fprintf(stderr, "addRoute: src midi port:%d dst audio in track:%s in route already exists. ir->channel:%d |= src.channel:%d\n", - src.midiPort, dst.track->name().toLatin1().constData(), ir->channel, src.channel); // REMOVE Tim. + src.midiPort, dst.track->name().toLatin1().constData(), ir->channel, src.channel); ir->channel |= src.channel; // Bitwise OR the desired channel bit with the existing bit mask. break; } diff --git a/muse2/muse/sync.cpp b/muse2/muse/sync.cpp index 9fe5f4d3..22117536 100644 --- a/muse2/muse/sync.cpp +++ b/muse2/muse/sync.cpp @@ -28,7 +28,7 @@ //MidiSyncPort midiSyncPorts[MIDI_PORTS]; int volatile curMidiSyncInPort = -1; -bool debugSync = true; +bool debugSync = false; int mtcType = 1; MTC mtcOffset; @@ -637,7 +637,6 @@ void MidiSeq::mtcInputQuarter(int port, unsigned char c) { static int hour, min, sec, frame; - // p3.3.28 //printf("MidiSeq::mtcInputQuarter c:%h\n", c); int valL = c & 0xf; @@ -924,7 +923,6 @@ void MidiSeq::realtimeSystemInput(int port, int c) if(port != curMidiSyncInPort) break; - // p3.3.31 //printf("midi clock:%f\n", curTime()); // Re-transmit clock to other devices if clock out turned on. @@ -1235,7 +1233,6 @@ void MidiSeq::realtimeSystemInput(int port, int c) if (debugSync) printf(" start\n"); - // p3.3.31 //printf("midi start:%f\n", curTime()); if (1 /* !audio->isPlaying()*/ /*state == IDLE*/) { @@ -1285,7 +1282,6 @@ void MidiSeq::realtimeSystemInput(int port, int c) if (debugSync) printf("realtimeSystemInput continue\n"); - // p3.3.31 //printf("continue:%f\n", curTime()); if (1 /* !audio->isPlaying() */ /*state == IDLE */) { @@ -1321,7 +1317,6 @@ void MidiSeq::realtimeSystemInput(int port, int c) //lastStoppedBeat = (audio->tickPos() * 4) / config.division; //curExtMidiSyncTick = (config.division * lastStoppedBeat) / 4; - // p3.3.31 //printf("stop:%f\n", curTime()); if (audio->isPlaying() /*state == PLAY*/) { diff --git a/muse2/muse/synth.cpp b/muse2/muse/synth.cpp index defcd02e..f8d21d41 100644 --- a/muse2/muse/synth.cpp +++ b/muse2/muse/synth.cpp @@ -851,6 +851,31 @@ void SynthI::preProcessAlways() if(_sif) _sif->preProcessAlways(); _processed = false; + + // TODO: p4.0.15 Tim. Erasure of already-played events was moved from Audio::processMidi() + // to each of the midi devices - ALSA, Jack, or Synth in SynthI::getData() below. + // If a synth track is 'off', AudioTrack::copyData() does not call our getData(). + // So there is no processing of midi play events, or putEvent FIFOs. + // Hence the play events list and putEvent FIFOs will then accumulate events, sometimes + // thousands. Only when the Synth track is turned on again, are all these events + // processed. Whether or not we want this is a question. + // + // If we DON'T want the events to accumulate, we NEED this following piece of code. + // Without this code: When a song is loaded, if a Synth track is off, various controller init events + // can remain queued up so that when the Synth track is turned on, those initializations + // will be processed. Otherwise we, or the user, will have to init every time the track is turned on. + // Con: Thousands of events can accumulate. For example selecting "midi -> Reset Instr." sends a flood + // of 2048 note-off events, one for each note in each channel! Each time, the 2048, 4096, 8192 etc. + // events remain in the list. + // Variation: Maybe allow certain types, or groups, of events through, especially bulk init or note offs. + if(off()) + { + // Clear any accumulated play events. + playEvents()->clear(); + // Eat up any fifo events. + while(!eventFifo.isEmpty()) + eventFifo.get(); + } } void MessSynthIF::preProcessAlways() @@ -872,11 +897,21 @@ bool SynthI::getData(unsigned pos, int ports, unsigned n, float** buffer) MidiPort* mp = (p != -1) ? &midiPorts[p] : 0; MPEventList* el = playEvents(); - iMPEvent ie = nextPlayEvent(); + ///iMPEvent ie = nextPlayEvent(); + iMPEvent ie = el->begin(); // p4.0.15 Tim. ie = _sif->getData(mp, el, ie, pos, ports, n, buffer); - setNextPlayEvent(ie); + ///setNextPlayEvent(ie); + // p4.0.15 We are done with these events. Let us erase them here instead of Audio::processMidi. + // That way we can simply set the next play event to the beginning. + // This also allows other events to be inserted without the problems caused by the next play event + // being at the 'end' iterator and not being *easily* set to some new place beginning of the newer insertions. + // The way that MPEventList sorts made it difficult to predict where the iterator of the first newly inserted items was. + // The erasure in Audio::processMidi was missing some events because of that. + el->erase(el->begin(), ie); + // setNextPlayEvent(el->begin()); // Removed p4.0.15 + return true; } diff --git a/muse2/muse/synth.h b/muse2/muse/synth.h index de400423..1e06682c 100644 --- a/muse2/muse/synth.h +++ b/muse2/muse/synth.h @@ -157,7 +157,7 @@ class SynthI : public AudioTrack, public MidiDevice, protected: Synth* synthesizer; - MidiFifo putFifo; + // MidiFifo putFifo; // Moved into MidiDevice p4.0.15 // List of initial floating point parameters, for synths which use them. // Used once upon song reload, then discarded. diff --git a/muse2/muse/widgets/shortcutcapturedialog.cpp b/muse2/muse/widgets/shortcutcapturedialog.cpp index ca627661..73534811 100644 --- a/muse2/muse/widgets/shortcutcapturedialog.cpp +++ b/muse2/muse/widgets/shortcutcapturedialog.cpp @@ -53,7 +53,7 @@ void ShortcutCaptureDialog::keyPressEvent(QKeyEvent* e) bool ispunct = keychar.isPunct(); bool issymbol = keychar.isSymbol(); //printf("Key:%x, alt:%d, ctrl:%d shift:%d ispunct:%d issymbol:%d text:%s\n", - // e->key(), alt, ctrl, shift, ispunct, issymbol, e->text().toLatin1().constData()); // REMOVE Tim. + // e->key(), alt, ctrl, shift, ispunct, issymbol, e->text().toLatin1().constData()); temp_key += (shift ? (int)Qt::SHIFT : 0); // (int) Tim temp_key += (ctrl ? (int)Qt::CTRL : 0); // |