void LOS::importController(int channel, MidiPort* mport, int n) { MidiInstrument* instr = mport->instrument(); MidiCtrlValListList* vll = mport->controller(); iMidiCtrlValList i = vll->find(channel, n); if (i != vll->end()) return; // controller does already exist MidiController* ctrl = 0; MidiControllerList* mcl = instr->controller(); // printf("import Ctrl\n"); for (iMidiController i = mcl->begin(); i != mcl->end(); ++i) { MidiController* mc = i->second; int cn = mc->num(); // printf(" %x %x\n", n, cn); if (cn == n) { ctrl = mc; break; } // wildcard? if (((cn & 0xff) == 0xff) && ((cn & ~0xff) == (n & ~0xff))) { ctrl = i->second; break; } } if (ctrl == 0) { printf("controller 0x%x not defined for instrument %s, channel %d\n", n, instr->iname().toLatin1().constData(), channel); // TODO: register default Controller // MidiController* MidiPort::midiController(int num) const } MidiCtrlValList* newValList = new MidiCtrlValList(n); vll->add(channel, newValList); }
static void writeSeqConfiguration(int level, Xml& xml, bool writePortInfo)/*{{{*/ { xml.tag(level++, "sequencer"); if (writePortInfo) { // // write information about all midi ports, their assigned // instruments and all managed midi controllers // for (int i = 0; i < MIDI_PORTS; ++i) { bool used = false; MidiPort* mport = &midiPorts[i]; // Route check by Tim. Port can now be used for routing even if no device. // Also, check for other non-defaults and save port, to preserve settings even if no device. // Dont write the config for the global inputs list they will be auto created with each startup if (mport->defaultInChannels() || mport->defaultOutChannels() || (mport->instrument() && !mport->instrument()->iname().isEmpty() && mport->instrument()->iname() != "GM") /*|| !mport->syncInfo().isDefault()*/ ) { used = true; } else {//Put the ID of this track into a list MidiTrackList* tl = song->midis(); for (iMidiTrack it = tl->begin(); it != tl->end(); ++it) { MidiTrack* t = *it; if (t->outPort() == i) { used = true; break; } } } MidiDevice* dev = mport->device(); if (!used && !dev) continue; bool isGlobal = gInputListPorts.contains(mport->portno()); xml.tag(level++, "midiport portId=\"%lld\" isGlobalInput=\"%d\"", mport->id(), isGlobal); if (mport->defaultInChannels()) xml.intTag(level, "defaultInChans", mport->defaultInChannels()); if (mport->defaultOutChannels()) xml.intTag(level, "defaultOutChans", mport->defaultOutChannels()); if (mport->instrument() && !mport->instrument()->iname().isEmpty() && (mport->instrument()->iname() != "GM")) // FIXME: TODO: Make this user configurable. { xml.strTag(level, "instrument", mport->instrument()->iname()); } if (dev) { xml.strTag(level, "name", dev->name()); xml.intTag(level, "cacheNRPN", (int)dev->cacheNRPN()); if (dev->deviceType() != MidiDevice::ALSA_MIDI) xml.intTag(level, "type", dev->deviceType()); xml.intTag(level, "openFlags", dev->openFlags()); } // write out registered controller for all channels MidiCtrlValListList* vll = mport->controller(); for (int k = 0; k < MIDI_CHANNELS; ++k) { int min = k << 24; int max = min + 0x100000; xml.tag(level++, "channel idx=\"%d\"", k); iMidiCtrlValList s = vll->lower_bound(min); iMidiCtrlValList e = vll->lower_bound(max); if (s != e) { for (iMidiCtrlValList i = s; i != e; ++i) { if(i->second->num() != 262145) { xml.tag(level++, "controller id=\"%d\"", i->second->num()); if (i->second->hwVal() != CTRL_VAL_UNKNOWN) xml.intTag(level, "val", i->second->hwVal()); xml.etag(--level, "controller"); } } } xml.etag(--level, "channel"); } QList<PatchSequence*> *patchSequences = mport->patchSequences(); if (patchSequences && !patchSequences->isEmpty()) { for (int p = 0; p < patchSequences->size(); ++p) { PatchSequence* ps = patchSequences->at(p); QString pm = ps->name.replace('\n', " "); xml.put(level, "<patchSequence id=\"%d\" name=\"%s\" checked=\"%d\" />", ps->id, pm.toLatin1().constData(), ps->selected); } } if(!mport->presets()->isEmpty()) { QHashIterator<int, QString> iter(*mport->presets()); while(iter.hasNext()) { iter.next(); xml.put(level, "<midiPreset id=\"%d\" sysex=\"%s\"/>", iter.key(), iter.value().toLatin1().constData()); } } xml.etag(--level, "midiport"); } } xml.tag(--level, "/sequencer"); }/*}}}*/
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); } } }