diff --git a/libmscore/rendermidi.cpp b/libmscore/rendermidi.cpp index 6a65776e2cdcb..d5dd42442f955 100644 --- a/libmscore/rendermidi.cpp +++ b/libmscore/rendermidi.cpp @@ -158,12 +158,13 @@ void Score::updateChannel() //--------------------------------------------------------- static void playNote(EventMap* events, const Note* note, int channel, int pitch, - int velo, int onTime, int offTime) + int velo, int onTime, int offTime, int staffIdx) { if (!note->play()) return; velo = note->customizeVelocity(velo); NPlayEvent ev(ME_NOTEON, channel, pitch, velo); + ev.setOriginatingStaff(staffIdx); ev.setTuning(note->tuning()); ev.setNote(note); events->insert(std::pair(onTime, ev)); @@ -175,7 +176,7 @@ static void playNote(EventMap* events, const Note* note, int channel, int pitch, // collectNote //--------------------------------------------------------- -static void collectNote(EventMap* events, int channel, const Note* note, int velo, int tickOffset) +static void collectNote(EventMap* events, int channel, const Note* note, int velo, int tickOffset, int staffIdx) { if (!note->play() || note->hidden()) // do not play overlapping notes return; @@ -208,7 +209,7 @@ static void collectNote(EventMap* events, int channel, const Note* note, int vel } else { // recurse - collectNote(events, channel, n, velo, tickOffset); + collectNote(events, channel, n, velo, tickOffset, staffIdx); break; } if (n->tieFor() && n != n->tieFor()->endNote()) @@ -243,7 +244,7 @@ static void collectNote(EventMap* events, int channel, const Note* note, int vel int off = on + (ticks * e.len())/1000 - 1; if (tieFor && i == nels - 1) off += tieLen; - playNote(events, note, channel, p, velo, on, off); + playNote(events, note, channel, p, velo, on, off, staffIdx); } // Bends @@ -269,6 +270,7 @@ static void collectNote(EventMap* events, int channel, const Note* note, int vel int msb = midiPitch / 128; int lsb = midiPitch % 128; NPlayEvent ev(ME_PITCHBEND, channel, lsb, msb); + ev.setOriginatingStaff(staffIdx); events->insert(std::pair(lastPointTick, ev)); lastPointTick = nextPointTick; continue; @@ -296,11 +298,13 @@ static void collectNote(EventMap* events, int channel, const Note* note, int vel int msb = midiPitch / 128; int lsb = midiPitch % 128; NPlayEvent ev(ME_PITCHBEND, channel, lsb, msb); + ev.setOriginatingStaff(staffIdx); events->insert(std::pair(i, ev)); } lastPointTick = nextPointTick; } NPlayEvent ev(ME_PITCHBEND, channel, 0, 64); // 0:64 is 8192 - no pitch bend + ev.setOriginatingStaff(staffIdx); events->insert(std::pair(tick1+noteLen, ev)); } } @@ -355,9 +359,11 @@ static void collectMeasureEvents(EventMap* events, Measure* m, Staff* staff, int Chord* chord = static_cast(cr); Staff* staff = chord->staff(); + int staffIdx = staff->idx(); int velocity = staff->velocities().velo(seg->tick()); Instrument* instr = chord->part()->instrument(tick); int channel = instr->channel(chord->upNote()->subchannel())->channel; + events->registerChannel(channel); foreach (Articulation* a, chord->articulations()) { instr->updateVelocity(&velocity,channel, a->subtypeName()); @@ -365,11 +371,11 @@ static void collectMeasureEvents(EventMap* events, Measure* m, Staff* staff, int for (Chord* c : chord->graceNotesBefore()) { for (const Note* note : c->notes()) - collectNote(events, channel, note, velocity, tickOffset); + collectNote(events, channel, note, velocity, tickOffset, staffIdx); } foreach (const Note* note, chord->notes()) - collectNote(events, channel, note, velocity, tickOffset); + collectNote(events, channel, note, velocity, tickOffset, staffIdx); #if 0 // TODO: add support for grace notes after - see createPlayEvents() @@ -377,7 +383,7 @@ static void collectMeasureEvents(EventMap* events, Measure* m, Staff* staff, int chord->getGraceNotesAfter(&gna); for (Chord* c : gna) { for (const Note* note : c->notes()) - collectNote(events, channel, note, velocity, tickOffset); + collectNote(events, channel, note, velocity, tickOffset, staffIdx); } #endif @@ -407,6 +413,7 @@ static void collectMeasureEvents(EventMap* events, Measure* m, Staff* staff, int for (MidiCoreEvent event : nel->events) { event.setChannel(channel); NPlayEvent e(event); + e.setOriginatingStaff(firstStaffIdx); if (e.dataA() == CTRL_PROGRAM) events->insert(std::pair(tick-1, e)); else @@ -643,43 +650,44 @@ void Score::renderStaff(EventMap* events, Staff* staff) // renderSpanners //--------------------------------------------------------- -void Score::renderSpanners(EventMap* events, int staffIdx) +void Score::renderSpanners(EventMap* events) { foreach (const RepeatSegment* rs, *repeatList()) { int tickOffset = rs->utick - rs->tick; int utick1 = rs->utick; int tick1 = repeatList()->utick2tick(utick1); int tick2 = tick1 + rs->len(); - std::map>> channelPedalEvents = std::map>>(); + std::map>>> channelPedalEvents; for (const auto& sp : _spanner.map()) { Spanner* s = sp.second; - if (s->type() != Element::Type::PEDAL || (staffIdx != -1 && s->staffIdx() != staffIdx)) + if (s->type() != Element::Type::PEDAL) continue; + int staff = s->staffIdx(); int idx = s->staff()->channel(s->tick(), 0); int channel = s->part()->instrument(s->tick())->channel(idx)->channel; - channelPedalEvents.insert({channel, std::vector>()}); - std::vector> pedalEventList = channelPedalEvents.at(channel); - std::pair lastEvent; + channelPedalEvents.insert({channel, std::vector>>()}); + std::vector>> pedalEventList = channelPedalEvents.at(channel); + std::pair> lastEvent; if (!pedalEventList.empty()) lastEvent = pedalEventList.back(); else - lastEvent = std::pair(0, true); + lastEvent = std::pair>(0, std::pair(true, staff)); if (s->tick() >= tick1 && s->tick() < tick2) { // Handle "overlapping" pedal segments (usual case for connected pedal line) - if (lastEvent.second == false && lastEvent.first >= (s->tick() + tickOffset + 2)) { + if (lastEvent.second.first == false && lastEvent.first >= (s->tick() + tickOffset + 2)) { channelPedalEvents.at(channel).pop_back(); - channelPedalEvents.at(channel).push_back(std::pair(s->tick() + tickOffset + 1, false)); + channelPedalEvents.at(channel).push_back(std::pair>(s->tick() + tickOffset + 1, std::pair(false, staff))); } - channelPedalEvents.at(channel).push_back(std::pair(s->tick() + tickOffset + 2, true)); + channelPedalEvents.at(channel).push_back(std::pair>(s->tick() + tickOffset + 2, std::pair(true, staff))); } if (s->tick2() >= tick1 && s->tick2() <= tick2) { int t = s->tick2() + tickOffset + 1; if (t > repeatList()->last()->utick + repeatList()->last()->len()) t = repeatList()->last()->utick + repeatList()->last()->len(); - channelPedalEvents.at(channel).push_back(std::pair(t, false)); + channelPedalEvents.at(channel).push_back(std::pair>(t, std::pair(false, staff))); } } @@ -687,10 +695,11 @@ void Score::renderSpanners(EventMap* events, int staffIdx) int channel = pedalEvents.first; for (const auto& pe : pedalEvents.second) { NPlayEvent event; - if (pe.second == true) + if (pe.second.first == true) event = NPlayEvent(ME_CONTROLLER, channel, CTRL_SUSTAIN, 127); else event = NPlayEvent(ME_CONTROLLER, channel, CTRL_SUSTAIN, 0); + event.setOriginatingStaff(pe.second.second); events->insert(std::pair(pe.first, event)); } } @@ -1562,22 +1571,29 @@ void Score::renderMetronome(EventMap* events, Measure* m, int tickOffset) //--------------------------------------------------------- void Score::renderMidi(EventMap* events) + { + renderMidi(events, true, MScore::playRepeats); + } + +void Score::renderMidi(EventMap* events, bool metronome, bool expandRepeats) { updateSwing(); createPlayEvents(); - updateRepeatList(MScore::playRepeats); - _foundPlayPosAfterRepeats = false; + updateRepeatList(expandRepeats); updateChannel(); updateVelo(); // create note & other events foreach (Staff* part, _staves) renderStaff(events, part); + events->fixupMIDI(); // create sustain pedal events - renderSpanners(events, -1); + renderSpanners(events); + if (!metronome) + return; // add metronome ticks foreach (const RepeatSegment* rs, *repeatList()) { int startTick = rs->tick; diff --git a/libmscore/score.h b/libmscore/score.h index 1627dc7f81da0..28c24fd5d6c14 100644 --- a/libmscore/score.h +++ b/libmscore/score.h @@ -398,10 +398,6 @@ class Score : public QObject, public ScoreElement { int _pos[3]; ///< 0 - current, 1 - left loop, 2 - right loop - bool _foundPlayPosAfterRepeats; ///< Temporary used during playback rendering - ///< indicating if playPos after expanded repeats - ///< has been calculated. - int _fileDivision; ///< division of current loading *.mscx file int _mscVersion; ///< version of .mscx file during file read, then changed to MSCVERSION for drag and drop int _mscRealVersion; ///< keep the actual and initial version of current loaded *.mscx file @@ -494,6 +490,10 @@ class Score : public QObject, public ScoreElement { FileError read114(XmlReader&); FileError read1(XmlReader&, bool ignoreVersionError); + void renderStaff(EventMap* events, Staff*); + void renderSpanners(EventMap* events); + void renderMetronome(EventMap* events, Measure* m, int tickOffset); + protected: void createPlayEvents(Chord*); void createGraceNotesPlayEvents(QList gnb, int tick, Chord* chord, int& ontime); @@ -807,9 +807,7 @@ class Score : public QObject, public ScoreElement { PasteStatus pasteStaff(XmlReader&, Segment* dst, int staffIdx); void pasteSymbols(XmlReader& e, ChordRest* dst); void renderMidi(EventMap* events); - void renderStaff(EventMap* events, Staff*); - void renderSpanners(EventMap* events, int staffIdx); - void renderMetronome(EventMap* events, Measure* m, int tickOffset); + void renderMidi(EventMap* events, bool metronome, bool expandRepeats); BeatType tick2beatType(int tick); diff --git a/mscore/exportmidi.cpp b/mscore/exportmidi.cpp index 57509437e066c..4b4e684ef7fe5 100644 --- a/mscore/exportmidi.cpp +++ b/mscore/exportmidi.cpp @@ -221,9 +221,9 @@ bool ExportMidi::write(const QString& name, bool midiExpandRepeats) for (int i = 0; i < cs->nstaves(); ++i) tracks.append(MidiTrack()); - cs->updateSwing(); - cs->createPlayEvents(); - cs->updateRepeatList(midiExpandRepeats); + EventMap events; + cs->renderMidi(&events, false, midiExpandRepeats); + pauseMap.calculate(cs); writeHeader(); @@ -235,11 +235,6 @@ bool ExportMidi::write(const QString& name, bool midiExpandRepeats) track.setOutPort(part->midiPort()); track.setOutChannel(part->midiChannel()); - // Render each staff only once - EventMap events; - cs->renderStaff(&events, staff); - cs->renderSpanners(&events, staffIdx); - // Pass throught the all instruments in the part const InstrumentList* il = part->instruments(); for(auto j = il->begin(); j!= il->end(); j++) { @@ -292,7 +287,10 @@ bool ExportMidi::write(const QString& name, bool midiExpandRepeats) } for (auto i = events.begin(); i != events.end(); ++i) { - NPlayEvent event(i->second); + const NPlayEvent& event = i->second; + if (event.getOriginatingStaff() != staffIdx) + continue; + char eventPort = cs->midiPort(event.channel()); char eventChannel = cs->midiChannel(event.channel()); if (port != eventPort || channel != eventChannel) diff --git a/mtest/libmscore/midi/testAndanteExcerpts-ref.txt b/mtest/libmscore/midi/testAndanteExcerpts-ref.txt index 2ff3f31e5215a..a4522420eacc1 100644 --- a/mtest/libmscore/midi/testAndanteExcerpts-ref.txt +++ b/mtest/libmscore/midi/testAndanteExcerpts-ref.txt @@ -65,8 +65,6 @@ Tick = 2880 Type = 144 Pitch = 67 Velocity = 49 Channel = Tick = 2880 Type = 144 Pitch = 55 Velocity = 49 Channel = 1 Tick = 2880 Type = 3 Pitch = 0 Velocity = 127 Channel = 0 Tick = 2999 Type = 144 Pitch = 55 Velocity = 0 Channel = 1 -Tick = 3120 Type = 144 Pitch = 62 Velocity = 49 Channel = 1 -Tick = 3239 Type = 144 Pitch = 62 Velocity = 0 Channel = 1 Tick = 3335 Type = 144 Pitch = 59 Velocity = 0 Channel = 1 Tick = 3335 Type = 144 Pitch = 62 Velocity = 0 Channel = 1 Tick = 3335 Type = 144 Pitch = 67 Velocity = 0 Channel = 1 @@ -193,13 +191,11 @@ Tick = 6947 Type = 144 Pitch = 66 Velocity = 0 Channel = Tick = 6959 Type = 144 Pitch = 81 Velocity = 0 Channel = 0 Tick = 6960 Type = 144 Pitch = 72 Velocity = 80 Channel = 0 Tick = 6960 Type = 144 Pitch = 69 Velocity = 74 Channel = 1 -Tick = 6960 Type = 144 Pitch = 69 Velocity = 74 Channel = 1 Tick = 6960 Type = 144 Pitch = 50 Velocity = 74 Channel = 1 Tick = 7079 Type = 144 Pitch = 72 Velocity = 0 Channel = 0 Tick = 7079 Type = 144 Pitch = 50 Velocity = 0 Channel = 1 Tick = 7080 Type = 144 Pitch = 69 Velocity = 80 Channel = 0 Tick = 7187 Type = 144 Pitch = 69 Velocity = 0 Channel = 1 -Tick = 7187 Type = 144 Pitch = 69 Velocity = 0 Channel = 1 Tick = 7199 Type = 144 Pitch = 69 Velocity = 0 Channel = 0 Tick = 7200 Type = 144 Pitch = 71 Velocity = 80 Channel = 0 Tick = 7200 Type = 144 Pitch = 67 Velocity = 80 Channel = 1 @@ -409,10 +405,8 @@ Tick = 11273 Type = 144 Pitch = 61 Velocity = 0 Channel = Tick = 11277 Type = 144 Pitch = 75 Velocity = 0 Channel = 0 Tick = 11280 Type = 144 Pitch = 76 Velocity = 64 Channel = 0 Tick = 11280 Type = 144 Pitch = 59 Velocity = 80 Channel = 1 -Tick = 11280 Type = 144 Pitch = 57 Velocity = 80 Channel = 1 Tick = 11393 Type = 144 Pitch = 59 Velocity = 0 Channel = 1 Tick = 11400 Type = 144 Pitch = 66 Velocity = 80 Channel = 1 -Tick = 11495 Type = 144 Pitch = 57 Velocity = 0 Channel = 1 Tick = 11507 Type = 144 Pitch = 57 Velocity = 0 Channel = 1 Tick = 11513 Type = 144 Pitch = 66 Velocity = 0 Channel = 1 Tick = 11519 Type = 144 Pitch = 76 Velocity = 0 Channel = 0 @@ -560,7 +554,6 @@ Tick = 16313 Type = 144 Pitch = 63 Velocity = 0 Channel = Tick = 16319 Type = 144 Pitch = 80 Velocity = 0 Channel = 0 Tick = 16320 Type = 144 Pitch = 81 Velocity = 64 Channel = 0 Tick = 16320 Type = 144 Pitch = 64 Velocity = 80 Channel = 1 -Tick = 16320 Type = 144 Pitch = 64 Velocity = 80 Channel = 1 Tick = 16320 Type = 144 Pitch = 59 Velocity = 80 Channel = 1 Tick = 16320 Type = 4 Pitch = 0 Velocity = 80 Channel = 0 Tick = 16348 Type = 144 Pitch = 81 Velocity = 0 Channel = 0 @@ -569,7 +562,6 @@ Tick = 16377 Type = 144 Pitch = 80 Velocity = 0 Channel = Tick = 16379 Type = 144 Pitch = 81 Velocity = 64 Channel = 0 Tick = 16407 Type = 144 Pitch = 81 Velocity = 0 Channel = 0 Tick = 16410 Type = 144 Pitch = 80 Velocity = 64 Channel = 0 -Tick = 16433 Type = 144 Pitch = 64 Velocity = 0 Channel = 1 Tick = 16438 Type = 144 Pitch = 80 Velocity = 0 Channel = 0 Tick = 16439 Type = 144 Pitch = 81 Velocity = 64 Channel = 0 Tick = 16440 Type = 144 Pitch = 66 Velocity = 80 Channel = 1 @@ -635,8 +627,6 @@ Tick = 17040 Type = 144 Pitch = 74 Velocity = 80 Channel = Tick = 17040 Type = 144 Pitch = 71 Velocity = 80 Channel = 1 Tick = 17040 Type = 144 Pitch = 62 Velocity = 80 Channel = 1 Tick = 17153 Type = 144 Pitch = 74 Velocity = 0 Channel = 1 -Tick = 17160 Type = 144 Pitch = 71 Velocity = 80 Channel = 1 -Tick = 17267 Type = 144 Pitch = 71 Velocity = 0 Channel = 1 Tick = 17267 Type = 144 Pitch = 62 Velocity = 0 Channel = 1 Tick = 17273 Type = 144 Pitch = 71 Velocity = 0 Channel = 1 Tick = 17279 Type = 144 Pitch = 81 Velocity = 0 Channel = 0 @@ -646,8 +636,6 @@ Tick = 17280 Type = 144 Pitch = 64 Velocity = 80 Channel = Tick = 17280 Type = 144 Pitch = 60 Velocity = 80 Channel = 1 Tick = 17280 Type = 3 Pitch = 0 Velocity = 127 Channel = 0 Tick = 17507 Type = 144 Pitch = 60 Velocity = 0 Channel = 1 -Tick = 17520 Type = 144 Pitch = 64 Velocity = 80 Channel = 1 -Tick = 17735 Type = 144 Pitch = 64 Velocity = 0 Channel = 1 Tick = 17747 Type = 144 Pitch = 64 Velocity = 0 Channel = 1 Tick = 17760 Type = 144 Pitch = 60 Velocity = 80 Channel = 1 Tick = 17760 Type = 4 Pitch = 0 Velocity = 80 Channel = 0 @@ -791,12 +779,10 @@ Tick = 21347 Type = 144 Pitch = 45 Velocity = 0 Channel = Tick = 21359 Type = 144 Pitch = 79 Velocity = 0 Channel = 0 Tick = 21360 Type = 144 Pitch = 81 Velocity = 80 Channel = 0 Tick = 21360 Type = 144 Pitch = 69 Velocity = 80 Channel = 1 -Tick = 21360 Type = 144 Pitch = 69 Velocity = 80 Channel = 1 Tick = 21360 Type = 144 Pitch = 54 Velocity = 80 Channel = 1 Tick = 21479 Type = 144 Pitch = 81 Velocity = 0 Channel = 0 Tick = 21480 Type = 144 Pitch = 72 Velocity = 80 Channel = 0 Tick = 21587 Type = 144 Pitch = 69 Velocity = 0 Channel = 1 -Tick = 21587 Type = 144 Pitch = 69 Velocity = 0 Channel = 1 Tick = 21587 Type = 144 Pitch = 54 Velocity = 0 Channel = 1 Tick = 21599 Type = 144 Pitch = 72 Velocity = 0 Channel = 0 Tick = 21600 Type = 144 Pitch = 71 Velocity = 80 Channel = 0 diff --git a/mtest/libmscore/midi/testKantataBWV140Excerpts-ref.txt b/mtest/libmscore/midi/testKantataBWV140Excerpts-ref.txt index 51807ffb20453..cf356b51a4157 100644 --- a/mtest/libmscore/midi/testKantataBWV140Excerpts-ref.txt +++ b/mtest/libmscore/midi/testKantataBWV140Excerpts-ref.txt @@ -564,22 +564,14 @@ Tick = 24240 Type = 144 Pitch = 68 Velocity = 80 Channel = Tick = 24479 Type = 144 Pitch = 68 Velocity = 0 Channel = 0 Tick = 24479 Type = 144 Pitch = 38 Velocity = 0 Channel = 2 Tick = 24480 Type = 144 Pitch = 70 Velocity = 80 Channel = 0 -Tick = 24480 Type = 144 Pitch = 70 Velocity = 80 Channel = 0 Tick = 24480 Type = 144 Pitch = 41 Velocity = 80 Channel = 2 Tick = 24480 Type = 4 Pitch = 0 Velocity = 80 Channel = 0 Tick = 24509 Type = 144 Pitch = 70 Velocity = 0 Channel = 0 -Tick = 24509 Type = 144 Pitch = 70 Velocity = 0 Channel = 0 Tick = 24510 Type = 144 Pitch = 68 Velocity = 80 Channel = 0 -Tick = 24510 Type = 144 Pitch = 68 Velocity = 80 Channel = 0 -Tick = 24539 Type = 144 Pitch = 68 Velocity = 0 Channel = 0 Tick = 24539 Type = 144 Pitch = 68 Velocity = 0 Channel = 0 Tick = 24540 Type = 144 Pitch = 70 Velocity = 80 Channel = 0 -Tick = 24540 Type = 144 Pitch = 70 Velocity = 80 Channel = 0 -Tick = 24569 Type = 144 Pitch = 70 Velocity = 0 Channel = 0 Tick = 24569 Type = 144 Pitch = 70 Velocity = 0 Channel = 0 Tick = 24570 Type = 144 Pitch = 68 Velocity = 80 Channel = 0 -Tick = 24570 Type = 144 Pitch = 68 Velocity = 80 Channel = 0 -Tick = 24599 Type = 144 Pitch = 68 Velocity = 0 Channel = 0 Tick = 24599 Type = 144 Pitch = 68 Velocity = 0 Channel = 0 Tick = 24600 Type = 144 Pitch = 67 Velocity = 80 Channel = 0 Tick = 24719 Type = 144 Pitch = 67 Velocity = 0 Channel = 0 diff --git a/synthesizer/event.cpp b/synthesizer/event.cpp index abd1d6e4ee375..5c20e8e985ac6 100644 --- a/synthesizer/event.cpp +++ b/synthesizer/event.cpp @@ -370,5 +370,45 @@ void EventList::insert(const Event& e) } append(e); } -} +//--------------------------------------------------------- +// class EventMap::fixupMIDI +//--------------------------------------------------------- + +void EventMap::fixupMIDI() + { + unsigned short nowPlaying[_highestChannel + 1][128 /* notes */]; + int originatingTrack[_highestChannel + 1][128 /* notes */]; + auto it = begin(); + + memset(nowPlaying, 0, (_highestChannel + 1) * 128 * sizeof(unsigned short)); + + while (it != end()) { + bool discard = false; + + /* ME_NOTEOFF is never emitted, no need to check for it */ + if (it->second.type() == ME_NOTEON) { + unsigned short np = nowPlaying[it->second.channel()][it->second.pitch()]; + if (it->second.velo() == 0) { + /* already off or still playing? */ + if (np == 0 || --np > 0) + discard = true; + else + /* hoist NOTEOFF to same track as NOTEON */ + it->second.setOriginatingStaff(originatingTrack[it->second.channel()][it->second.pitch()]); + } + else if (++np > 1) + discard = true; /* already playing */ + else + originatingTrack[it->second.channel()][it->second.pitch()] = it->second.getOriginatingStaff(); + nowPlaying[it->second.channel()][it->second.pitch()] = np; + } + + if (discard) + it = erase(it); + else + ++it; + } + } + +} diff --git a/synthesizer/event.h b/synthesizer/event.h index 24a182bea4387..bae47543afe85 100644 --- a/synthesizer/event.h +++ b/synthesizer/event.h @@ -235,6 +235,7 @@ class PlayEvent : public MidiCoreEvent { class NPlayEvent : public PlayEvent { const Note* _note = 0; + int _origin = -1; public: NPlayEvent() : PlayEvent() {} @@ -244,6 +245,8 @@ class NPlayEvent : public PlayEvent { NPlayEvent(BeatType beatType); const Note* note() const { return _note; } void setNote(const Note* v) { _note = v; } + int getOriginatingStaff() const { return _origin; } + void setOriginatingStaff(int i) { _origin = i; } }; //--------------------------------------------------------- @@ -311,7 +314,12 @@ class EventList : public QList { void insertNote(int channel, Note*); }; -class EventMap : public std::multimap {}; +class EventMap : public std::multimap { + int _highestChannel = 15; + public: + void fixupMIDI(); + void registerChannel(int c) { if (c > _highestChannel) _highestChannel = c; } + }; typedef EventList::iterator iEvent; typedef EventList::const_iterator ciEvent;