I'm not sure if this is really the best way to do event types (should it
just be a completely static enum in evoral, or completely dynamic and
provided by the type map, or a mix like currently?), but previously the
event type was frequently set to either total garbage, or parameter
types, which are a different thing.
This fixes all those cases, and makes Evoral::EventType an enum so the
compiler will warn about implicit conversions from int.
It is slightly questionable whether type specific methods like
velocity() belong on Event at all, these may be better off as free
functions. However the code currently uses them as methods in many
places, and it seems like a step in the right direction, since, for
example, we might some day have events that have a velocity but aren't
stored as MIDI messages (e.g. if Ardour uses an internal musical model
that is more expressive).
In any case, the former inheritance and plethora of sloppy casts is
definitely not the right thing.
eg. import a .mid that has a CC later in the file.
Arodur wrongly added an initial point, effectively moving the event
backwards to "0" (no virgin territory)
Iterating over a const Midi-Sequence calls Evoral::Sequence::set_event(),
which in turn used Evoral::Event::operator=() which always created
a new event-ID (create copy of the event).
Issues fixed:
- Saving *unmodified* MIDI produced new event-IDs on every save;
files changed with every save. - greetings to Deva.
- all [GUI] operations that use IDs to refer to notes e.g. undo.
invalid undo-history.
Also clarify assignment operator name. Prefer explicit assign() over =.
This avoids stuck notes if active notes are edited, but without stopping all
active notes in the region on any edit as before.
This implementation injects note ons in places that aren't actually note
starts. Depending on how percussive the instrument is, this may not be
desired. In the future, an option for this would be an improvement, but there
are other places where "start notes in the middle" is a reasonable option. I
think that should be handled universally if we're to do it at all, so not
considering it a part of this fix for now.
Silly to make a junk Note just to pass to append_note_off_unlocked, which just
uses the fields that are on the MIDIEvent anyway then throws it away.
Also explicitly dispatch to append_note_off_unlocked in the caller for note ons
with velocity 0 rather than make append_note_on_unlocked deal with it.
Among other things, this means that automation controls/lists have the actual
min/max/normal/toggled of parameters, and not those inferred from the Parameter
ID, which is not correct for things like plugin parameters.
Pushing things down to the Evoral::ParmeterDescriptor may be useful in the
future to have lists do smarter things based on parameter range, but currently
I have just pushed down the above-mentioned currently used attributes.
This lets us get a more explicit handle on time conversions, and is the main
step towards using actual beat:tick time and getting away from floating point
precision problems.
Fix initial read of discrete MIDI controllers.
Fix spurious note offs when starting to play in the middle of a note.
Faster search for initial event when cached iterator is invalid.
So much for dropping the cached iterator. The iterator is responsible for
handling note offs, so that doesn't work. This design means we have some stuck
note issues at the source read level, but they should be taken care of by the
state tracker anyway.
This cleans up a lot of false-positives in static analysis
and also helps compilers to optimize code paths in general.
(tagging the fatal stingstream operator as ‘noreturn’ is
far less trivial)