bool MidiDevice::sendNullRPNParams(unsigned time, int port, int chn, bool nrpn) { if(_port == -1) return false; int nv = MusEGlobal::midiPorts[_port].nullSendValue(); if(nv == -1) return false; int nvh = (nv >> 8) & 0xff; int nvl = nv & 0xff; if(nvh != 0xff) { if(nrpn) putMidiEvent(MidiPlayEvent(time, port, chn, ME_CONTROLLER, CTRL_HNRPN, nvh & 0x7f)); else putMidiEvent(MidiPlayEvent(time, port, chn, ME_CONTROLLER, CTRL_HRPN, nvh & 0x7f)); } if(nvl != 0xff) { if(nrpn) putMidiEvent(MidiPlayEvent(time, port, chn, ME_CONTROLLER, CTRL_LNRPN, nvl & 0x7f)); else putMidiEvent(MidiPlayEvent(time, port, chn, ME_CONTROLLER, CTRL_LRPN, nvl & 0x7f)); } return true; }
void Audio::panic() { for (int i = 0; i < MIDI_PORTS; ++i) { MidiPort* port = &midiPorts[i]; if (port == 0) // ?? continue; for (int chan = 0; chan < MIDI_CHANNELS; ++chan) { if (debugMsg) printf("send all sound of to midi port %d channel %d\n", i, chan); port->sendEvent(MidiPlayEvent(0, i, chan, ME_CONTROLLER, CTRL_ALL_SOUNDS_OFF, 0), true); port->sendEvent(MidiPlayEvent(0, i, chan, ME_CONTROLLER, CTRL_RESET_ALL_CTRL, 0), true); } } }
bool MidiJackDevice::processEvent(const MidiPlayEvent& event) { int chn = event.channel(); unsigned t = event.time(); int a = event.dataA(); int b = event.dataB(); // Perhaps we can find use for this value later, together with the Jack midi LOS port(s). // No big deal if not. Not used for now. int port = event.port(); // TODO: No sub-tick playback resolution yet, with external sync. // 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 (t == 0 /*|| extSyncFlag.value()*/) t = audio->getFrameOffset() + audio->pos().frame(); #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()); #endif //Send to monitor thread for processing monitorOutputEvent(event); if (event.type() == ME_PROGRAM) { // don't output program changes for GM drum channel int hb = (a >> 16) & 0xff; int lb = (a >> 8) & 0xff; int pr = a & 0x7f; //TODO: NOTE this is where program changes are sent we can later debug this to diagnose the dropped events if (hb != 0xff) { if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HBANK, hb))) return false; } if (lb != 0xff) { if(!queueEvent(MidiPlayEvent(t + 1, port, chn, ME_CONTROLLER, CTRL_LBANK, lb))) return false; } //sleep(1); if(!queueEvent(MidiPlayEvent(t + 2, port, chn, ME_PROGRAM, pr, 0))) return false; }
void Audio::sendLocalOff() { for (int k = 0; k < MIDI_PORTS; ++k) { for (int i = 0; i < MIDI_CHANNELS; ++i) midiPorts[k].sendEvent(MidiPlayEvent(0, k, i, ME_CONTROLLER, CTRL_LOCAL_OFF, 0)); } }
bool MidiDevice::putEvent(const MidiPlayEvent& ev) { if(!_writeEnable) //return true; return false; unsigned t = ev.time(); int port = ev.port(); if (ev.type() == ME_CONTROLLER) { int a = ev.dataA(); int b = ev.dataB(); int chn = ev.channel(); if (a == CTRL_PITCH) { return putMidiEvent(MidiPlayEvent(t, port, chn, ME_PITCHBEND, b, 0)); } if ((a | 0xff) == CTRL_POLYAFTER) { return putMidiEvent(MidiPlayEvent(t, port, chn, ME_POLYAFTER, a & 0x7f, b & 0x7f)); } if (a == CTRL_AFTERTOUCH) { return putMidiEvent(MidiPlayEvent(t, port, chn, ME_AFTERTOUCH, b, 0)); } if (a == CTRL_PROGRAM) { int hb = (b >> 16) & 0xff; int lb = (b >> 8) & 0xff; int pr = b & 0x7f; if (hb != 0xff) putMidiEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HBANK, hb)); if (lb != 0xff) putMidiEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LBANK, lb)); return putMidiEvent(MidiPlayEvent(t, port, chn, ME_PROGRAM, pr, 0)); }
void MidiJackDevice::recordEvent(MidiRecordEvent& event)/*{{{*/ { if (audio->isPlaying()) event.setLoopNum(audio->loopCount()); if (midiInputTrace) { printf("Jack MidiInput: "); event.dump(); } int typ = event.type(); if (_port != -1) { //call our midimonitor with the data, it can then decide what to do monitorEvent(event); } // // process midi event input filtering and // transformation // processMidiInputTransformPlugins(event); if (filterEvent(event, midiRecordType, false)) return; if (!applyMidiInputTransformation(event)) { if (midiInputTrace) printf(" midi input transformation: event filtered\n"); return; } // // transfer noteOn events to gui for step recording and keyboard // remote control // if (typ == ME_NOTEON) { int pv = ((event.dataA() & 0xff) << 8) + (event.dataB() & 0xff); song->putEvent(pv); } //This was not sending noteoff events at all sometimes causing strange endless note in step rec else if(typ == ME_NOTEOFF) { int pv = ((event.dataA() & 0xff) << 8) + (0x00); song->putEvent(pv); } // Do not bother recording if it is NOT actually being used by a port. // Because from this point on, process handles things, by selected port. if (_port == -1) return; // Split the events up into channel fifos. Special 'channel' number 17 for sysex events. unsigned int ch = (typ == ME_SYSEX) ? kMaxMidiChannels : event.channel(); if (_recordFifo[ch].put(MidiPlayEvent(event))) printf("MidiJackDevice::recordEvent: fifo channel %d overflow\n", ch); }/*}}}*/
if (a < CTRL_14_OFFSET) { // 7 Bit Controller l->add(MidiPlayEvent(tick, port, channel, ME_CONTROLLER, a, b)); } else if (a < CTRL_RPN_OFFSET) { // 14 Bit Controller int ctrlH = (a >> 8) & 0x7f; int ctrlL = a & 0x7f; int dataH = (b >> 7) & 0x7f; int dataL = b & 0x7f; l->add(MidiPlayEvent(tick, port, channel, ME_CONTROLLER, ctrlH, dataH)); l->add(MidiPlayEvent(tick+1, port, channel, ME_CONTROLLER, ctrlL, dataL)); } else if (a < CTRL_NRPN_OFFSET) { // RPN 7-Bit Controller int ctrlH = (a >> 8) & 0x7f; int ctrlL = a & 0x7f; l->add(MidiPlayEvent(tick, port, channel, ME_CONTROLLER, CTRL_HRPN, ctrlH)); l->add(MidiPlayEvent(tick+1, port, channel, ME_CONTROLLER, CTRL_LRPN, ctrlL)); l->add(MidiPlayEvent(tick+2, port, channel, ME_CONTROLLER, CTRL_HDATA, b)); } else if (a < CTRL_INTERNAL_OFFSET) { // NRPN 7-Bit Controller int ctrlH = (a >> 8) & 0x7f; int ctrlL = a & 0x7f; l->add(MidiPlayEvent(tick, port, channel, ME_CONTROLLER, CTRL_HNRPN, ctrlH)); l->add(MidiPlayEvent(tick+1, port, channel, ME_CONTROLLER, CTRL_LNRPN, ctrlL)); l->add(MidiPlayEvent(tick+2, port, channel, ME_CONTROLLER, CTRL_HDATA, b)); } else if (a == CTRL_PITCH) { int a = b + 8192; int b = a >> 7; l->add(MidiPlayEvent(tick, port, channel, ME_PITCHBEND, a & 0x7f, b & 0x7f)); } else if (a == CTRL_PROGRAM) {
virtual MidiPlayEvent receiveEvent() { return MidiPlayEvent(); }
void MidiDevice::handleSeek() { // If the device is not in use by a port, don't bother it. if(_port == -1) return; MidiPort* mp = &MusEGlobal::midiPorts[_port]; MidiInstrument* instr = mp->instrument(); MidiCtrlValListList* cll = mp->controller(); unsigned pos = MusEGlobal::audio->tickPos(); //--------------------------------------------------- // Send STOP //--------------------------------------------------- // Don't send if external sync is on. The master, and our sync routing system will take care of that. if(!MusEGlobal::extSyncFlag.value()) { if(mp->syncInfo().MRTOut()) { // Shall we check for device write open flag to see if it's ok to send?... //if(!(rwFlags() & 0x1) || !(openFlags() & 1)) //if(!(openFlags() & 1)) // continue; mp->sendStop(); } } //--------------------------------------------------- // If playing, clear all notes and flush out any // stuck notes which were put directly to the device //--------------------------------------------------- if(MusEGlobal::audio->isPlaying()) { _playEvents.clear(); for(iMPEvent i = _stuckNotes.begin(); i != _stuckNotes.end(); ++i) { MidiPlayEvent ev(*i); ev.setTime(0); putEvent(ev); // For immediate playback try putEvent, putMidiEvent, or sendEvent (for the optimizations). } _stuckNotes.clear(); } //--------------------------------------------------- // Send new controller values //--------------------------------------------------- // Find channels on this port used in the song... bool usedChans[MIDI_CHANNELS]; int usedChanCount = 0; for(int i = 0; i < MIDI_CHANNELS; ++i) usedChans[i] = false; if(MusEGlobal::song->click() && MusEGlobal::clickPort == _port) { usedChans[MusEGlobal::clickChan] = true; ++usedChanCount; } bool drum_found = false; for(ciMidiTrack imt = MusEGlobal::song->midis()->begin(); imt != MusEGlobal::song->midis()->end(); ++imt) { //------------------------------------------------------------ // While we are at it, flush out any track-related playback stuck notes // (NOT 'live' notes) which were not put directly to the device //------------------------------------------------------------ MPEventList& mel = (*imt)->stuckNotes; for(iMPEvent i = mel.begin(), i_next = i; i != mel.end(); i = i_next) { ++i_next; if((*i).port() != _port) continue; MidiPlayEvent ev(*i); ev.setTime(0); putEvent(ev); // For immediate playback try putEvent, putMidiEvent, or sendEvent (for the optimizations). mel.erase(i); } if((*imt)->type() == MusECore::Track::DRUM) { if(!drum_found) { drum_found = true; for(int i = 0; i < DRUM_MAPSIZE; ++i) { // Default to track port if -1 and track channel if -1. int mport = MusEGlobal::drumMap[i].port; if(mport == -1) mport = (*imt)->outPort(); int mchan = MusEGlobal::drumMap[i].channel; if(mchan == -1) mchan = (*imt)->outChannel(); if(mport != _port || usedChans[mchan]) continue; usedChans[mchan] = true; ++usedChanCount; if(usedChanCount >= MIDI_CHANNELS) break; // All are used, done searching. } } } else { if((*imt)->outPort() != _port || usedChans[(*imt)->outChannel()]) continue; usedChans[(*imt)->outChannel()] = true; ++usedChanCount; } if(usedChanCount >= MIDI_CHANNELS) break; // All are used. Done searching. } for(iMidiCtrlValList ivl = cll->begin(); ivl != cll->end(); ++ivl) { MidiCtrlValList* vl = ivl->second; int chan = ivl->first >> 24; if(!usedChans[chan]) // Channel not used in song? continue; int ctlnum = vl->num(); // Find the first non-muted value at the given tick... bool values_found = false; bool found_value = false; iMidiCtrlVal imcv = vl->lower_bound(pos); if(imcv != vl->end() && imcv->first == (int)pos) { for( ; imcv != vl->end() && imcv->first == (int)pos; ++imcv) { const Part* p = imcv->second.part; if(!p) continue; // Ignore values that are outside of the part. if(pos < p->tick() || pos >= (p->tick() + p->lenTick())) continue; values_found = true; // Ignore if part or track is muted or off. if(p->mute()) continue; const Track* track = p->track(); if(track && (track->isMute() || track->off())) continue; found_value = true; break; } } else { while(imcv != vl->begin()) { --imcv; const Part* p = imcv->second.part; if(!p) continue; // Ignore values that are outside of the part. unsigned t = imcv->first; if(t < p->tick() || t >= (p->tick() + p->lenTick())) continue; values_found = true; // Ignore if part or track is muted or off. if(p->mute()) continue; const Track* track = p->track(); if(track && (track->isMute() || track->off())) continue; found_value = true; break; } } if(found_value) { // Don't bother sending any sustain values if not playing. Just set the hw state. if(ctlnum == CTRL_SUSTAIN && !MusEGlobal::audio->isPlaying()) mp->setHwCtrlState(chan, CTRL_SUSTAIN, imcv->second.val); else // Use sendEvent to get the optimizations and limiting. But force if there's a value at this exact position. // NOTE: Why again was this forced? There was a reason. Think it was RJ in response to bug rep, then I modded. // A reason not to force: If a straight line is drawn on graph, multiple identical events are stored // (which must be allowed). So seeking through them here sends them all redundantly, not good. // REMOVE Tim. mp->sendEvent(MidiPlayEvent(0, _port, chan, ME_CONTROLLER, ctlnum, imcv->second.val), false); //, imcv->first == pos); //mp->sendEvent(MidiPlayEvent(0, _port, chan, ME_CONTROLLER, ctlnum, imcv->second.val), pos == 0 || imcv->first == pos); } // Either no value was found, or they were outside parts, or pos is in the unknown area before the first value. // Send instrument default initial values. NOT for syntis. Use midiState and/or initParams for that. //if((imcv == vl->end() || !done) && !MusEGlobal::song->record() && instr && !isSynti()) // Hmm, without refinement we can only do this at position 0, due to possible 'skipped' values outside parts, above. if(!values_found && MusEGlobal::config.midiSendCtlDefaults && !MusEGlobal::song->record() && pos == 0 && instr && !isSynti()) { MidiControllerList* mcl = instr->controller(); ciMidiController imc = mcl->find(vl->num()); if(imc != mcl->end()) { MidiController* mc = imc->second; if(mc->initVal() != CTRL_VAL_UNKNOWN) // Use sendEvent to get the optimizations and limiting. No force sending. Note the addition of bias. mp->sendEvent(MidiPlayEvent(0, _port, chan, ME_CONTROLLER, ctlnum, mc->initVal() + mc->bias()), false); } } } //--------------------------------------------------- // reset sustain //--------------------------------------------------- for(int ch = 0; ch < MIDI_CHANNELS; ++ch) { if(mp->hwCtrlState(ch, CTRL_SUSTAIN) == 127) { const MidiPlayEvent ev(0, _port, ch, ME_CONTROLLER, CTRL_SUSTAIN, 0); putEvent(ev); } } //--------------------------------------------------- // Send STOP and "set song position pointer" //--------------------------------------------------- // Don't send if external sync is on. The master, and our sync routing system will take care of that. if(!MusEGlobal::extSyncFlag.value()) { if(mp->syncInfo().MRTOut()) { //mp->sendStop(); // Moved above int beat = (pos * 4) / MusEGlobal::config.division; mp->sendSongpos(beat); } } }
MidiPlayEvent SynthPluginDevice::receiveEvent() { if(debugMsg) qWarning("SynthPluginDevice::receiveEvent()"); return MidiPlayEvent() ;//_sif->receiveEvent(); }
void Audio::collectEvents(MidiTrack* track, unsigned int cts, unsigned int nts) { int port = track->outPort(); int channel = track->outChannel(); int defaultPort = port; MidiDevice* md = midiPorts[port].device(); MPEventList* playEvents = md->playEvents(); MPEventList* stuckNotes = md->stuckNotes(); PartList* pl = track->parts(); for (iPart p = pl->begin(); p != pl->end(); ++p) { MidiPart* part = (MidiPart*) (p->second); // dont play muted parts if (part->mute()) continue; EventList* events = part->events(); unsigned partTick = part->tick(); unsigned partLen = part->lenTick(); int delay = track->delay; if (cts > nts) { printf("processMidi: FATAL: cur > next %d > %d\n", cts, nts); return; } unsigned offset = delay + partTick; if (offset > nts) continue; unsigned stick = (offset > cts) ? 0 : cts - offset; unsigned etick = nts - offset; // By T356. Do not play events which are past the end of this part. if (etick > partLen) continue; iEvent ie = events->lower_bound(stick); iEvent iend = events->lower_bound(etick); for (; ie != iend; ++ie) { Event ev = ie->second; port = defaultPort; //Reset each loop // // dont play any meta events // if (ev.type() == Meta) continue; if (track->type() == Track::DRUM) { int instr = ev.pitch(); // ignore muted drums if (ev.isNote() && drumMap[instr].mute) continue; } unsigned tick = ev.tick() + offset; unsigned frame = tempomap.tick2frame(tick) + frameOffset; switch (ev.type()) { case Note: { int len = ev.lenTick(); int pitch = ev.pitch(); int velo = ev.velo(); if (track->type() == Track::DRUM) { // // Map drum-notes to the drum-map values // int instr = ev.pitch(); pitch = drumMap[instr].anote; port = drumMap[instr].port; //This changes to non-default port channel = drumMap[instr].channel; velo = int(double(velo) * (double(drumMap[instr].vol) / 100.0)); } else { // // transpose non drum notes // pitch += (track->transposition + song->globalPitchShift()); } if (pitch > 127) pitch = 127; if (pitch < 0) pitch = 0; velo += track->velocity; velo = (velo * track->compression) / 100; if (velo > 127) velo = 127; if (velo < 1) // no off event velo = 1; len = (len * track->len) / 100; if (len <= 0) // dont allow zero length len = 1; int veloOff = ev.veloOff(); if (port == defaultPort) { //printf("Adding event normally: frame=%d port=%d channel=%d pitch=%d velo=%d\n",frame, port, channel, pitch, velo); // p3.3.25 // If syncing to external midi sync, we cannot use the tempo map. // Therefore we cannot get sub-tick resolution. Just use ticks instead of frames. if (extSyncFlag.value()) playEvents->add(MidiPlayEvent(tick, port, channel, 0x90, pitch, velo)); else playEvents->add(MidiPlayEvent(frame, port, channel, 0x90, pitch, velo)); stuckNotes->add(MidiPlayEvent(tick + len, port, channel, veloOff ? 0x80 : 0x90, pitch, veloOff)); } else { //Handle events to different port than standard. MidiDevice* mdAlt = midiPorts[port].device(); if (mdAlt) { // p3.3.25 if (extSyncFlag.value()) mdAlt->playEvents()->add(MidiPlayEvent(tick, port, channel, 0x90, pitch, velo)); else mdAlt->playEvents()->add(MidiPlayEvent(frame, port, channel, 0x90, pitch, velo)); mdAlt->stuckNotes()->add(MidiPlayEvent(tick + len, port, channel, veloOff ? 0x80 : 0x90, pitch, veloOff)); } } if (velo > track->activity()) track->setActivity(velo); } break; // Added by T356. case Controller: { //int len = ev.lenTick(); //int pitch = ev.pitch(); if (track->type() == Track::DRUM) { int ctl = ev.dataA(); // Is it a drum controller event, according to the track port's instrument? MidiController *mc = midiPorts[defaultPort].drumController(ctl); if (mc) { int instr = ctl & 0x7f; ctl &= ~0xff; int pitch = drumMap[instr].anote & 0x7f; port = drumMap[instr].port; //This changes to non-default port channel = drumMap[instr].channel; MidiDevice* mdAlt = midiPorts[port].device(); if (mdAlt) { // p3.3.25 // If syncing to external midi sync, we cannot use the tempo map. // Therefore we cannot get sub-tick resolution. Just use ticks instead of frames. if (extSyncFlag.value()) mdAlt->playEvents()->add(MidiPlayEvent(tick, port, channel, ME_CONTROLLER, ctl | pitch, ev.dataB())); else //playEvents->add(MidiPlayEvent(frame, port, channel, ev)); mdAlt->playEvents()->add(MidiPlayEvent(frame, port, channel, ME_CONTROLLER, ctl | pitch, ev.dataB())); } break; } } // p3.3.25 if (extSyncFlag.value()) playEvents->add(MidiPlayEvent(tick, port, channel, ev)); else playEvents->add(MidiPlayEvent(frame, port, channel, ev)); } break; default: // p3.3.25 if (extSyncFlag.value()) playEvents->add(MidiPlayEvent(tick, port, channel, ev)); else playEvents->add(MidiPlayEvent(frame, port, channel, ev)); break; } } } }
void Audio::preloadControllers()/*{{{*/ { midiBusy = true; MidiTrackList* tracks = song->midis(); for (iMidiTrack it = tracks->begin(); it != tracks->end(); ++it) { MidiTrack* track = *it; //activePorts[track->outPort()] = true; QList<ProcessList*> pcevents; int port = track->outPort(); int channel = track->outChannel(); int defaultPort = port; MidiDevice* md = midiPorts[port].device(); if (!md) { continue; } MPEventList* playEvents = md->playEvents(); playEvents->erase(playEvents->begin(), playEvents->end()); PartList* pl = track->parts(); for (iPart p = pl->begin(); p != pl->end(); ++p) { MidiPart* part = (MidiPart*) (p->second); EventList* events = part->events(); unsigned partTick = part->tick(); //unsigned partLen = part->lenTick(); int delay = track->delay; unsigned offset = delay + partTick; for (iEvent ie = events->begin(); ie != events->end(); ++ie) { Event ev = ie->second; port = defaultPort; unsigned tick = ev.tick() + offset; //unsigned frame = tempomap.tick2frame(tick) + frameOffset; switch (ev.dataA()) { case CTRL_PROGRAM: { ProcessList *pl = new ProcessList; pl->port = port; pl->channel = channel; pl->dataB = ev.dataB(); bool addEvent = true; for(int i = 0; i < pcevents.size(); ++i) { ProcessList* ipl = pcevents.at(i); if(ipl->port == pl->port && ipl->channel == pl->channel && ipl->dataB == pl->dataB) { addEvent = false; break; } } if(addEvent) { printf("Audio::preloadControllers() Loading event @ tick: %d - on channel: %d - on port: %d - dataA: %d - dataB: %d\n", tick, channel, port, ev.dataA(), ev.dataB()); pcevents.append(pl); playEvents->add(MidiPlayEvent(tick, port, channel, ev)); } } break; default: break; } } } md->setNextPlayEvent(playEvents->begin()); } midiBusy = false; }/*}}}*/
MidiPlayEvent VstSynthIF::receiveEvent() { return MidiPlayEvent(); }