void LOS::processTrack(MidiTrack* track) { EventList* tevents = track->events(); if (tevents->empty()) return; //--------------------------------------------------- // Identify Parts // The MIDI tracks are broken up into parts // A new Part will be located based on track. // Events will be aligned on new track //--------------------------------------------------- PartList* pl = track->parts(); int lastTick = 0; for (iEvent i = tevents->begin(); i != tevents->end(); ++i) { Event event = i->second; int epos = event.tick() + event.lenTick(); if (epos > lastTick) lastTick = epos; } QString partname = track->name(); int len = song->roundUpBar(lastTick + 1); // p3.3.27 if (config.importMidiSplitParts) { int bar2, beat; unsigned tick; sigmap.tickValues(len, &bar2, &beat, &tick); int lastOff = 0; int st = -1; // start tick current part int x1 = 0; // start tick current measure int x2 = 0; // end tick current measure for (int bar = 0; bar < bar2; ++bar, x1 = x2) { ///x2 = sigmap.bar2tick(bar+1, 0, 0); x2 = sigmap.bar2tick(bar + 1, 0, 0); if (lastOff > x2) { // this measure is busy! continue; } iEvent i1 = tevents->lower_bound(x1); iEvent i2 = tevents->lower_bound(x2); if (i1 == i2) { // empty? if (st != -1) { MidiPart* part = new MidiPart(track); part->setTick(st); part->setLenTick(x1 - st); // printf("new part %d len: %d\n", st, x1-st); part->setName(partname); pl->add(part); st = -1; } } else { if (st == -1) st = x1; // begin new part //HACK: //lastOff: for (iEvent i = i1; i != i2; ++i) { Event event = i->second; if (event.type() == Note) { int off = event.tick() + event.lenTick(); if (off > lastOff) lastOff = off; } } } } if (st != -1) { MidiPart* part = new MidiPart(track); part->setTick(st); // printf("new part %d len: %d\n", st, x2-st); part->setLenTick(x2 - st); part->setName(partname); pl->add(part); } } else { // Just one long part... MidiPart* part = new MidiPart(track); //part->setTick(st); part->setTick(0); part->setLenTick(len); part->setName(partname); pl->add(part); } //------------------------------------------------------------- // assign events to parts //------------------------------------------------------------- for (iPart p = pl->begin(); p != pl->end(); ++p) { MidiPart* part = (MidiPart*) (p->second); int stick = part->tick(); int etick = part->tick() + part->lenTick(); iEvent r1 = tevents->lower_bound(stick); iEvent r2 = tevents->lower_bound(etick); int startTick = part->tick(); EventList* el = part->events(); for (iEvent i = r1; i != r2; ++i) { Event ev = i->second; int ntick = ev.tick() - startTick; ev.setTick(ntick); el->add(ev); } tevents->erase(r1, r2); } if (tevents->size()) printf("-----------events left: %zd\n", tevents->size()); for (iEvent i = tevents->begin(); i != tevents->end(); ++i) { printf("%d===\n", i->first); i->second.dump(); } // all events should be processed: assert(tevents->empty()); }
void buildMidiEventList(EventList* del, const MPEventList* el, MidiTrack* track, int div, bool addSysexMeta, bool doLoops) { int hbank = 0xff; int lbank = 0xff; int rpnh = -1; int rpnl = -1; int datah = 0; int datal = 0; int dataType = 0; // 0 : disabled, 0x20000 : rpn, 0x30000 : nrpn EventList mel; for (iMPEvent i = el->begin(); i != el->end(); ++i) { MidiPlayEvent ev = *i; if (!addSysexMeta && (ev.type() == ME_SYSEX || ev.type() == ME_META)) continue; if (!(ev.type() == ME_SYSEX || ev.type() == ME_META || ((ev.channel() == track->outChannel()) && (ev.port() == track->outPort())))) continue; unsigned tick = ev.time(); // Added by Tim. p3.3.8 // Added by T356. if (doLoops) { if (tick >= song->lPos().tick() && tick < song->rPos().tick()) { int loopn = ev.loopNum(); int loopc = audio->loopCount(); int cmode = song->cycleMode(); // CYCLE_NORMAL, CYCLE_MIX, CYCLE_REPLACE // If we want REPLACE and the event was recorded in a previous loop, // just ignore it. This will effectively ignore ALL previous loop events inside // the left and right markers, regardless of where recording was started or stopped. // We want to keep any loop 0 note-offs from notes which crossed over the left marker. // To avoid more searching here, just keep ALL note-offs from loop 0, and let code below // sort out and keep which ones had note-ons. if (!(ev.isNoteOff() && loopn == 0)) { if (cmode == Song::CYCLE_REPLACE && loopn < loopc) { // Added by Tim. p3.3.8 //printf("buildMidiEventList: CYCLE_REPLACE t:%d type:%d A:%d B:%d ln:%d lc:%d\n", tick, ev.type(), ev.dataA(), ev.dataB(), loopn, loopc); continue; } // If we want NORMAL, same as REPLACE except keep all events from the previous loop // from rec stop position to right marker (and beyond). if (cmode == Song::CYCLE_NORMAL) { // Not sure of accuracy here. Adjust? Adjusted when used elsewhere? unsigned endRec = audio->getEndRecordPos().tick(); if ((tick < endRec && loopn < loopc) || (tick >= endRec && loopn < (loopc - 1))) { // Added by Tim. p3.3.8 //printf("buildMidiEventList: CYCLE_NORMAL t:%d type:%d A:%d B:%d ln:%d lc:%d\n", tick, ev.type(), ev.dataA(), ev.dataB(), loopn, loopc); continue; } } } } } Event e; switch (ev.type()) { case ME_NOTEON: e.setType(Note); if (track->type() == Track::DRUM) { int instr = drumInmap[ev.dataA()]; e.setPitch(instr); } else { e.setPitch(ev.dataA()); } e.setVelo(ev.dataB()); e.setLenTick(0); break; case ME_NOTEOFF: e.setType(Note); if (track->type() == Track::DRUM) { int instr = drumInmap[ev.dataA()]; e.setPitch(instr); } else e.setPitch(ev.dataA()); e.setVelo(0); e.setVeloOff(ev.dataB()); e.setLenTick(0); break; case ME_POLYAFTER: e.setType(PAfter); e.setA(ev.dataA()); e.setB(ev.dataB()); break; case ME_CONTROLLER: { int val = ev.dataB(); switch (ev.dataA()) { case CTRL_HBANK: hbank = val; break; case CTRL_LBANK: lbank = val; break; case CTRL_HDATA: datah = val; // check if a CTRL_LDATA follows // e.g. wie have a 14 bit controller: { iMPEvent ii = i; ++ii; bool found = false; for (; ii != el->end(); ++ii) { MidiPlayEvent ev = *ii; if (ev.type() == ME_CONTROLLER) { if (ev.dataA() == CTRL_LDATA) { // handle later found = true; } break; } } if (!found) { if (rpnh == -1 || rpnl == -1) { printf("parameter number not defined, data 0x%x\n", datah); } else { int ctrl = dataType | (rpnh << 8) | rpnl; e.setType(Controller); e.setA(ctrl); e.setB(datah); } } } break; case CTRL_LDATA: datal = val; if (rpnh == -1 || rpnl == -1) { printf("parameter number not defined, data 0x%x 0x%x, tick %d, channel %d\n", datah, datal, tick, track->outChannel()); break; } // assume that the sequence is always // CTRL_HDATA - CTRL_LDATA // eg. that LDATA is always send last e.setType(Controller); // 14 Bit RPN/NRPN e.setA((dataType + 0x30000) | (rpnh << 8) | rpnl); e.setB((datah << 7) | datal); break; case CTRL_HNRPN: rpnh = val; dataType = 0x30000; break; case CTRL_LNRPN: rpnl = val; dataType = 0x30000; break; case CTRL_HRPN: rpnh = val; dataType = 0x20000; break; case CTRL_LRPN: rpnl = val; dataType = 0x20000; break; default: e.setType(Controller); int ctl = ev.dataA(); e.setA(ctl); if (track->type() == Track::DRUM) { // Is it a drum controller event, according to the track port's instrument? MidiController *mc = midiPorts[track->outPort()].drumController(ctl); if (mc) // Store an index into the drum map. e.setA((ctl & ~0xff) | drumInmap[ctl & 0x7f]); } e.setB(val); break; } } break; case ME_PROGRAM: e.setType(Controller); e.setA(CTRL_PROGRAM); e.setB((hbank << 16) | (lbank << 8) | ev.dataA()); break; case ME_AFTERTOUCH: e.setType(CAfter); e.setA(ev.dataA()); break; case ME_PITCHBEND: e.setType(Controller); e.setA(CTRL_PITCH); e.setB(ev.dataA()); break; case ME_SYSEX: e.setType(Sysex); e.setData(ev.data(), ev.len()); break; case ME_META: { const unsigned char* data = ev.data(); switch (ev.dataA()) { case 0x01: // Text if (track->comment().isEmpty()) track->setComment(QString((const char*) data)); else track->setComment(track->comment() + "\n" + QString((const char*) data)); break; case 0x03: // Sequence-/TrackName track->setName(QString((char*) data)); break; case 0x6: // Marker { unsigned ltick = CALC_TICK(tick); //(tick * config.division + div/2) / div; song->addMarker(QString((const char*) (data)), ltick, false); } break; case 0x5: // Lyrics case 0x8: // text case 0x9: case 0xa: break; case 0x0f: // Track Comment track->setComment(QString((char*) data)); break; case 0x51: // Tempo { unsigned tempo = data[2] + (data[1] << 8) + (data[0] << 16); unsigned ltick = CALC_TICK(tick); // (unsigned(tick) * unsigned(config.division) + unsigned(div/2)) / unsigned(div); // After ca 10 mins 32 bits will not be enough... This expression has to be changed/factorized or so in some "sane" way... tempomap.addTempo(ltick, tempo); } break; case 0x58: // Time Signature { int timesig_z = data[0]; int n = data[1]; int timesig_n = 1; for (int i = 0; i < n; i++) timesig_n *= 2; int ltick = CALC_TICK(tick); //(tick * config.division + div/2) / div; ///sigmap.add(ltick, timesig_z, timesig_n); AL::sigmap.add(ltick, AL::TimeSignature(timesig_z, timesig_n)); } break; case 0x59: // Key Signature // track->scale.set(data[0]); // track->scale.setMajorMinor(data[1]); break; default: printf("unknown Meta 0x%x %d\n", ev.dataA(), ev.dataA()); } } break; } // switch(ev.type() if (!e.empty()) { e.setTick(tick); // Added by Tim. p3.3.8 //printf("buildMidiEventList: mel adding t:%d type:%d A:%d B:%d C:%d\n", tick, e.type(), e.dataA(), e.dataB(), e.dataC()); mel.add(e); } } // i != el->end() //--------------------------------------------------- // resolve NoteOff events //--------------------------------------------------- // for (iEvent i = mel.begin(); i != mel.end(); ++i) { // Event event = i->second; // if (event.isNote()) // event.setLenTick(0); // } // Added by Tim. p3.3.8 // The loop is a safe way to delete while iterating. bool loop; do { loop = false; for (iEvent i = mel.begin(); i != mel.end(); ++i) { Event ev = i->second; if (ev.isNote()) { if (ev.isNoteOff()) { iEvent k; bool found = false; for (k = i; k != mel.end(); ++k) { Event event = k->second; if (event.tick() > ev.tick()) break; if (event.isNoteOff(ev)) { ev.setLenTick(1); ev.setVelo(event.velo()); ev.setVeloOff(0); // Added by Tim. p3.3.8 //printf("buildMidiEventList: found note off: event t:%d len:%d type:%d A:%d B:%d C:%d ev t:%d len:%d type:%d A:%d B:%d C:%d\n", event.tick(), event.lenTick(), event.type(), event.dataA(), event.dataB(), event.dataC(), ev.tick(), ev.lenTick(), ev.type(), ev.dataA(), ev.dataB(), ev.dataC()); found = true; break; } } if (!found) { printf("NOTE OFF without Note ON tick %d type %d %d %d\n", ev.tick(), ev.type(), ev.pitch(), ev.velo()); } else { mel.erase(k); // Changed by Tim. p3.3.8 //continue; loop = true; break; } } // Added by Tim. p3.3.8 // If the event length is not zero, it means the event and its // note on/off have already been taken care of. So ignore it. if (ev.lenTick() != 0) { continue; } iEvent k; for (k = mel.lower_bound(ev.tick()); k != mel.end(); ++k) { Event event = k->second; if (ev.isNoteOff(event)) { int t = k->first - i->first; if (t <= 0) { if (debugMsg) { printf("Note len is (%d-%d)=%d, set to 1\n", k->first, i->first, k->first - i->first); ev.dump(); event.dump(); } t = 1; } ev.setLenTick(t); ev.setVeloOff(event.veloOff()); // Added by Tim. p3.3.8 //printf("buildMidiEventList: set len and velOff: event t:%d len:%d type:%d A:%d B:%d C:%d ev t:%d len:%d type:%d A:%d B:%d C:%d\n", event.tick(), event.lenTick(), event.type(), event.dataA(), event.dataB(), event.dataC(), ev.tick(), ev.lenTick(), ev.type(), ev.dataA(), ev.dataB(), ev.dataC()); break; } } if (k == mel.end()) { printf("-no note-off! %d pitch %d velo %d\n", ev.tick(), ev.pitch(), ev.velo()); // // switch off at end of measure // int endTick = song->roundUpBar(ev.tick() + 1); ev.setLenTick(endTick - ev.tick()); } else { mel.erase(k); // Added by Tim. p3.3.8 loop = true; break; } } } } while (loop); // DEBUG: any note offs left? // Removed by Tim. p3.3.8 //for (iEvent i = mel.begin(); i != mel.end(); ++i) { // Event ev = i->second; // if (ev.isNoteOff()) { // printf("+extra note-off! %d pitch %d velo %d\n", // i->first, ev.pitch(), ev.velo()); // ev.dump(); // } // } for (iEvent i = mel.begin(); i != mel.end(); ++i) { Event ev = i->second; if (ev.isNoteOff()) { printf("+extra note-off! %d pitch %d velo %d\n", i->first, ev.pitch(), ev.velo()); // ev.dump(); continue; } int tick = CALC_TICK(ev.tick()); //(ev.tick() * config.division + div/2) / div; if (ev.isNote()) { int lenTick = CALC_TICK(ev.lenTick()); //(ev.lenTick() * config.division + div/2) / div; ev.setLenTick(lenTick); } ev.setTick(tick); del->add(ev); } }