A note lives as two events - a note-on and a later note-off - but musicians think in whole notes with a duration. Today you pair them into notes, using absolute ticks to compute how long each sounds.
Pair each note-on with its matching note-off into a note with a start tick and duration.
Events are how a file stores music, but notes are how people hear it: a pitch
that starts, sounds for a while, and stops. Pairing turns the two-event
representation into one. Walk the events in order; on a note-on remember that
this (channel, note) is open and at what absolute tick it began; on the matching
note-off compute duration = offTick - onTick and emit a finished Note.
Two details make it correct. Key the open notes by channel and note number so overlapping notes on different keys pair with the right partners. And remember the velocity-0 rule from earlier: a note-on with velocity 0 is a note-off, so it must close an open note, not open a new one - the reason you folded that rule in at the source. A note-on of note 60 at tick 0 closed at tick 96 is a note of duration 96, one quarter at a division of 96.
type Note struct {Pitch, Velocity byteStartTick, Duration uint32}// key open notes by (channel, note); on note-off, duration = nowTick - startTickopen := map[[2]byte]int{} // (channel, note) -> index of the open note