MidiMessageSequence and updateMatchedPairs()

I see that this has sort of come up before on the forums but I couldn't see any resolution.

I'm writing a MIDI editor and started considering the use of MidiMessageSequence as a container for events but I'm now wondering if that's the wrong choice.  The main problem is that the class doesn't continuously enforce matched note on/off pairs. eg:

- You can remove a note off event without removing the note on.  

- Copying the sequence to another one doesn't copy the matched pair info

- addSequence() has no intelligence about adding mismatched pairs (ie. it'll happily copy over note ons without their corresponding note offs if they fall outside range, and note offs without note ons). 

- updateMatchedPairs() always discards any current pairing info and begins from scratch.  It also does some odd things by artificially adding extra note offs as soon as it encounters a note on with the same note number before that note has been turned off.  Although I agree that the note on will need a note off somewhere, it doesn't need to occur before another note on, and presumably this action will often leave the 'real' note off for that event still hanging around somewhere later on in the sequence.  The one situation where I would expect it to add extra note offs is where it comes to the end of the sequence and finds there are more ons than offs (ie. hanging notes) but it doesn't do this.

 

Maybe I could wrap it in a higher level class to enforce a more strict behaviour?  I'd be also tempted to modify MidiMessage sequence so that MidiEventHolders are doubly linked - ie. those containing note off messages also have a 'back link' to the corresponding note on, in the same way the holder with the note on has a forward link to the note off.

What I'm asking in a rambling way is, has anyone else applied MidiMessageSequence successfully to anything other than holding fairly static MIDI file data, and did you encounter/solve any of these problems?

Hi, CowTipper,

Sorry for the computer English. I hope you will understand the meaning…

Not sure that this is the ideal solution, but I'm doing the following manipulations to work with notes from input MIDI sequences.

1. Create component "Note" with graphic painting and mouse listener.

2. Create your OwnedArray of notes in pianoRoll component class.

3. When importing inputs midiSequence from MIDI file, create a new instance for each note component "note" and immediately fill properties for every "note": noteKey, noteStart, noteLenth, noteVelocity in the drawing grid coordinates pianoRoll.

Similarly, if it is necessary, add related settings controller component properties for any "note" (for example, pitchWheel). This allows you to "notes" when moving around the screen synchronously pianoRoll shift all bound to note events controllers and automatically scale them fill density X when you change the length of the notes.

4. After convert MIDI events in the "note" object properties work in pianoRoll with array elements: remove, add, and edit notes, move its...

5. Convert back from an array of notes in a MIDI sequence-when you save your project.


Hi CowTipper,

I don't really appreciate your concerns about missing noteoffs, at least if your talking about a graphical midi editor. Like Aziop says, you probably put a bunch of midinotes graphically on a grid. Then you identify one or more by clicking with your mouse and look up their index in the midiMessageSequence using getNextIndexAtTime and ​getEventPointer​. Now you have both the noteon and noteoff and can update their timestamp, velocity, midinotenumber or whatever. Then in most case you need to do mms.sort and mms.updatematchedpairs. And then you're done; the mms now ​resembles your edited sequence.


But I agree with you regarding the way how updateMatchedPairs handles consecutive noteons. It's not ideal. You might have e.g. several noteons for a gong you don't want disturbed with intervening noteoffs. Ideally updateMatchedPairs should have a parameter with at least three values:
insertNoteOffs (like today), addTrailingNoteOffs (add any missing note of at the end of the sequence) or - dontMess which
silently ignores any missing noteoffs.

I'm really just saying that addSequence() will copy a section from one MidiMessageSequence to another but makes no attempt to deal with note pairs, leaving 'dangling' note ons and notes offs in the destination sequence.

Furthermore, updateMatchedPairs() makes no attempt to preserve existing on/off pairs and overwrites them with its own idea of what they should be.

In a piano roll you should be able to drag, resize, overlap notes to your heart's content, all the time preserving the same note on/off pairing (ie. more like a start + duration rather than a start + end).  MidiMessageSequence doesn't look like it's set up for this due to the issues I mention above.  If you were to draw some notes on top of each other in the UI and then call mms.updatematchedpairs() as you suggest, it'll screw up your sequence.  You're actually a lot better off not calling updateMatchedPairs() and doing it all yourself if you care about that.

I'm really just trying to understand the rationale behind MidiMessageSequence.  Is it intended as a container for general sequence editing, or is it more just for use by the MidiFile class?  I think it's probably the latter.

 

Well, you could roll your own version om midimessagesequence that keeps a strong bond between note on and noteoff through all kinds of sequnece adding and sorting. The sad thing however is that when you at the end of day want to store your editing effort in a midifile your editors overlapping notes will be squashed into a non overlapping sequence of midi events. When reading that file into another midi application (or even back into your own) there's no way to to know if the sequence noteon noteon notoff noteoff with the same midinotenumbers means

start note 1, start note 2, stop note 1, stop note 2 or
start note 1, start note 2, stop note 2, stop note 1

So I guess one rationale behind midimessagesequence is "why bother keep an order that eventually inevitably will get lost anyway?"

MidiMessageSequence uses the same representation as a standard midi file. That's its purpose, it's not designed to be directly manipulated in a midi editor.

In Tracktion for example we have our own architecture of note and controller objects which is designed for editing, and which we edit/save/load via a ValueTree, and which gets converted into a MidiMessageSequence only when it gets played. But these classes are all too customised to go into juce as generic library code, and you'd need to write something similarly appropriate for your own app if you're building a midi editor.

jules - thanks for the clarification and hints about the way you use it, this really helps me put the class in context.

oxxyyd - yes I agree about MIDI files, but the more common operation would be to serialise the sequence in some proprietary format which would preserve such stuff as note pairings.  Handling of MIDI files would be a much less common import/export operation in my case.

OK well after sleeping on it I've decided to go with my own format for storing/editing/serialising the sequence, probably storing notes in terms of note on time + duration rather than note on/note off.  From that I should be able to generate a MIDIBuffer on the fly for playback, or a MidiMessageSequence for MIDI file import/export.

Nothing can go wrong.  Thanks for your help everyone.