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 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; } } }/*}}}*/
QWidget* MenuList::createWidget(QWidget* parent) { if(!_track) return 0; //if(!_track->isMidiTrack() || _track->type() != Track::AUDIO_SOFTSYNTH) // return 0; MidiTrack* track = (MidiTrack*) _track; MidiDevice* md = 0; int port = -1; if (track->type() == Track::AUDIO_SOFTSYNTH) { md = dynamic_cast<MidiDevice*> (track); if (md) port = md->midiPort(); } else port = track->outPort(); list = new QListWidget(parent); list->setSelectionMode(QAbstractItemView::SingleSelection); list->setAlternatingRowColors(true); list->setEditTriggers(QAbstractItemView::NoEditTriggers); list->setFixedHeight(300); for (int i = 0; i < MIDI_PORTS; ++i) { QString name; name.sprintf("%d:%s", i + 1, midiPorts[i].portname().toLatin1().constData()); list->insertItem(i, name); if (i == port) list->setCurrentRow(i); } connect(list, SIGNAL(itemPressed(QListWidgetItem*)), this, SLOT(updateData(QListWidgetItem*))); //connect(list, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(updateData(QListWidgetItem*))); //connect(list, SIGNAL(itemActivated(QListWidgetItem*)), this, SLOT(updateData(QListWidgetItem*))); return list; }
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"); }/*}}}*/
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; }/*}}}*/
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; }/*}}}*/
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 // 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; // somewhat silly and slooow: QList<QPair<int, int> > eventChannelList; if(mPort >= 0 && mPort < kMaxMidiPorts) { 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) break; } if (i == el->end()) continue; MidiTrack* track = new MidiTrack(); track->setDefaultName(); track->setMasterFlag(true); track->setOutChannel(channel); 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); song->insertTrack(track, -1); //Create the Audio input side of the track Track* input = song->addTrackByName(QString("i").append(track->name()), Track::AUDIO_INPUT, -1, false, false); if(input) { input->setMasterFlag(false); input->setChainMaster(track->id()); track->addManagedTrack(input->id()); } } } 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); EventList* mel = track->events(); //buildMidiEventList(mel, el, track, division, true); // Do SysexMeta. Don't do loops. buildMidiEventList(mel, el, track, division, true, false, false); processTrack(track); song->insertTrack(track, -1); //Create the Audio input side of the track Track* input = song->addTrackByName(QString("i").append(track->name()), Track::AUDIO_INPUT, -1, false, false); if(input) { input->setMasterFlag(false); input->setChainMaster(track->id()); track->addManagedTrack(input->id()); } } 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 (!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); 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 Audio::initDevices() { // // mark all used ports // bool activePorts[MIDI_PORTS]; for (int i = 0; i < MIDI_PORTS; ++i) activePorts[i] = false; MidiTrackList* tracks = song->midis(); for (iMidiTrack it = tracks->begin(); it != tracks->end(); ++it) { MidiTrack* track = *it; activePorts[track->outPort()] = true; } if (song->click()) activePorts[clickPort] = true; // // test for explicit instrument initialization // for (int i = 0; i < MIDI_PORTS; ++i) { if (!activePorts[i]) continue; MidiPort* port = &midiPorts[i]; MidiInstrument* instr = port->instrument(); MidiDevice* md = port->device(); if (instr && md) { EventList* events = instr->midiInit(); if (events->empty()) continue; for (iEvent ie = events->begin(); ie != events->end(); ++ie) { MidiPlayEvent ev(0, i, 0, ie->second); md->putEvent(ev); } activePorts[i] = false; // no standard initialization } } // // damit Midi-Devices, die mehrere Ports besitzen, wie z.B. // das Korg NS5R, nicht mehrmals zwischen GM und XG/GS hin und // hergeschaltet werden, wird zunïÿýhst auf allen Ports GM // initialisiert, und dann erst XG/GS // // Standard initialization... for (int i = 0; i < MIDI_PORTS; ++i) { if (!activePorts[i]) continue; MidiPort* port = &midiPorts[i]; switch (song->mtype()) { case MT_GS: case MT_UNKNOWN: break; case MT_GM: case MT_XG: port->sendGmOn(); break; } } for (int i = 0; i < MIDI_PORTS; ++i) { if (!activePorts[i]) continue; MidiPort* port = &midiPorts[i]; switch (song->mtype()) { case MT_UNKNOWN: break; case MT_GM: port->sendGmInitValues(); break; case MT_GS: port->sendGsOn(); port->sendGsInitValues(); break; case MT_XG: port->sendXgOn(); port->sendXgInitValues(); 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; }/*}}}*/
void Audio::processMidi() { midiBusy = true; // // TODO: syntis should directly write into recordEventList // for (iMidiDevice id = midiDevices.begin(); id != midiDevices.end(); ++id) { MidiDevice* md = *id; MPEventList* playEvents = md->playEvents(); // // erase already played events: // iMPEvent nextPlayEvent = md->nextPlayEvent(); playEvents->erase(playEvents->begin(), nextPlayEvent); // klumsy hack for synti devices: if (md->isSynti()) { SynthI* s = (SynthI*) md; while (s->eventsPending()) { MidiRecordEvent ev = s->receiveEvent(); md->recordEvent(ev); } } // Is it a Jack midi device? //MidiJackDevice* mjd = dynamic_cast<MidiJackDevice*>(md); //if(mjd) // mjd->collectMidiEvents(); md->collectMidiEvents(); // Take snapshots of the current sizes of the recording fifos, // because they may change while here in process, asynchronously. md->beforeProcess(); } MPEventList* playEvents = metronome->playEvents(); iMPEvent nextPlayEvent = metronome->nextPlayEvent(); playEvents->erase(playEvents->begin(), nextPlayEvent); // p3.3.25 bool extsync = extSyncFlag.value(); for (iMidiTrack t = song->midis()->begin(); t != song->midis()->end(); ++t) { MidiTrack* track = *t; int port = track->outPort(); MidiDevice* md = midiPorts[port].device(); // Changed by Tim. p3.3.8 //if(md == 0) // continue; //MPEventList* playEvents = md->playEvents(); //if (playEvents == 0) // continue; //if (!track->isMute()) MPEventList* playEvents = 0; if (md) { playEvents = md->playEvents(); // only add track events if the track is unmuted if (!track->isMute()) { if (isPlaying() && (curTickPos < nextTickPos)) collectEvents(track, curTickPos, nextTickPos); } } // //----------midi recording // if (track->recordFlag()) { //int portMask = track->inPortMask(); // p3.3.38 Removed //unsigned int portMask = track->inPortMask(); //int channelMask = track->inChannelMask(); MPEventList* rl = track->mpevents(); MidiPort* tport = &midiPorts[port]; // p3.3.38 //for (iMidiDevice id = midiDevices.begin(); id != midiDevices.end(); ++id) //{ RouteList* irl = track->inRoutes(); for (ciRoute r = irl->begin(); r != irl->end(); ++r) { //if(!r->isValid() || (r->type != Route::ALSA_MIDI_ROUTE && r->type != Route::JACK_MIDI_ROUTE)) //if(!r->isValid() || (r->type != Route::MIDI_DEVICE_ROUTE)) if (!r->isValid() || (r->type != Route::MIDI_PORT_ROUTE)) // p3.3.49 continue; int devport = r->midiPort; // p3.3.49 if (devport == -1) continue; //MidiDevice* dev = *id; //MidiDevice* dev = r->device; MidiDevice* dev = midiPorts[devport].device(); // p3.3.49 if (!dev) continue; // p3.3.50 Removed //int channel = r->channel; // NOTE: TODO: Special for input device sysex 'channel' marked as -1, ** IF we end up going with that method **. // This would mean having a separate 'System' channel listed in the routing popups. // The other alternative is to accept sysex from a device as long as ANY regular channel is routed from it, // this does not require a 'System' channel listed in the routing popups. // But that requires more code below... Done. //if(channel == -1) //channel = MIDI_CHANNELS; // Special channel '17' // continue; //int devport = dev->midiPort(); // record only from ports marked in portMask: //if (devport == -1 || !(portMask & (1 << devport))) //if (devport == -1) // continue; //MREventList* el = dev->recordEvents(); //MidiFifo& rf = dev->recordEvents(); int channelMask = r->channel; // p3.3.50 if (channelMask == -1 || channelMask == 0) continue; for (int channel = 0; channel < MIDI_CHANNELS; ++channel) // p3.3.50 { if (!(channelMask & (1 << channel))) continue; if (!dev->sysexFIFOProcessed()) { // Set to the sysex fifo at first. MidiFifo& rf = dev->recordEvents(MIDI_CHANNELS); // Get the frozen snapshot of the size. int count = dev->tmpRecordCount(MIDI_CHANNELS); for (int i = 0; i < count; ++i) { MidiPlayEvent event(rf.peek(i)); //unsigned time = event.time() + segmentSize*(segmentCount-1); //unsigned time = event.time() + (extsync ? config.division/24 : segmentSize*(segmentCount-1)); //unsigned time = extsync ? curTickPos : (event.time() + segmentSize*(segmentCount-1)); //event.setTime(time); //if(!extsync) // event.setTime(event.time() + segmentSize*(segmentCount-1)); event.setPort(port); // dont't echo controller changes back to software // synthesizer: if (!dev->isSynti() && md && track->recEcho()) playEvents->add(event); // If syncing externally the event time is already in units of ticks, set above. if (!extsync) { //time = tempomap.frame2tick(event.time()); //event.setTime(time); // set tick time event.setTime(tempomap.frame2tick(event.time())); // set tick time } if (recording) rl->add(event); } dev->setSysexFIFOProcessed(true); } // Set to the sysex fifo at first. ///MidiFifo& rf = dev->recordEvents(MIDI_CHANNELS); // Get the frozen snapshot of the size. ///int count = dev->tmpRecordCount(MIDI_CHANNELS); // Iterate once for sysex fifo (if needed), once for channel fifos. ///for(int sei = 0; sei < 2; ++sei) { // If on first pass, do sysex fifo. /* if(sei == 0) { // Ignore any further channel routes on this device if already done here. if(dev->sysexFIFOProcessed()) continue; // Go ahead and set this now. dev->setSysexFIFOProcessed(true); // Allow it to fall through with the sysex fifo and count... } else { // We're on the second pass, do channel fifos. rf = dev->recordEvents(channel); // Get the frozen snapshot of the size. count = dev->tmpRecordCount(channel); } */ MidiFifo& rf = dev->recordEvents(channel); int count = dev->tmpRecordCount(channel); //for (iMREvent ie = el->begin(); ie != el->end(); ++ie) for (int i = 0; i < count; ++i) { MidiPlayEvent event(rf.peek(i)); //int channel = ie->channel(); ///int channel = event.channel(); int defaultPort = devport; ///if (!(channelMask & (1 << channel))) ///{ /// continue; ///} //MidiPlayEvent event(*ie); int drumRecPitch = 0; //prevent compiler warning: variable used without initialization MidiController *mc = 0; int ctl = 0; //Hmmm, hehhh... // TODO: Clean up a bit around here when it comes to separate events for rec & for playback. // But not before 0.7 (ml) int prePitch = 0, preVelo = 0; event.setChannel(track->outChannel()); if (event.isNote() || event.isNoteOff()) { // // apply track values // //Apply drum inkey: if (track->type() == Track::DRUM) { int pitch = event.dataA(); //Map note that is played according to drumInmap drumRecPitch = drumMap[(unsigned int) drumInmap[pitch]].enote; devport = drumMap[(unsigned int) drumInmap[pitch]].port; event.setPort(devport); channel = drumMap[(unsigned int) drumInmap[pitch]].channel; event.setA(drumMap[(unsigned int) drumInmap[pitch]].anote); event.setChannel(channel); } else { //Track transpose if non-drum prePitch = event.dataA(); int pitch = prePitch + track->transposition; if (pitch > 127) pitch = 127; if (pitch < 0) pitch = 0; event.setA(pitch); } if (!event.isNoteOff()) { preVelo = event.dataB(); int velo = preVelo + track->velocity; velo = (velo * track->compression) / 100; if (velo > 127) velo = 127; if (velo < 1) velo = 1; event.setB(velo); } } // Added by T356. else if (event.type() == ME_CONTROLLER) { //printf("11111111111111111111111111111111111111111111111111111\n"); if (track->type() == Track::DRUM) { //printf("2222222222222222222222222222222222222222222222222222222222\n"); ctl = event.dataA(); // Regardless of what port the event came from, is it a drum controller event // according to the track port's instrument? mc = tport->drumController(ctl); if (mc) { //printf("333333333333333333333333333333333333333333333333\n"); int pitch = ctl & 0x7f; ctl &= ~0xff; int dmindex = drumInmap[pitch] & 0x7f; //Map note that is played according to drumInmap drumRecPitch = drumMap[dmindex].enote; devport = drumMap[dmindex].port; event.setPort(devport); channel = drumMap[dmindex].channel; event.setA(ctl | drumMap[dmindex].anote); event.setChannel(channel); } } } // p3.3.25 // OOMidi uses a fixed clocks per quarternote of 24. // At standard 384 ticks per quarternote for example, // 384/24=16 for a division of 16 sub-frames (16 OOMidi 'ticks'). // That is what we'll use if syncing externally. //unsigned time = event.time() + segmentSize*(segmentCount-1); //unsigned time = event.time() + (extsync ? config.division/24 : segmentSize*(segmentCount-1)); // p3.3.34 // Oops, use the current tick. //unsigned time = extsync ? curTickPos : (event.time() + segmentSize*(segmentCount-1)); //event.setTime(time); // p3.3.35 // If ext sync, events are now time-stamped with last tick in MidiDevice::recordEvent(). // TODO: Tested, but record resolution not so good. Switch to wall clock based separate list in MidiDevice. // p3.3.36 //if(!extsync) // event.setTime(event.time() + segmentSize*(segmentCount-1)); // dont't echo controller changes back to software // synthesizer: if (!dev->isSynti()) { //printf("444444444444444444444444444444444444444444444444444444\n"); //Check if we're outputting to another port than default: if (devport == defaultPort) { //printf("5555555555555555555555555555555555555555555\n"); event.setPort(port); if (md && track->recEcho()) playEvents->add(event); } else { //printf("66666666666666666666666666666666666666\n"); // Hmm, this appears to work, but... Will this induce trouble with md->setNextPlayEvent?? MidiDevice* mdAlt = midiPorts[devport].device(); if (mdAlt && track->recEcho()) mdAlt->playEvents()->add(event); } // Shall we activate meters even while rec echo is off? Sure, why not... if (event.isNote() && event.dataB() > track->activity()) track->setActivity(event.dataB()); } // p3.3.25 // If syncing externally the event time is already in units of ticks, set above. if (!extsync) { //printf("7777777777777777777777777777777777777777777\n"); // p3.3.35 //time = tempomap.frame2tick(event.time()); //event.setTime(time); // set tick time event.setTime(tempomap.frame2tick(event.time())); // set tick time } // Special handling of events stored in rec-lists. a bit hACKish. TODO: Clean up (after 0.7)! :-/ (ml) if (recording) { //printf("888888888888888888888888888888888888888888888888\n"); // In these next steps, it is essential to set the recorded event's port // to the track port so buildMidiEventList will accept it. Even though // the port may have no device "<none>". // if (track->type() == Track::DRUM) { //printf("99999999999999999999999999999999999999999999999999\n"); // Is it a drum controller event? if (mc) { //printf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n"); MidiPlayEvent drumRecEvent = event; drumRecEvent.setA(ctl | drumRecPitch); // In this case, preVelo is simply the controller value. drumRecEvent.setB(preVelo); drumRecEvent.setPort(port); //rec-event to current port drumRecEvent.setChannel(track->outChannel()); //rec-event to current channel rl->add(drumRecEvent); } else { //printf("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n"); MidiPlayEvent drumRecEvent = event; drumRecEvent.setA(drumRecPitch); drumRecEvent.setB(preVelo); // Changed by T356. // Tested: Events were not being recorded for a drum map entry pointing to a // different port. This must have been wrong - buildMidiEventList would ignore this. //drumRecEvent.setPort(devport); drumRecEvent.setPort(port); //rec-event to current port drumRecEvent.setChannel(track->outChannel()); //rec-event to current channel rl->add(drumRecEvent); } } else { //printf("ccccccccccccccccccccccccccccccccccccccccccccc\n"); // Restore record-pitch to non-transposed value since we don't want the note transposed twice next MidiPlayEvent recEvent = event; if (prePitch) recEvent.setA(prePitch); if (preVelo) recEvent.setB(preVelo); recEvent.setPort(port); recEvent.setChannel(track->outChannel()); rl->add(recEvent); } } } } } } } // Added by Tim. p3.3.8 if (md) { //printf("zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\n"); md->setNextPlayEvent(playEvents->begin()); } } // // clear all recorded events in midiDevices // process stuck notes // for (iMidiDevice id = midiDevices.begin(); id != midiDevices.end(); ++id) { //printf("--------------------------aaaaaaaaaaaaaaaaaaaaaaaaaaaa\n"); MidiDevice* md = *id; ///md->recordEvents()->clear(); // By T356. Done processing this rec buffer, now flip to the other one. ///md->flipRecBuffer(); // We are done with the 'frozen' recording fifos, remove the events. md->afterProcess(); MPEventList* stuckNotes = md->stuckNotes(); MPEventList* playEvents = md->playEvents(); iMPEvent k; for (k = stuckNotes->begin(); k != stuckNotes->end(); ++k) { if (k->time() >= nextTickPos) break; MidiPlayEvent ev(*k); // p3.3.25 //int frame = tempomap.tick2frame(k->time()) + frameOffset; if (extsync) { ev.setTime(k->time()); } else { int frame = tempomap.tick2frame(k->time()) + frameOffset; ev.setTime(frame); } // p3.3.25 //ev.setTime(frame); playEvents->add(ev); } stuckNotes->erase(stuckNotes->begin(), k); md->setNextPlayEvent(playEvents->begin()); } //--------------------------------------------------- // insert metronome clicks //--------------------------------------------------- MidiDevice* md = 0; if (midiClickFlag) md = midiPorts[clickPort].device(); if (song->click() && (isPlaying() || state == PRECOUNT)) { MPEventList* playEvents = 0; MPEventList* stuckNotes = 0; if (md) { playEvents = md->playEvents(); stuckNotes = md->stuckNotes(); } int bar, beat; unsigned tick; bool isMeasure = false; while (midiClick < nextTickPos) { if (isPlaying()) { ///sigmap.tickValues(midiClick, &bar, &beat, &tick); AL::sigmap.tickValues(midiClick, &bar, &beat, &tick); isMeasure = beat == 0; } else if (state == PRECOUNT) { isMeasure = (clickno % clicksMeasure) == 0; } // p3.3.25 //int frame = tempomap.tick2frame(midiClick) + frameOffset; int evtime = extsync ? midiClick : tempomap.tick2frame(midiClick) + frameOffset; // p3.3.25 //MidiPlayEvent ev(frame, clickPort, clickChan, ME_NOTEON, MidiPlayEvent ev(evtime, clickPort, clickChan, ME_NOTEON, beatClickNote, beatClickVelo); if (md) { // p3.3.25 //MidiPlayEvent ev(frame, clickPort, clickChan, ME_NOTEON, MidiPlayEvent ev(evtime, clickPort, clickChan, ME_NOTEON, beatClickNote, beatClickVelo); if (isMeasure) { ev.setA(measureClickNote); ev.setB(measureClickVelo); } playEvents->add(ev); } if (audioClickFlag) { // p3.3.25 //MidiPlayEvent ev1(frame, 0, 0, ME_NOTEON, 0, 0); MidiPlayEvent ev1(evtime, 0, 0, ME_NOTEON, 0, 0); ev1.setA(isMeasure ? 0 : 1); metronome->playEvents()->add(ev1); } if (md) { ev.setB(0); // p3.3.25 // Removed. Why was this here? //frame = tempomap.tick2frame(midiClick+20) + frameOffset; // // Does it mean this should be changed too? // No, stuck notes are in units of ticks, not frames like (normal, non-external) play events... ev.setTime(midiClick + 10); if (md) stuckNotes->add(ev); } if (isPlaying()) ///midiClick = sigmap.bar2tick(bar, beat+1, 0); midiClick = AL::sigmap.bar2tick(bar, beat + 1, 0); else if (state == PRECOUNT) { midiClick += ticksBeat; if (clickno) --clickno; else state = START_PLAY; } } if (md) md->setNextPlayEvent(playEvents->begin()); if (audioClickFlag) metronome->setNextPlayEvent(metronome->playEvents()->begin()); } if (state == STOP) { //--------------------------------------------------- // end all notes //--------------------------------------------------- for (iMidiDevice imd = midiDevices.begin(); imd != midiDevices.end(); ++imd) { MidiDevice* md = *imd; MPEventList* playEvents = md->playEvents(); MPEventList* stuckNotes = md->stuckNotes(); for (iMPEvent k = stuckNotes->begin(); k != stuckNotes->end(); ++k) { MidiPlayEvent ev(*k); ev.setTime(0); // play now playEvents->add(ev); } stuckNotes->clear(); } } // p3.3.36 //int tickpos = audio->tickPos(); //bool extsync = extSyncFlag.value(); // // Special for Jack midi devices: Play all Jack midi events up to curFrame. // for (iMidiDevice id = midiDevices.begin(); id != midiDevices.end(); ++id) { (*id)->processMidi(); /* int port = md->midiPort(); MidiPort* mp = port != -1 ? &midiPorts[port] : 0; MPEventList* el = md->playEvents(); if (el->empty()) continue; iMPEvent i = md->nextPlayEvent(); for(; i != el->end(); ++i) { // 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(i->time() > curFrame) if(i->time() > (extsync ? tickpos : curFrame)) { //printf(" curT %d frame %d\n", i->time(), curFrame); break; // skip this event } if(mp) { if(mp->sendEvent(*i)) break; } else { if(md->putEvent(*i)) break; } } md->setNextPlayEvent(i); */ } midiBusy = false; }
void addPortCtrlEvents(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 && t->isMidiTrack()) { 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 add events which are past the end of the part. if (ev.tick() >= len) break; if (ev.type() == Controller) { int ch = mt->outChannel(); int tck = ev.tick() + p->tick(); int cntrl = ev.dataA(); int val = ev.dataB(); MidiPort* mp = &midiPorts[port]; // Is it a drum controller event, according to the track port's instrument? if (mt->type() == Track::DRUM) { MidiController* mc = mp->drumController(cntrl); if (mc) { int note = cntrl & 0x7f; cntrl &= ~0xff; ch = drumMap[note].channel; mp = &midiPorts[drumMap[note].port]; cntrl |= drumMap[note].anote; } } 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; } } }
bool Song::event(QEvent* _e) { if (_e->type() != QEvent::User) return false; //ignore all events except user events, which are events from Python bridge subsystem QPybridgeEvent* e = (QPybridgeEvent*) _e; switch (e->getType()) { case QPybridgeEvent::SONG_UPDATE: this->update(e->getP1()); break; case QPybridgeEvent::SONGLEN_CHANGE: this->setLen(e->getP1()); break; case QPybridgeEvent::SONG_POSCHANGE: this->setPos(e->getP1(), e->getP2()); break; case QPybridgeEvent::SONG_SETPLAY: this->setPlay(true); break; case QPybridgeEvent::SONG_SETSTOP: this->setStop(true); break; case QPybridgeEvent::SONG_REWIND: this->rewindStart(); break; case QPybridgeEvent::SONG_SETMUTE: { Track* track = this->findTrack(e->getS1()); if (track == NULL) return false; bool muted = e->getP1() == 1; track->setMute(muted); this->update(SC_MUTE | SC_TRACK_MODIFIED); break; } case QPybridgeEvent::SONG_SETCTRL: { Track* t = this->findTrack(e->getS1()); if (t == NULL) return false; if (t->isMidiTrack() == false) return false; MidiTrack* track = (MidiTrack*) t; int chan = track->outChannel(); int num = e->getP1(); int val = e->getP2(); int tick = song->cpos(); MidiPlayEvent ev(tick, track->outPort(), chan, ME_CONTROLLER, num, val, t); audio->msgPlayMidiEvent(&ev); song->update(SC_MIDI_CONTROLLER); break; } case QPybridgeEvent::SONG_SETAUDIOVOL: { Track* t = this->findTrack(e->getS1()); if (t == NULL) return false; if (t->type() == Track::DRUM || t->type() == Track::MIDI) return false; AudioTrack* track = (AudioTrack*) t; track->setVolume(e->getD1()); break; } case QPybridgeEvent::SONG_IMPORT_PART: { Track* track = this->findTrack(e->getS1()); QString filename = e->getS2(); unsigned int tick = e->getP1(); if (track == NULL) return false; oom->importPartToTrack(filename, tick, track); break; } case QPybridgeEvent::SONG_TOGGLE_EFFECT: { Track* t = this->findTrack(e->getS1()); if (t == NULL) return false; if (t->type() != Track::WAVE) return false; int fxid = e->getP1(); int onoff = (e->getP2() == 1); AudioTrack* track = (AudioTrack*) t; Pipeline* pipeline = track->efxPipe(); const Pipeline* pipeline = track->efxPipe(); if(pipeline) { int pdepth = pipeline->size(); if (fxid > pdepth) return false; pipeline->setOn(fxid, onoff); } break; } case QPybridgeEvent::SONG_ADD_TRACK: song->addTrack(e->getP1()); song->updateTrackViews(); break; case QPybridgeEvent::SONG_CHANGE_TRACKNAME: { Track* t = this->findTrack(e->getS1()); if (t == NULL) return false; t->setName(e->getS2()); break; } case QPybridgeEvent::SONG_DELETE_TRACK: { Track* t = this->findTrack(e->getS1()); if (t == NULL) return false; audio->msgRemoveTrack(t); break; } default: printf("Unknown pythonthread event received: %d\n", e->getType()); break; } return true; }