void MidiPort::setMidiDevice(MidiDevice* dev) { // close old device if (_device) { _device->setPort(-1); _device->close(); } // set-up new device if (dev) { for (int i = 0; i < kMaxMidiPorts; ++i) { MidiPort* mp = &midiPorts[i]; if (mp->device() == dev) { // move device _state = mp->state(); mp->clearDevice(); break; } } _device = dev; _state = _device->open(); _device->setPort(portno()); } else // dev is null, clear this device clearDevice(); }
PyObject* getMidiControllerValue(PyObject*, PyObject* args) { const char* trackname; int ctrltype; if (!PyArg_ParseTuple(args, "si", &trackname, &ctrltype)) { return NULL; } Track* t = song->findTrack(QString(trackname)); if (t == NULL) return NULL; if (t->isMidiTrack() == false) { Py_INCREF(Py_None); return Py_None; } MidiTrack* track = (MidiTrack*) t; int channel = track->outChannel(); int outport = track->outPort(); MidiPort* mp = &midiPorts[outport]; if (mp == NULL) return Py_BuildValue("i", -1); int value = mp->hwCtrlState(channel, ctrltype); return Py_BuildValue("i", value); }
void addPortCtrlEvents(Event& event, Part* part, bool doClones)/*{{{*/ { // Traverse and process the clone chain ring until we arrive at the same part again. // The loop is a safety net. // Update: Due to the varying calls, and order of, incARefcount, (msg)ChangePart, replaceClone, and remove/addPortCtrlEvents, // we can not rely on the reference count as a safety net in these routines. We will just have to trust the clone chain. Part* p = part; //int j = doClones ? p->cevents()->arefCount() : 1; //if(j > 0) { //for(int i = 0; i < j; ++i) while (1) { // Added by Tim. p3.3.6 //printf("addPortCtrlEvents i:%d %s %p events %p refs:%d arefs:%d\n", i, p->name().toLatin1().constData(), p, part->cevents(), part->cevents()->refCount(), j); Track* t = p->track(); if (t) { MidiTrack* mt = (MidiTrack*) t; int port = mt->outPort(); //const EventList* el = p->cevents(); unsigned len = p->lenTick(); //for(ciEvent ie = el->begin(); ie != el->end(); ++ie) //{ //const Event& ev = ie->second; // Added by Tim. p3.3.6 //printf("addPortCtrlEvents %s len:%d end:%d etick:%d\n", p->name().toLatin1().constData(), p->lenTick(), p->endTick(), event.tick()); // Do not add events which are past the end of the part. if (event.tick() >= len) break; if (event.type() == Controller) { int ch = mt->outChannel(); int tck = event.tick() + p->tick(); int cntrl = event.dataA(); int val = event.dataB(); MidiPort* mp = &midiPorts[port]; mp->setControllerVal(ch, tck, cntrl, val, p); } //} } if (!doClones) break; // Get the next clone in the chain ring. p = p->nextClone(); // Same as original part? Finished. if (p == part) break; } } }/*}}}*/
void initMidiPorts() { //TODO: Remove the need for this code //We should populate the losMidiPort hash with ports as we create them for (int i = 0; i < kMaxMidiPorts; ++i) { MidiPort* port = &midiPorts[i]; ///port->setInstrument(genericMidiInstrument); port->setInstrument(registerMidiInstrument("GM")); // Changed by Tim. } }
void initMidiPorts() { //TODO: Remove the need for this code //We should populate the oomMidiPort hash with ports as we create them for (int i = 0; i < MIDI_PORTS; ++i) { MidiPort* port = &midiPorts[i]; ///port->setInstrument(genericMidiInstrument); port->setInstrument(registerMidiInstrument("GM")); // Changed by Tim. port->syncInfo().setPort(i); } }
void removePortCtrlEvents(Part* part, bool doClones)/*{{{*/ { // Traverse and process the clone chain ring until we arrive at the same part again. // The loop is a safety net. // Update: Due to the varying calls, and order of, incARefcount, (msg)ChangePart, replaceClone, and remove/addPortCtrlEvents, // we can not rely on the reference count as a safety net in these routines. We will just have to trust the clone chain. Part* p = part; //int j = doClones ? p->cevents()->arefCount() : 1; //if(j > 0) { //for(int i = 0; i < j; ++i) while (1) { MidiTrack* t = p->track(); if (t) { MidiTrack* mt = (MidiTrack*) t; int port = mt->outPort(); const EventList* el = p->cevents(); //unsigned len = p->lenTick(); for (ciEvent ie = el->begin(); ie != el->end(); ++ie) { const Event& ev = ie->second; // Added by T356. Do not remove events which are past the end of the part. // No, actually, do remove ALL of them belonging to the part. // Just in case there are stray values left after the part end. //if(ev.tick() >= len) // break; if (ev.type() == Controller) { int ch = mt->outChannel(); int tck = ev.tick() + p->tick(); int cntrl = ev.dataA(); MidiPort* mp = &midiPorts[port]; mp->deleteController(ch, tck, cntrl, p); } } } if (!doClones) break; // Get the next clone in the chain ring. p = p->nextClone(); // Same as original part? Finished. if (p == part) break; } } }/*}}}*/
static void readSeqConfiguration(Xml& xml) { //bool updatePorts = false; for (;;) { Xml::Token token = xml.parse(); if (token == Xml::Error || token == Xml::End) break; const QString& tag = xml.s1(); switch (token) { case Xml::TagStart: if (tag == "midiport") { //updatePorts = true; readConfigMidiPort(xml); } else if (tag == "rcStop") rcStopNote = xml.parseInt(); else if (tag == "rcEnable") rcEnable = xml.parseInt(); else if (tag == "rcRecord") rcRecordNote = xml.parseInt(); else if (tag == "rcGotoLeft") rcGotoLeftMarkNote = xml.parseInt(); else if (tag == "rcPlay") rcPlayNote = xml.parseInt(); else xml.skip(tag); break; case Xml::TagEnd: if (tag == "sequencer") { //All Midiports have been read so put all the unconfigured ports in the id list //if(updatePorts) //{ for(int i = 0; i < MIDI_PORTS; ++i) { MidiPort *mp = &midiPorts[i]; if(!losMidiPorts.contains(mp->id())) losMidiPorts.insert(mp->id(), mp); } //} return; } default: break; } } }
void MidiPortMenu::updateMenu() { MidiPort * mp = castModel<MidiPort>(); const MidiPort::Map & map = ( m_mode == MidiPort::Input ) ? mp->readablePorts() : mp->writablePorts(); clear(); for( MidiPort::Map::ConstIterator it = map.begin(); it != map.end(); ++it ) { QAction * a = addAction( it.key() ); a->setCheckable( true ); a->setChecked( it.value() ); } }
int getFreeMidiPort()/*{{{*/ { int rv = -1; for (int i = 0; i < MIDI_PORTS; ++i) { MidiPort* mp = &midiPorts[i]; //Use the first unconfigured port if (!mp->device()) { rv = i; break; } } return rv; }/*}}}*/
//Midi Port Route Route::Route(int port, int ch) { track = 0; midiPort = port; channel = ch; channels = -1; remoteChannel = -1; type = MIDI_PORT_ROUTE; trackId = -1; if(midiPort >= 0 && midiPort < MIDI_PORTS) { MidiPort* mp = &midiPorts[midiPort]; midiPortId = mp->id(); } }
QMenu* midiPortsPopup(QWidget* parent, int checkPort) { QMenu* p = new QMenu(parent); for (int i = 0; i < MIDI_PORTS; ++i) { MidiPort* port = &midiPorts[i]; QString name; name.sprintf("%d:%s", port->portno() + 1, port->portname().toLatin1().constData()); QAction *act = p->addAction(name); act->setData(i); if (i == checkPort) act->setChecked(true); } return p; }
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); } } }
void MidiInstrument::reset(int portNo, MidiType) { MidiPort* port = &midiPorts[portNo]; MidiPlayEvent ev; ev.setType(0x90); ev.setPort(portNo); ev.setTime(0); for (int chan = 0; chan < kMaxMidiChannels; ++chan) { ev.setChannel(chan); for (int pitch = 0; pitch < 128; ++pitch) { ev.setA(pitch); ev.setB(0); port->sendEvent(ev); } } }
void MidiInstrument::reset(int portNo, MType) { 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.setChannel(chan); for (int pitch = 0; pitch < 128; ++pitch) { ev.setA(pitch); ev.setB(0); port->sendEvent(ev); } } }
void MidiPort::setMidiDevice(MidiDevice* dev) { // close old device if (_device) { if (_device->isSynthPlugin()) { _instrument = genericMidiInstrument; } _device->setPort(-1); _device->close(); } // set-up new device if (dev) { for (int i = 0; i < MIDI_PORTS; ++i) { MidiPort* mp = &midiPorts[i]; if (mp->device() == dev) { if (dev->isSynthPlugin()) mp->setInstrument(genericMidiInstrument); // move device _state = mp->state(); mp->clearDevice(); break; } } _device = dev; if (_device->isSynthPlugin()) { SynthPluginDevice* s = (SynthPluginDevice*) _device; _instrument = s; //_instrument = genericMidiInstrument; } _state = _device->open(); _device->setPort(portno()); } else // dev is null, clear this device clearDevice(); }
//Route From settings //Used mostly for restoring routes from the xml file Route::Route(const QString& s, bool dst, int ch, int rtype) { trackId = -1; midiPort = -1; midiPortId = -1; Route node(name2route(s, dst, rtype)); channel = node.channel; if (channel == -1) channel = ch; channels = node.channels; remoteChannel = node.remoteChannel; type = node.type; if (type == TRACK_ROUTE) { track = node.track; if(track) trackId = track->id(); } else if (type == JACK_ROUTE) { jackPort = node.jackPort; } else if (type == MIDI_DEVICE_ROUTE) { device = node.device; } else if (type == MIDI_PORT_ROUTE) { track = 0; midiPort = node.midiPort; if(midiPort >= 0 && midiPort < MIDI_PORTS) { MidiPort* mp = &midiPorts[midiPort]; midiPortId = mp->id(); } } }
static void readController(Xml& xml, int midiPort, int channel) { int id = 0; int val = CTRL_VAL_UNKNOWN; for (;;) { Xml::Token token = xml.parse(); QString tag = xml.s1(); switch (token) { case Xml::TagStart: if (tag == "val") val = xml.parseInt(); else xml.skip(tag); break; case Xml::Attribut: if (tag == "id") id = xml.s2().toInt(); break; case Xml::TagEnd: if (tag == "controller") { MidiPort* port = &midiPorts[midiPort]; //port->addManagedController(channel, id); val = port->limitValToInstrCtlRange(id, val); // The value here will actually be sent to the device LATER, in MidiPort::setMidiDevice() port->setHwCtrlState(channel, id, val); return; } default: return; } } }
bool LOS::importMidi(const QString name, bool merge)/*{{{*/ { bool popenFlag; FILE* fp = fileOpen(this, name, QString(".mid"), "r", popenFlag); if (fp == 0) return true; MidiFile mf(fp); bool rv = mf.read(); popenFlag ? pclose(fp) : fclose(fp); if (rv) { QString s(tr("reading midifile\n ")); s += name; s += tr("\nfailed: "); s += mf.error(); QMessageBox::critical(this, QString("LOS"), s); return rv; } // // evaluate song Type (GM, XG, GS, unknown) // MidiType t = song->midiType(); if (!merge) { t = mf.midiType(); song->setMidiType(t); } MidiInstrument* instr = 0; for (iMidiInstrument i = midiInstruments.begin(); i != midiInstruments.end(); ++i) { MidiInstrument* mi = *i; if ((mi->iname() == "GM" && ((t == MIDI_TYPE_NULL) || (t == MIDI_TYPE_GM))) || (mi->iname() == "GS" && t == MIDI_TYPE_GS) || (mi->iname() == "XG" && t == MIDI_TYPE_XG)) { instr = mi; break; } } if (instr == 0) { // the standard instrument files (GM, GS, XG) must be present printf("no instrument, type %d\n", t); return true; //abort(); } MidiFileTrackList* etl = mf.trackList(); int division = mf.division(); // // create MidiTrack and copy events to ->events() // - combine note on/off events // - calculate tick value for internal resolution // int mPort = getFreeMidiPort(); for (iMidiFileTrack t = etl->begin(); t != etl->end(); ++t) { MPEventList* el = &((*t)->events); if (el->empty()) continue; // // if we split the track, SYSEX and META events go into // the first target track bool first = true; QList<QPair<int, int> > portChannelList; iMPEvent i; for(i = el->begin(); i != el->end(); i++) { if (i->type() != ME_SYSEX && i->type() != ME_META) { int chan = i->channel(); int port = i->port(); if(portChannelList.isEmpty() || !portChannelList.contains(qMakePair(chan, port))) { portChannelList.append(qMakePair(chan, port)); MidiTrack* track = new MidiTrack(); track->setDefaultName(); track->setMasterFlag(true); if(config.partColorNames[lastTrackPartColorIndex].contains("menu:", Qt::CaseSensitive)) lastTrackPartColorIndex ++; track->setDefaultPartColor(lastTrackPartColorIndex); lastTrackPartColorIndex ++; if(lastTrackPartColorIndex == NUM_PARTCOLORS) lastTrackPartColorIndex = 1; //Set track channel so buildMidiEventList can match the event to a channel track->setOutChannel(chan); track->setOutPort(mPort); MidiPort* mport = &midiPorts[track->outPort()]; // this overwrites any instrument set for this port: mport->setInstrument(instr); EventList* mel = track->events(); buildMidiEventList(mel, el, track, division, first, false, false); first = false; processTrack(track); //Update track to channel 1 //99% of all midi we import will be alien to our setup anyway, //so I'm making it easy for the user to just set the Instrument and go track->setOutChannel(0); song->insertTrack(track, -1); mPort++; //FIXME: Provice a non-iterative way to do this using the new losMidiPorts hash //Or maintain a list of configured or inuse ports while((&midiPorts[mPort])->device() && mPort < kMaxMidiPorts) mPort++;//Just incase we have a configured port after an empty one } } } if (first) { // // track does only contain non-channel messages // (SYSEX or META) // MidiTrack* track = new MidiTrack(); track->setDefaultName(); track->setMasterFlag(true); track->setOutChannel(0); track->setOutPort(mPort); if(config.partColorNames[lastTrackPartColorIndex].contains("menu:", Qt::CaseSensitive)) lastTrackPartColorIndex ++; track->setDefaultPartColor(lastTrackPartColorIndex); lastTrackPartColorIndex ++; if(lastTrackPartColorIndex == NUM_PARTCOLORS) lastTrackPartColorIndex = 1; EventList* mel = track->events(); // Do SysexMeta. Don't do loops. // TODO: Properly support sysex dumps buildMidiEventList(mel, el, track, division, true, false, false); processTrack(track); song->insertTrack(track, -1); mPort++; while((&midiPorts[mPort])->device() && mPort < kMaxMidiPorts) mPort++; } } if (!merge) { MidiTrackList* tl = song->tracks(); if (!tl->empty()) { MidiTrack* track = tl->front(); track->setSelected(true); } song->initLen(); int z, n; sigmap.timesig(0, z, n); int tempo = tempomap.tempo(0); transport->setTimesig(z, n); transport->setTempo(tempo); bool masterF = !tempomap.empty(); song->setMasterFlag(masterF); transport->setMasterFlag(masterF); song->updatePos(); composer->reset(); } else { song->initLen(); } return false; }/*}}}*/
void MPConfig::rbClicked(QTableWidgetItem* item) { if (item == 0) return; QString id = item->tableWidget()->item(item->row(), DEVCOL_NO)->text(); int no = atoi(id.toLatin1().constData()) - 1; if (no < 0 || no >= kMaxMidiPorts) return; int n; MidiPort* port = &midiPorts[no]; MidiDevice* dev = port->device(); int rwFlags = dev ? dev->rwFlags() : 0; int openFlags = dev ? dev->openFlags() : 0; QTableWidget* listView = item->tableWidget(); QPoint ppt = listView->visualItemRect(item).bottomLeft(); QPoint mousepos = QCursor::pos(); int col = item->column(); ppt += QPoint(0, listView->horizontalHeader()->height()); ppt = listView->mapToGlobal(ppt); switch (col) { case DEVCOL_GUI: if (dev == 0) //break; return; // falkTX, we don't want this in the connections manager //if (port->hasGui()) //{ // port->instrument()->showGui(!port->guiVisible()); // item->setIcon(port->guiVisible() ? QIcon(*dotIcon) : QIcon(*dothIcon)); //} //break; return; case DEVCOL_CACHE_NRPN: if (!dev) return; dev->setCacheNRPN(!dev->cacheNRPN()); item->setIcon(dev->cacheNRPN() ? QIcon(*dotIcon) : QIcon(*dothIcon)); return; case DEVCOL_REC: if (dev == 0 || !(rwFlags & 2)) return; openFlags ^= 0x2; dev->setOpenFlags(openFlags); midiSeq->msgSetMidiDevice(port, dev); // reopen device item->setIcon(openFlags & 2 ? QIcon(*dotIcon) : QIcon(*dothIcon)); if (dev->deviceType() == MidiDevice::JACK_MIDI) { if (dev->openFlags() & 2) { item->tableWidget()->item(item->row(), DEVCOL_INROUTES)->setText(tr("in")); } else { item->tableWidget()->item(item->row(), DEVCOL_INROUTES)->setText(""); } } return; case DEVCOL_PLAY: if (dev == 0 || !(rwFlags & 1)) return; openFlags ^= 0x1; dev->setOpenFlags(openFlags); midiSeq->msgSetMidiDevice(port, dev); // reopen device item->setIcon(openFlags & 1 ? QIcon(*dotIcon) : QIcon(*dothIcon)); if (dev->deviceType() == MidiDevice::JACK_MIDI) { if (dev->openFlags() & 1) { item->tableWidget()->item(item->row(), DEVCOL_OUTROUTES)->setText(tr("out")); } else { item->tableWidget()->item(item->row(), DEVCOL_OUTROUTES)->setText(""); } } return; case DEVCOL_INROUTES: case DEVCOL_OUTROUTES: { if (!checkAudioDevice()) return; if (audioDevice->deviceType() != AudioDevice::JACK_AUDIO) //Only if Jack is running. return; if (!dev) return; // Only Jack midi devices. if (dev->deviceType() != MidiDevice::JACK_MIDI) return; if (!(dev->openFlags() & ((col == DEVCOL_OUTROUTES) ? 1 : 2))) return; RouteList* rl = (col == DEVCOL_OUTROUTES) ? dev->outRoutes() : dev->inRoutes(); // p3.3.55 QMenu* pup = 0; int gid = 0; std::list<QString> sl; pup = new QMenu(this); //A temporary Route to us for matching later QString currentRoute(""); bool routeSelected = false; _redisplay: pup->clear(); gid = 0; // Jack input ports if device is writable, and jack output ports if device is readable. sl = (col == DEVCOL_OUTROUTES) ? audioDevice->inputPorts(true, _showAliases) : audioDevice->outputPorts(true, _showAliases); QAction* act; act = pup->addAction(tr("Show first aliases")); act->setData(gid); act->setCheckable(true); act->setChecked(_showAliases == 0); ++gid; act = pup->addAction(tr("Show second aliases")); act->setData(gid); act->setCheckable(true); act->setChecked(_showAliases == 1); ++gid; pup->addSeparator(); for (std::list<QString>::iterator ip = sl.begin(); ip != sl.end(); ++ip) { act = pup->addAction(*ip); act->setData(gid); act->setCheckable(true); Route rt(*ip, (col == DEVCOL_OUTROUTES), -1, Route::JACK_ROUTE); for (iRoute ir = rl->begin(); ir != rl->end(); ++ir) { if (*ir == rt) { currentRoute = (*ir).name(); act->setChecked(true); routeSelected = true; break; } } ++gid; } act = pup->exec(ppt); if (act) { n = act->data().toInt(); if (n == 0) // Show first aliases { if (_showAliases == 0) _showAliases = -1; else _showAliases = 0; goto _redisplay; // Go back } else if (n == 1) // Show second aliases { if (_showAliases == 1) _showAliases = -1; else _showAliases = 1; goto _redisplay; // Go back } QString s(act->text()); if (col == DEVCOL_OUTROUTES) // Writable { Route srcRoute(dev, -1); Route dstRoute(s, true, -1, Route::JACK_ROUTE); if(routeSelected && currentRoute == s) { //it will alread be handled by an unchecked operation routeSelected = false; } iRoute iir = rl->begin(); for (; iir != rl->end(); ++iir) { if (*iir == dstRoute) break; } if (iir != rl->end()) { // disconnect audio->msgRemoveRoute(srcRoute, dstRoute); } else { // connect audio->msgAddRoute(srcRoute, dstRoute); } if(routeSelected) { iRoute selr = rl->begin(); for (; selr != rl->end(); ++selr) { //clean up the routing list as something was selected that was not the current so delete the old route if((*selr).name() == currentRoute) { audio->msgRemoveRoute(srcRoute, (*selr)); break; } } } } else { Route srcRoute(s, false, -1, Route::JACK_ROUTE); Route dstRoute(dev, -1); iRoute iir = rl->begin(); for (; iir != rl->end(); ++iir) { if (*iir == srcRoute) break; } if (iir != rl->end()) // disconnect audio->msgRemoveRoute(srcRoute, dstRoute); else // connect audio->msgAddRoute(srcRoute, dstRoute); } audio->msgUpdateSoloStates(); song->update(SC_ROUTE); } delete pup; } return; case DEVCOL_DEF_IN_CHANS: case DEVCOL_DEF_OUT_CHANS: { } //break; return; case DEVCOL_NAME: { //printf("MPConfig::rbClicked DEVCOL_NAME\n"); // Did we click in the text area? // NOTE: this needs the +15 pixels to make up for padding in the stylesheet. if ((mousepos.x() - (ppt.x() + 15)) > buttondownIcon->width()) { // Start the renaming of the cell... QModelIndex current = item->tableWidget()->currentIndex(); if (item->flags() & Qt::ItemIsEditable) item->tableWidget()->edit(current.sibling(current.row(), DEVCOL_NAME)); return; } else {// We clicked the 'down' button. QMenu* pup = new QMenu(this); QAction* act; act = pup->addAction(tr("Create") + QT_TRANSLATE_NOOP("@default", " Jack") + tr(" device")); act->setData(0); typedef std::map<std::string, int > asmap; typedef std::map<std::string, int >::iterator imap; asmap mapALSA; asmap mapJACK; int aix = 0x10000000; int jix = 0x20000000; for (iMidiDevice i = midiDevices.begin(); i != midiDevices.end(); ++i) { if ((*i)->deviceType() == MidiDevice::ALSA_MIDI) { mapALSA.insert(std::pair<std::string, int> (std::string((*i)->name().toLatin1().constData()), aix)); ++aix; } else if ((*i)->deviceType() == MidiDevice::JACK_MIDI) { mapJACK.insert(std::pair<std::string, int> (std::string((*i)->name().toLatin1().constData()), jix)); ++jix; } else printf("MPConfig::rbClicked unknown midi device: %s\n", (*i)->name().toLatin1().constData()); } pup->addSeparator(); pup->addAction(new MenuTitleItem(QT_TRANSLATE_NOOP("@default", "ALSA:"), pup)); for (imap i = mapALSA.begin(); i != mapALSA.end(); ++i) { int idx = i->second; QString s(i->first.c_str()); MidiDevice* md = midiDevices.find(s, MidiDevice::ALSA_MIDI); if (md) { if (md->deviceType() != MidiDevice::ALSA_MIDI) continue; act = pup->addAction(QT_TRANSLATE_NOOP("@default", md->name())); act->setData(idx); act->setCheckable(true); act->setChecked(md == dev); } } pup->addSeparator(); pup->addAction(new MenuTitleItem(QT_TRANSLATE_NOOP("@default", "JACK:"), pup)); for (imap i = mapJACK.begin(); i != mapJACK.end(); ++i) { int idx = i->second; QString s(i->first.c_str()); MidiDevice* md = midiDevices.find(s, MidiDevice::JACK_MIDI); if (md) { if (md->deviceType() != MidiDevice::JACK_MIDI) continue; act = pup->addAction(QT_TRANSLATE_NOOP("@default", md->name())); act->setData(idx); act->setCheckable(true); act->setChecked(md == dev); } } act = pup->exec(ppt); if (!act) { delete pup; return; } n = act->data().toInt(); //printf("MPConfig::rbClicked n:%d\n", n); MidiDevice* sdev = 0; if (n < 0x10000000) { delete pup; if (n <= 2) { sdev = MidiJackDevice::createJackMidiDevice(); if (sdev) { int of = 3; switch (n) { case 0: of = 0; //3; Set the open flags of the midiDevice this should be off by default break; case 1: of = 2; break; case 2: of = 1; break; } sdev->setOpenFlags(of); } } } else { int typ; if (n < 0x20000000) typ = MidiDevice::ALSA_MIDI; else if (n < 0x30000000) typ = MidiDevice::JACK_MIDI; else typ = MidiDevice::UNKNOWN_MIDI; sdev = midiDevices.find(act->text(), typ); delete pup; // Is it the current device? Reset it to <none>. // falkTX, handle synths properly here if (sdev == dev) { sdev = 0; } else { } } midiSeq->msgSetMidiDevice(port, sdev); los->changeConfig(true); // save configuration file song->update(); } } //break; return; case DEVCOL_INSTR: { if (instrPopup == 0) instrPopup = new QMenu(this); instrPopup->clear(); for (iMidiInstrument i = midiInstruments.begin(); i != midiInstruments.end(); ++i) { instrPopup->addAction((*i)->iname()); } QAction* act = instrPopup->exec(ppt, 0); if (!act) return; QString s = act->text(); item->tableWidget()->item(item->row(), DEVCOL_INSTR)->setText(s); for (iMidiInstrument i = midiInstruments.begin(); i != midiInstruments.end(); ++i) { if ((*i)->iname() == s) { port->setInstrument(*i); break; } } song->update(); } return; } }
void MPConfig::songChanged(int flags) { // Is it simply a midi controller value adjustment? Forget it. if (flags == SC_MIDI_CONTROLLER) return; // Get currently selected index... int no = -1; QTableWidgetItem* sitem = mdevView->currentItem(); if (sitem) { QString id = sitem->tableWidget()->item(sitem->row(), DEVCOL_NO)->text(); no = atoi(id.toLatin1().constData()) - 1; if (no < 0 || no >= kMaxMidiPorts) no = -1; } sitem = 0; for (int i = kMaxMidiPorts - 1; i >= 0; --i) { mdevView->blockSignals(true); // otherwise itemChanged() is triggered and bad things happen. MidiPort* port = &midiPorts[i]; MidiDevice* dev = port->device(); QString s; s.setNum(i + 1); QTableWidgetItem* itemno = mdevView->item(i, DEVCOL_NO); QTableWidgetItem* itemstate = mdevView->item(i, DEVCOL_STATE); itemstate->setText(port->state()); QTableWidgetItem* iteminstr = mdevView->item(i, DEVCOL_INSTR); QString instrumentName = port->instrument() ? port->instrument()->iname() : tr("<unknown>"); iteminstr->setText(instrumentName); iteminstr->setToolTip(instrumentName); QTableWidgetItem* itemname = mdevView->item(i, DEVCOL_NAME); QTableWidgetItem* itemgui = mdevView->item(i, DEVCOL_GUI); QTableWidgetItem* itemfb = mdevView->item(i, DEVCOL_CACHE_NRPN); QTableWidgetItem* itemrec = mdevView->item(i, DEVCOL_REC); QTableWidgetItem* itemplay = mdevView->item(i, DEVCOL_PLAY); QTableWidgetItem* itemout = mdevView->item(i, DEVCOL_OUTROUTES); QTableWidgetItem* itemin = mdevView->item(i, DEVCOL_INROUTES); QTableWidgetItem* itemdefin = mdevView->item(i, DEVCOL_DEF_IN_CHANS); itemdefin->setText(bitmap2String(port->defaultInChannels())); QTableWidgetItem* itemdefout = mdevView->item(i, DEVCOL_DEF_OUT_CHANS); itemdefout->setText(bitmap2String(port->defaultOutChannels())); mdevView->blockSignals(false); if (dev) { itemname->setText(dev->name()); itemname->setToolTip(dev->name()); // Is it a Jack midi device? Allow renaming. //if(dynamic_cast<MidiJackDevice*>(dev)) if (dev->deviceType() == MidiDevice::JACK_MIDI) itemname->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled); if (dev->rwFlags() & 0x2) { itemrec->setIcon(dev->openFlags() & 2 ? QIcon(*dotIcon) : QIcon(*dothIcon)); } else { itemrec->setIcon(QIcon(QPixmap())); } if (dev->rwFlags() & 0x1) { itemplay->setIcon(dev->openFlags() & 1 ? QIcon(*dotIcon) : QIcon(*dothIcon)); } else itemplay->setIcon(QIcon(QPixmap())); itemfb->setIcon(dev->cacheNRPN() ? QIcon(*dotIcon) : QIcon(*dothIcon)); } else { itemname->setText(tr("<none>")); itemname->setToolTip(""); itemgui->setIcon(QIcon(*dothIcon)); itemrec->setIcon(QIcon(QPixmap())); itemplay->setIcon(QIcon(QPixmap())); itemfb->setIcon(QIcon(QPixmap())); } // falkTX, we don't want this in the connections manager //if (port->hasGui()) //{ // itemgui->setIcon(port->guiVisible() ? QIcon(*dotIcon) : QIcon(*dothIcon)); //} //else //{ itemgui->setIcon(QIcon(QPixmap())); //} iteminstr->setIcon(QIcon(*buttondownIcon)); itemname->setIcon(QIcon(*buttondownIcon)); //if(dev && dynamic_cast<MidiJackDevice*>(dev)) if (dev && dev->deviceType() == MidiDevice::JACK_MIDI) { //item->setPixmap(DEVCOL_ROUTES, *buttondownIcon); //item->setText(DEVCOL_ROUTES, tr("routes")); // p3.3.55 if (dev->rwFlags() & 1) //if(dev->openFlags() & 1) { itemout->setIcon(QIcon(*buttondownIcon)); if (port->device() && !port->device()->outRoutes()->empty()) { RouteList* list = port->device()->outRoutes(); if (!list->empty()) { iRoute r = list->begin(); itemout->setText(r->name()); } } else { itemout->setText(tr("out")); } //if (dev->openFlags() & 1) // itemout->setText(tr("out")); } if (dev->rwFlags() & 2) //if(dev->openFlags() & 2) { itemin->setIcon(QIcon(*buttondownIcon)); if (dev->openFlags() & 2) itemin->setText(tr("in")); } } if (i == no) sitem = itemno; } if (sitem) { mdevView->setCurrentItem(sitem); } }
void Route::read(Xml& xml)/*{{{*/ { QString s; int dtype = MidiDevice::ALSA_MIDI; int port = -1; unsigned char rtype = Route::TRACK_ROUTE; for (;;) { const QString& tag = xml.s1(); Xml::Token token = xml.parse(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::Attribut: #ifdef ROUTE_DEBUG printf("Route::read(): attribute:%s\n", tag.toLatin1().constData()); #endif if (tag == "type") rtype = xml.s2().toInt(); else if (tag == "devtype") { dtype = xml.s2().toInt(); rtype = Route::MIDI_DEVICE_ROUTE; } else if (tag == "name") s = xml.s2(); else if(tag == "trackId") { trackId = xml.s2().toLongLong(); rtype = Route::TRACK_ROUTE; } else if (tag == "mport") // p3.3.49 { port = xml.s2().toInt(); rtype = Route::MIDI_PORT_ROUTE; } else if(tag == "mportId") { midiPortId = xml.s2().toLongLong(); rtype = Route::MIDI_PORT_ROUTE; } else printf("Route::read(): unknown attribute:%s\n", tag.toLatin1().constData()); break; case Xml::TagEnd: #ifdef ROUTE_DEBUG printf("Route::read(): tag end type:%d channel:%d name:%s\n", rtype, channel, s.toLatin1().constData()); #endif if (rtype == MIDI_PORT_ROUTE) { if(midiPortId > 0) { //qDebug("Route::read(): MIDI_PORT_ROUTE Finding midiport from id"); type = rtype; MidiPort *mp = oomMidiPorts.value(midiPortId); if(mp) { midiPort = mp->portno(); //qDebug("Route::read(): Found midiport from id: %d", midiPort); } } else if (port >= 0 && port < MIDI_PORTS) { type = rtype; midiPort = port; MidiPort *mp = &midiPorts[midiPort]; midiPortId = mp->id(); } else printf("Route::read(): midi port <%d> out of range\n", port); } else if (!s.isEmpty()) { if (rtype == TRACK_ROUTE) { if(trackId > 0) { track = song->findTrackById(trackId); type = rtype; } else { TrackList* tl = song->tracks(); iTrack i = tl->begin(); for (; i != tl->end(); ++i) { Track* t = *i; if (t->name() == s) { track = t; type = rtype; trackId = t->id(); break; } } if (i == tl->end()) printf("Route::read(): track <%s> not found\n", s.toLatin1().constData()); } } else if (rtype == JACK_ROUTE) { void* jport = 0; if (audioDevice) //Fix from Robert at muse jport = audioDevice->findPort(s.toLatin1().constData()); if (jport == 0) printf("Route::read(): jack port <%s> not found audioDevice(%p)\n", s.toLatin1().constData(), audioDevice); else { jackPort = jport; type = rtype; } } else if (rtype == MIDI_DEVICE_ROUTE) { iMidiDevice imd = midiDevices.begin(); for (; imd != midiDevices.end(); ++imd) { MidiDevice* md = *imd; if (md->name() == s && md->deviceType() == dtype) { // We found a device, but if it is not in use by the song (port is -1), ignore it. // This prevents loading and propagation of bogus routes in the oom file. if (md->midiPort() == -1) break; device = md; type = rtype; break; } } if (imd == midiDevices.end()) printf("Route::read(): midi device <%s> not found\n", s.toLatin1().constData()); } } return; default: break; } } }/*}}}*/
void MidiDevice::handleStop() { // If the device is not in use by a port, don't bother it. if(_port == -1) return; MidiPort* mp = &MusEGlobal::midiPorts[_port]; //--------------------------------------------------- // send midi 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()) { // Shall we check open flags? DELETETHIS 4? //if(!(dev->rwFlags() & 0x1) || !(dev->openFlags() & 1)) //if(!(dev->openFlags() & 1)) // return; MidiSyncInfo& si = mp->syncInfo(); if(si.MMCOut()) mp->sendMMCStop(); if(si.MRTOut()) { mp->sendStop(); //DELETETHIS 5? // Added check of option send continue not start. Hmm, is this required? Seems to make other devices unhappy. // (Could try now that this is in MidiDevice.) //if(!si.sendContNotStart()) // mp->sendSongpos(MusEGlobal::audio->tickPos() * 4 / MusEGlobal::config.division); } } //--------------------------------------------------- // Clear all notes and flush out any stuck notes // which were put directly to the device //--------------------------------------------------- setStopFlag(true); for(iMPEvent i = _stuckNotes.begin(); i != _stuckNotes.end(); ++i) { MidiPlayEvent ev(*i); ev.setTime(0); // Immediate processing. TODO Use curFrame? //ev.setTime(MusEGlobal::audio->midiQueueTimeStamp(ev.time())); putEvent(ev, MidiDevice::NotLate); } _stuckNotes.clear(); //------------------------------------------------------------ // Flush out any track-related playback stuck notes (NOT 'live' notes) // which were not put directly to the device //------------------------------------------------------------ for(ciMidiTrack imt = MusEGlobal::song->midis()->begin(); imt != MusEGlobal::song->midis()->end(); ++imt) { 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); // Immediate processing. TODO Use curFrame? //ev.setTime(MusEGlobal::audio->midiQueueTimeStamp(ev.time())); putEvent(ev, MidiDevice::NotLate); mel.erase(i); } } //--------------------------------------------------- // reset sustain //--------------------------------------------------- for(int ch = 0; ch < MusECore::MUSE_MIDI_CHANNELS; ++ch) { if(mp->hwCtrlState(ch, CTRL_SUSTAIN) == 127) { MidiPlayEvent ev(0, _port, ch, ME_CONTROLLER, CTRL_SUSTAIN, 0); // Immediate processing. TODO Use curFrame? //ev.setTime(MusEGlobal::audio->midiQueueTimeStamp(ev.time())); putEvent(ev, MidiDevice::NotLate); } } }
void removeRoute(Route src, Route dst)/*{{{*/ { //TODO: Add hooks to update the patchcanvas on success //This should be conditionally checked so we dont update if the canvas is what made the connection if (src.type == Route::JACK_ROUTE) { if (!dst.isValid()) { printf("removeRoute: source is jack, invalid destination\n"); return; } if (dst.type == Route::TRACK_ROUTE) { if (dst.track->type() != Track::AUDIO_INPUT) { fprintf(stderr, "removeRoute: source is jack, destination is track but not audio input\n"); // exit(-1); return; } RouteList* inRoutes = dst.track->inRoutes(); iRoute i; for (i = inRoutes->begin(); i != inRoutes->end(); ++i) { if (*i == src) { inRoutes->erase(i); break; } } } else if (dst.type == Route::MIDI_DEVICE_ROUTE) { RouteList* routes = dst.device->inRoutes(); iRoute i; for (i = routes->begin(); i != routes->end(); ++i) { if (*i == src) { routes->erase(i); break; } } } else { fprintf(stderr, "removeRoute: source is jack, destination unknown\n"); return; } } else if (dst.type == Route::JACK_ROUTE) { if (!src.isValid()) { printf("removeRoute: destination is jack, invalid source\n"); return; } if (src.type == Route::TRACK_ROUTE) { if (src.track->type() != Track::AUDIO_OUTPUT) { fprintf(stderr, "removeRoute: destination is jack, source is track but not audio output\n"); return; } RouteList* outRoutes = src.track->outRoutes(); iRoute i; for (i = outRoutes->begin(); i != outRoutes->end(); ++i) { if (*i == dst) { outRoutes->erase(i); break; } } } else if (src.type == Route::MIDI_DEVICE_ROUTE) { RouteList* routes = src.device->outRoutes(); iRoute i; for (i = routes->begin(); i != routes->end(); ++i) { if (*i == dst) { routes->erase(i); break; } } } else { fprintf(stderr, "removeRoute: destination is jack, source unknown\n"); // exit(-1); return; } } else if (src.type == Route::MIDI_PORT_ROUTE) // p3.3.49 { if (dst.type != Route::TRACK_ROUTE) { fprintf(stderr, "removeRoute: source is midi port:%d, but destination is not track\n", src.midiPort); return; } if (src.isValid()) { MidiPort *mp = &midiPorts[src.midiPort]; RouteList* outRoutes = mp->outRoutes(); for (iRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) { //if (*i == dst) if (i->type == Route::TRACK_ROUTE && i->track == dst.track) // p3.3.50 Is there a route to the track? { i->channel &= ~dst.channel; // p3.3.50 Unset the desired channel bits. if (i->channel == 0) // Only if there are no channel bits set, erase the route. outRoutes->erase(i); break; // For safety, keep looking and remove any more found. // No, must break, else crash. There should only be one route anyway... } } } else printf("removeRoute: source is midi port:%d but invalid\n", src.midiPort); if (dst.isValid()) { RouteList* inRoutes = dst.track->inRoutes(); for (iRoute i = inRoutes->begin(); i != inRoutes->end(); ++i) { if (i->type == Route::MIDI_PORT_ROUTE && i->midiPort == src.midiPort) // p3.3.50 Is there a route to the midi port? { i->channel &= ~src.channel; // p3.3.50 Unset the desired channel bits. if (i->channel == 0) // Only if there are no channel bits set, erase the route. inRoutes->erase(i); break; // For safety, keep looking and remove any more found. // No, must break, else crash. There should only be one route anyway... } } } else printf("removeRoute: source is midi port:%d but destination track invalid\n", src.midiPort); } else if (dst.type == Route::MIDI_PORT_ROUTE) // p3.3.49 { if (src.type != Route::TRACK_ROUTE) { fprintf(stderr, "removeRoute: destination is midi port:%d, but source is not track\n", dst.midiPort); return; } if (src.isValid()) { RouteList* outRoutes = src.track->outRoutes(); for (iRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) { if (i->type == Route::MIDI_PORT_ROUTE && i->midiPort == dst.midiPort) // p3.3.50 Is there a route to the midi port? { i->channel &= ~dst.channel; // p3.3.50 Unset the desired channel bits. if (i->channel == 0) // Only if there are no channel bits set, erase the route. outRoutes->erase(i); break; // For safety, keep looking and remove any more found. // No, must break, else crash. There should only be one route anyway... } } } else printf("removeRoute: destination is midi port:%d but source track is invalid\n", dst.midiPort); if (dst.isValid()) { MidiPort *mp = &midiPorts[src.midiPort]; RouteList* inRoutes = mp->inRoutes(); for (iRoute i = inRoutes->begin(); i != inRoutes->end(); ++i) { if (i->type == Route::TRACK_ROUTE && i->track == src.track) // p3.3.50 Is there a route to the track? { i->channel &= ~src.channel; // p3.3.50 Unset the desired channel bits. if (i->channel == 0) // Only if there are no channel bits set, erase the route. inRoutes->erase(i); break; // For safety, keep looking and remove any more found. // No, must break, else crash. There should only be one route anyway... } } } else printf("removeRoute: destination is midi port:%d but invalid\n", dst.midiPort); } else { if (src.type != Route::TRACK_ROUTE || dst.type != Route::TRACK_ROUTE) // p3.3.49 { fprintf(stderr, "removeRoute: source and destination are not tracks\n"); return; } if (src.isValid()) { RouteList* outRoutes = src.track->outRoutes(); for (iRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) { if ((*i).name() == dst.name()) { outRoutes->erase(i); break; } } } else printf("removeRoute: source is track but invalid\n"); if (dst.isValid()) { RouteList* inRoutes = dst.track->inRoutes(); for (iRoute i = inRoutes->begin(); i != inRoutes->end(); ++i) { if ((*i).name() == src.name()) { inRoutes->erase(i); break; } } } else printf("removeRoute: source is track but destination invalid\n"); } }/*}}}*/
void MPConfig::mdevViewItemRenamed(QTableWidgetItem* item) { int col = item->column(); QString s = item->text(); //printf("MPConfig::mdevViewItemRenamed col:%d txt:%s\n", col, s.toLatin1().constData()); if (item == 0) return; switch (col) { case DEVCOL_DEF_IN_CHANS: { QString id = item->tableWidget()->item(item->row(), DEVCOL_NO)->text(); int no = atoi(id.toLatin1().constData()) - 1; if (no < 0 || no >= kMaxMidiPorts) return; midiPorts[no].setDefaultInChannels(((1 << kMaxMidiChannels) - 1) & string2bitmap(s)); song->update(); } break; case DEVCOL_DEF_OUT_CHANS: { QString id = item->tableWidget()->item(item->row(), DEVCOL_NO)->text(); int no = atoi(id.toLatin1().constData()) - 1; if (no < 0 || no >= kMaxMidiPorts) return; midiPorts[no].setDefaultOutChannels(((1 << kMaxMidiChannels) - 1) & string2bitmap(s)); song->update(); } break; case DEVCOL_NAME: { QString id = item->tableWidget()->item(item->row(), DEVCOL_NO)->text(); int no = atoi(id.toLatin1().constData()) - 1; if (no < 0 || no >= kMaxMidiPorts) return; MidiPort* port = &midiPorts[no]; MidiDevice* dev = port->device(); // Only Jack midi devices. if (!dev || dev->deviceType() != MidiDevice::JACK_MIDI) return; if (dev->name() == s) return; if (midiDevices.find(s)) { QMessageBox::critical(this, tr("LOS: bad device name"), tr("please choose a unique device name"), QMessageBox::Ok, Qt::NoButton, Qt::NoButton); songChanged(-1); return; } dev->setName(s); song->update(); } break; default: //printf("MPConfig::mdevViewItemRenamed unknown column clicked col:%d txt:%s\n", col, s.toLatin1().constData()); break; } }
void MidiAlsaSeq::run() { // watch the pipe and sequencer input events int pollfd_count = snd_seq_poll_descriptors_count( m_seqHandle, POLLIN ); struct pollfd * pollfd_set = new struct pollfd[pollfd_count + 1]; snd_seq_poll_descriptors( m_seqHandle, pollfd_set + 1, pollfd_count, POLLIN ); pollfd_set[0].fd = m_pipe[0]; pollfd_set[0].events = POLLIN; ++pollfd_count; while( m_quit == false ) { int pollRet = poll( pollfd_set, pollfd_count, EventPollTimeOut ); if( pollRet == 0 ) { continue; } else if( pollRet == -1 ) { // gdb may interrupt the poll if( errno == EINTR ) { continue; } qCritical( "error while polling ALSA sequencer handle" ); break; } // shutdown? if( m_quit ) { break; } m_seqMutex.lock(); // while event queue is not empty while( snd_seq_event_input_pending( m_seqHandle, true ) > 0 ) { snd_seq_event_t * ev; if( snd_seq_event_input( m_seqHandle, &ev ) < 0 ) { m_seqMutex.unlock(); qCritical( "error while fetching MIDI event from sequencer" ); break; } m_seqMutex.unlock(); snd_seq_addr_t * source = NULL; MidiPort * dest = NULL; for( int i = 0; i < m_portIDs.size(); ++i ) { if( m_portIDs.values()[i][0] == ev->dest.port ) { dest = m_portIDs.keys()[i]; } if( ( m_portIDs.values()[i][1] != -1 && m_portIDs.values()[i][1] == ev->source.port ) || m_portIDs.values()[i][0] == ev->source.port ) { source = &ev->source; } } if( dest == NULL ) { continue; } switch( ev->type ) { case SND_SEQ_EVENT_NOTEON: dest->processInEvent( MidiEvent( MidiNoteOn, ev->data.note.channel, ev->data.note.note - KeysPerOctave, ev->data.note.velocity, source ), MidiTime( ev->time.tick ) ); break; case SND_SEQ_EVENT_NOTEOFF: dest->processInEvent( MidiEvent( MidiNoteOff, ev->data.note.channel, ev->data.note.note - KeysPerOctave, ev->data.note.velocity, source ), MidiTime( ev->time.tick) ); break; case SND_SEQ_EVENT_KEYPRESS: dest->processInEvent( MidiEvent( MidiKeyPressure, ev->data.note.channel, ev->data.note.note - KeysPerOctave, ev->data.note.velocity, source ), MidiTime() ); break; case SND_SEQ_EVENT_CONTROLLER: dest->processInEvent( MidiEvent( MidiControlChange, ev->data.control.channel, ev->data.control.param, ev->data.control.value, source ), MidiTime() ); break; case SND_SEQ_EVENT_PGMCHANGE: dest->processInEvent( MidiEvent( MidiProgramChange, ev->data.control.channel, ev->data.control.param, ev->data.control.value, source ), MidiTime() ); break; case SND_SEQ_EVENT_CHANPRESS: dest->processInEvent( MidiEvent( MidiChannelPressure, ev->data.control.channel, ev->data.control.param, ev->data.control.value, source ), MidiTime() ); break; case SND_SEQ_EVENT_PITCHBEND: dest->processInEvent( MidiEvent( MidiPitchBend, ev->data.control.channel, ev->data.control.value + 8192, 0, source ), MidiTime() ); break; case SND_SEQ_EVENT_SENSING: case SND_SEQ_EVENT_CLOCK: break; default: fprintf( stderr, "ALSA-sequencer: unhandled input " "event %d\n", ev->type ); break; } // end switch m_seqMutex.lock(); } // end while m_seqMutex.unlock(); } delete[] pollfd_set; }
bool LOS::importMidi(const QString name, bool merge)/*{{{*/ { bool popenFlag; FILE* fp = fileOpen(this, name, QString(".mid"), "r", popenFlag); if (fp == 0) return true; MidiFile mf(fp); bool rv = mf.read(); popenFlag ? pclose(fp) : fclose(fp); if (rv) { QString s(tr("reading midifile\n ")); s += name; s += tr("\nfailed: "); s += mf.error(); QMessageBox::critical(this, QString("LOS"), s); return rv; } // // evaluate song Type (GM, XG, GS, unknown) // MType t = song->midiType(); if (!merge) { t = mf.mtype(); song->setMType(t); } MidiInstrument* instr = 0; for (iMidiInstrument i = midiInstruments.begin(); i != midiInstruments.end(); ++i) { MidiInstrument* mi = *i; if ((mi->iname() == "GM" && ((t == MT_UNKNOWN) || (t == MIDI_TYPE_GM))) || ((mi->iname() == "GS") && (t == MT_GS)) || ((mi->iname() == "XG") && (t == MT_XG))) { instr = mi; break; } } if (instr == 0) { // the standard instrument files (GM, GS, XG) must be present printf("no instrument, type %d\n", t); return true; //abort(); } MidiFileTrackList* etl = mf.trackList(); int division = mf.division(); // // create MidiTrack and copy events to ->events() // - combine note on/off events // - calculate tick value for internal resolution // for (iMidiFileTrack t = etl->begin(); t != etl->end(); ++t) { MPEventList* el = &((*t)->events); if (el->empty()) continue; // // if we split the track, SYSEX and META events go into // the first target track bool first = true; // somewhat silly and slooow: for (int port = 0; port < kMaxMidiPorts; ++port) { for (int channel = 0; channel < kMaxMidiChannels; ++channel) { // // check if there are any events for port/channel in track: // iMPEvent i; for (i = el->begin(); i != el->end(); ++i) { MidiPlayEvent ev = *i; if (ev.type() != ME_SYSEX && ev.type() != ME_META && ev.channel() == channel && ev.port() == port) break; } if (i == el->end()) continue; MidiTrack* track = new MidiTrack(); if ((*t)->isDrumTrack) { track->setType(Track::DRUM); } track->setOutChannel(channel); track->setOutPort(port); MidiPort* mport = &midiPorts[track->outPort()]; // this overwrites any instrument set for this port: mport->setInstrument(instr); EventList* mel = track->events(); //buildMidiEventList(mel, el, track, division, first); // Don't do loops. buildMidiEventList(mel, el, track, division, first, false); first = false; // Hmm. buildMidiEventList already takes care of this. // But it seems to work. How? Must test. if (channel == 9 && song->midiType() != MT_UNKNOWN) { track->setType(Track::DRUM); // // remap drum pitch with drumInmap // EventList* tevents = track->events(); for (iEvent i = tevents->begin(); i != tevents->end(); ++i) { Event ev = i->second; if (ev.isNote()) { int pitch = drumInmap[ev.pitch()]; ev.setPitch(pitch); } else if (ev.type() == Controller) { int ctl = ev.dataA(); MidiController *mc = mport->drumController(ctl); if (mc) ev.setA((ctl & ~0xff) | drumInmap[ctl & 0x7f]); } } } processTrack(track); song->insertTrack(track, -1); } } if (first) { // // track does only contain non-channel messages // (SYSEX or META) // MidiTrack* track = new MidiTrack(); track->setOutChannel(0); track->setOutPort(0); EventList* mel = track->events(); //buildMidiEventList(mel, el, track, division, true); // Do SysexMeta. Don't do loops. buildMidiEventList(mel, el, track, division, true, false); processTrack(track); song->insertTrack(track, -1); } } if (!merge) { TrackList* tl = song->tracks(); if (!tl->empty()) { Track* track = tl->front(); track->setSelected(true); } song->initLen(); int z, n; ///sigmap.timesig(0, z, n); sigmap.timesig(0, z, n); int tempo = tempomap.tempo(0); transport->setTimesig(z, n); transport->setTempo(tempo); bool masterF = !tempomap.empty(); song->setMasterFlag(masterF); transport->setMasterFlag(masterF); song->updatePos(); composer->reset(); ///composer->setMode(int(song->midiType())); // p4.0.7 Tim } else { song->initLen(); } return false; }/*}}}*/
static void readConfigMidiPort(Xml& xml)/*{{{*/ { int idx = 0; qint64 id = -1; QString device; bool isGlobal = false; QString instrument("GM"); QList<PatchSequence*> patchSequences; QList<QPair<int, QString> > presets; int openFlags = 1; int dic = 0; int doc = 0; int type = MidiDevice::ALSA_MIDI; bool cachenrpn = false; MidiDevice* dev = 0; for (;;) { Xml::Token token = xml.parse(); if (token == Xml::Error || token == Xml::End) break; QString tag = xml.s1(); switch (token) { case Xml::TagStart: if (tag == "name") { device = xml.parse1(); if (!dev)//Look for it as an alsa or already created device dev = midiDevices.find(device); } else if (tag == "type") { type = xml.parseInt(); } else if (tag == "record") { // old bool f = xml.parseInt(); if (f) openFlags |= 2; } else if (tag == "openFlags") openFlags = xml.parseInt(); else if (tag == "defaultInChans") dic = xml.parseInt(); else if (tag == "defaultOutChans") doc = xml.parseInt(); else if (tag == "instrument") { instrument = xml.parse1(); } else if (tag == "channel") { readPortChannel(xml, idx); } else if (tag == "preset" || tag == "patchSequence") { PatchSequence* p = readMidiPortPatchSequences(xml); if (p) patchSequences.append(p); } else if(tag == "midiPreset") { presets.append(readMidiPortPreset(xml)); } else if(tag == "cacheNRPN") { cachenrpn = xml.parseInt(); } else xml.skip(tag); break; case Xml::Attribut: if (tag == "idx") {//Check to see if this port is already used, and bump if so idx = xml.s2().toInt(); int freePort = getFreeMidiPort(); if(freePort != idx) {//Set a flag here so we know when loading tracks later that we are dealing with an old file or global inputs changed idx = freePort; } } else if(tag == "portId") {//New style id = xml.s2().toLongLong(); idx = getFreeMidiPort(); } else if(tag == "isGlobalInput") {//Find the matching input if posible and set our index to it isGlobal = xml.s2().toInt(); } break; case Xml::TagEnd: if (tag == "midiport") { if(isGlobal) {//Find the global input that matches // if(gInputListPorts.size()) { int myport = -1; for(int i = 0; i < gInputListPorts.size(); ++i) { myport = gInputListPorts.at(i); MidiPort* inport = &midiPorts[i]; if(inport && inport->device() && inport->device()->name() == device) { idx = myport; break; } } } } if (idx < 0 || idx >= MIDI_PORTS) { fprintf(stderr, "bad midi port %d (>%d)\n", idx, MIDI_PORTS); idx = 0; } if (!dev) { if (type == MidiDevice::JACK_MIDI) { dev = MidiJackDevice::createJackMidiDevice(device); // p3.3.55 if (debugMsg) fprintf(stderr, "readConfigMidiPort: creating jack midi device %s\n", device.toLatin1().constData()); } else dev = midiDevices.find(device); } if (debugMsg && !dev) fprintf(stderr, "readConfigMidiPort: device not found %s\n", device.toLatin1().constData()); MidiPort* mp = &midiPorts[idx]; if(id) mp->setPortId(id); mp->setInstrument(registerMidiInstrument(instrument)); mp->setDefaultInChannels(dic); mp->setDefaultOutChannels(doc); //Indicate the port was found in the song file, even if no device is assigned to it. mp->setFoundInSongFile(true); if (!patchSequences.isEmpty()) { for (int i = 0; i < patchSequences.size(); ++i) { mp->appendPatchSequence(patchSequences.at(i)); } } if(!presets.isEmpty()) { for(int i = 0; i < presets.size(); ++i) { QPair<int, QString> pair = presets.at(i); mp->addPreset(pair.first, pair.second); } } if (dev) { dev->setOpenFlags(openFlags); midiSeq->msgSetMidiDevice(mp, dev); dev->setCacheNRPN(cachenrpn); } losMidiPorts.insert(mp->id(), mp); return; } default: break; } } }/*}}}*/
void addRoute(Route src, Route dst)/*{{{*/ { //TODO: Add hooks to update the patchcanvas on success //This should be conditionally checked so we dont update if the canvas is what made the connection #ifdef ROUTE_DEBUG fprintf(stderr, "addRoute:\n"); #endif if (!src.isValid() || !dst.isValid()) { if (!src.isValid()) fprintf(stderr, "addRoute: invalid src\n"); if (!dst.isValid()) fprintf(stderr, "addRoute: invalid dst\n"); return; } if (src.type == Route::JACK_ROUTE) { if (dst.type == Route::TRACK_ROUTE) { if (dst.track->type() != Track::AUDIO_INPUT) { fprintf(stderr, "addRoute: source is jack, dest:%s is track but not audio input\n", dst.track->name().toLatin1().constData()); return; } if (dst.channel < 0) { fprintf(stderr, "addRoute: source is jack, dest:%s is track but invalid channel:%d\n", dst.track->name().toLatin1().constData(), dst.channel); return; } src.channel = dst.channel; RouteList* inRoutes = dst.track->inRoutes(); for (iRoute i = inRoutes->begin(); i != inRoutes->end(); ++i) { if (*i == src) // route already there { fprintf(stderr, "addRoute: src track route already exists.\n"); return; } } #ifdef ROUTE_DEBUG fprintf(stderr, "addRoute: src Jack dst track name: %s pushing source route\n", dst.track->name().toLatin1().constData()); #endif inRoutes->push_back(src); } else if (dst.type == Route::MIDI_DEVICE_ROUTE) { if (dst.device->deviceType() == MidiDevice::JACK_MIDI) { src.channel = dst.channel; RouteList* routes = dst.device->inRoutes(); for (iRoute i = routes->begin(); i != routes->end(); ++i) { if (*i == src) // route already there { fprintf(stderr, "addRoute: src Jack midi route already exists.\n"); return; } } #ifdef ROUTE_DEBUG fprintf(stderr, "addRoute: src Jack dst Jack midi name: %s pushing source route\n", dst.device->name().toLatin1().constData()); #endif routes->push_back(src); } else { fprintf(stderr, "addRoute: source is Jack, but destination is not jack midi - type:%d\n", dst.device->deviceType()); return; } } else { fprintf(stderr, "addRoute: source is Jack, but destination is not track or midi - type:%d \n", dst.type); return; } } else if (dst.type == Route::JACK_ROUTE) { if (src.type == Route::TRACK_ROUTE) { if (src.track->type() != Track::AUDIO_OUTPUT) { fprintf(stderr, "addRoute: destination is jack, source is track but not audio output\n"); return; } if (src.channel < 0) { fprintf(stderr, "addRoute: destination is jack, source:%s is track but invalid channel:%d\n", src.track->name().toLatin1().constData(), src.channel); return; } RouteList* outRoutes = src.track->outRoutes(); dst.channel = src.channel; for (iRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) { if (*i == dst) // route already there { #ifdef ROUTE_DEBUG fprintf(stderr, "addRoute: dst track route already exists.\n"); #endif return; } } #ifdef ROUTE_DEBUG fprintf(stderr, "addRoute: dst Jack src track name: %s pushing destination route\n", src.track->name().toLatin1().constData()); #endif outRoutes->push_back(dst); } else if (src.type == Route::MIDI_DEVICE_ROUTE) { if (src.device->deviceType() == MidiDevice::JACK_MIDI) { dst.channel = src.channel; RouteList* routes = src.device->outRoutes(); for (iRoute i = routes->begin(); i != routes->end(); ++i) { if (*i == dst) // route already there { fprintf(stderr, "addRoute: dst Jack midi route already exists.\n"); return; } } #ifdef ROUTE_DEBUG fprintf(stderr, "addRoute: dst Jack src Jack midi name: %s pushing destination route\n", src.device->name().toLatin1().constData()); #endif routes->push_back(dst); } else { fprintf(stderr, "addRoute: destination is Jack, but source is not jack midi - type:%d\n", src.device->deviceType()); return; } } else { fprintf(stderr, "addRoute: destination is Jack, but source is not track or midi - type:%d \n", src.type); return; } } else if (src.type == Route::MIDI_PORT_ROUTE) // p3.3.49 { if (dst.type != Route::TRACK_ROUTE) { fprintf(stderr, "addRoute: source is midi port:%d, but destination is not track\n", src.midiPort); return; } if (dst.channel < 1 || dst.channel >= (1 << MIDI_CHANNELS)) { fprintf(stderr, "addRoute: source is midi port:%d, but destination channel mask:%d out of range\n", src.midiPort, dst.channel); return; } MidiPort *mp = &midiPorts[src.midiPort]; src.channel = dst.channel; RouteList* outRoutes = mp->outRoutes(); iRoute ir = outRoutes->begin(); // p3.3.50 for (; ir != outRoutes->end(); ++ir) { if (ir->type == Route::TRACK_ROUTE && ir->track == dst.track) // p3.3.50 Does a route to the track exist? { ir->channel |= dst.channel; // p3.3.50 Bitwise OR the desired channel bit with the existing bit mask. break; } } #ifdef ROUTE_DEBUG fprintf(stderr, "addRoute: src midi port:%d dst track name:%s pushing dst and src routes\n", src.midiPort, dst.track->name().toLatin1().constData()); #endif if (ir == outRoutes->end()) // p3.3.50 Only if route not found, add the route, with the requested channel bits as mask to start with. outRoutes->push_back(dst); RouteList* inRoutes = dst.track->inRoutes(); // p3.3.50 Make sure only one single route, with a channel mask, can ever exist. ir = inRoutes->begin(); for (; ir != inRoutes->end(); ++ir) { if (ir->type == Route::MIDI_PORT_ROUTE && ir->midiPort == src.midiPort) // p3.3.50 Does a route to the midi port exist? { ir->channel |= src.channel; // p3.3.50 Bitwise OR the desired channel bit with the existing bit mask. break; } } if (ir == inRoutes->end()) // p3.3.50 Only if route not found, add the route, with the requested channel bits as mask to start with. inRoutes->push_back(src); } else if (dst.type == Route::MIDI_PORT_ROUTE) // p3.3.49 { if (src.type != Route::TRACK_ROUTE) { fprintf(stderr, "addRoute: destination is midi port:%d, but source is not track\n", dst.midiPort); return; } if (src.channel < 1 || src.channel >= (1 << MIDI_CHANNELS)) { fprintf(stderr, "addRoute: destination is midi port:%d, but source channel mask:%d out of range\n", dst.midiPort, src.channel); return; } dst.channel = src.channel; RouteList* outRoutes = src.track->outRoutes(); iRoute ir = outRoutes->begin(); // p3.3.50 for (; ir != outRoutes->end(); ++ir) { if (ir->type == Route::MIDI_PORT_ROUTE && ir->midiPort == dst.midiPort) // p3.3.50 Does a route to the midi port exist? { ir->channel |= dst.channel; // p3.3.50 Bitwise OR the desired channel bit with the existing bit mask. break; } } if (ir == outRoutes->end()) // p3.3.50 Only if route not found, add the route, with the requested channel bits as mask to start with. outRoutes->push_back(dst); MidiPort *mp = &midiPorts[dst.midiPort]; #ifdef ROUTE_DEBUG fprintf(stderr, "addRoute: src track:%s dst midi port:%d pushing dst and src routes\n", src.track->name().toLatin1().constData(), dst.midiPort); #endif RouteList* inRoutes = mp->inRoutes(); // p3.3.50 Make sure only one single route, with a channel mask, can ever exist. ir = inRoutes->begin(); for (; ir != inRoutes->end(); ++ir) { if (ir->type == Route::TRACK_ROUTE && ir->track == src.track) // p3.3.50 Does a route to the track exist? { ir->channel |= src.channel; // p3.3.50 Bitwise OR the desired channel bit with the existing bit mask. break; } } if (ir == inRoutes->end()) // p3.3.50 Only if route not found, add the route, with the requested channel bits as mask to start with. inRoutes->push_back(src); } else { if (src.type != Route::TRACK_ROUTE || dst.type != Route::TRACK_ROUTE) // p3.3.49 { fprintf(stderr, "addRoute: source or destination are not track routes\n"); return; } { RouteList* outRoutes = src.track->outRoutes(); // // Must enforce to ensure channel and channels are valid if defaults of -1 passed. // if (src.track->type() == Track::AUDIO_SOFTSYNTH) { if (src.channel == -1) src.channel = 0; if (src.channels == -1) src.channels = src.track->channels(); dst.channel = src.channel; dst.channels = src.channels; dst.remoteChannel = src.remoteChannel; } for (iRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) { if (*i == dst) // route already there { //#ifdef ROUTE_DEBUG fprintf(stderr, "addRoute: src track route already exists.\n"); //#endif return; } } outRoutes->push_back(dst); RouteList* inRoutes; { #ifdef ROUTE_DEBUG fprintf(stderr, "addRoute: src track ch:%d chs:%d remch:%d dst track ch:%d chs:%d remch:%d name: %s pushing dest and source routes\n", src.channel, src.channels, src.remoteChannel, dst.channel, dst.channels, dst.remoteChannel, dst.track->name().toLatin1().constData()); #endif inRoutes = dst.track->inRoutes(); } // // make sure AUDIO_AUX is processed last // if (src.track->type() == Track::AUDIO_AUX) inRoutes->push_back(src); else inRoutes->insert(inRoutes->begin(), src); } } }/*}}}*/
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); } } }