Fixes bug #6166 (except record).
This attempts to follow the "current" control value somewhat aggressively:
* On locate, slider is set to the value from the top region at the new
transport position.
* Playback or MIDI input is followed "live".
* Whenever the slider is moved (including automatically), that value is emitted
as an immediate event to keep external gear in sync.
General idea is that the Ardour slider should act as a mirror of an external
hardware knob, and both should be synced to whatever the control is at the
current transport position. Since we lack real playback/touch/etc modes for
these for now, we must choose one behaviour, and this seems like the most
reasonable one.
Follow is handled in the audio thread, which is probably not ideal, but since
these controls have no lists and do not record, should be fine. Probably.
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.
Best to just do this as early as possible to avoid having to deal with this
situation all over the code.
Also fixes violation of LV2 MIDI specification, which requires no such events
are delivered to plugins.
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.
Add a test, based on the worked example in www.korf.co.uk/spline.pdf, for
the constrained cubic spline interpolation.
The delta values for the float comparisons are rather arbitrary, I'm sorry
to say: they're basically chosen so that everything passes.
When the crossfade length is only 1 frame, I got strange
gain coefficients from get_vector (63 in my case).
The function wrongly returned the x axis value.
We're still a very long way from tolerant of weird SMF files (libsmf takes a
"crash if input is not exactly perfect" philosophy, if we're going to be polite
and elevate such a thing to "philosophy"), but at least we'll get what's there
from files truncated by old broken versions of Ardour or other situations.
This was a very clever attempt to fix a non-problem. If the platform doesn't have enough file descriptors available
then the platform is broken and we're not going to hack around trying to fix it.
For things like copying from pitch bender to a CC.
Also things like fader to pan, but that seems a bit funny. The conversion
probably needs to be a bit smarter here, perhaps taking the normal into
consideration...
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.
Remove old (already #if 0'ed) implementation of Evoral::coverage() and its
comments.
Tidy up the comment enumerating all the possible ways in which two ranges
can overlap, note the Evoral::OverlapType corresponding to each one, and add
comments to the if()s in coverage corresponding to the cases in the list of
overlap types.
Remove some commented-out assert()s that actually do happen, and re-instate
one that really shouldn't.
Fix a small typo (with -> within)
The various conditionals in Evoral::RangeList::subtract() appear to have
been there to work around
(a) coverage() not always returning the correct value, &
(b) the test suite assuming that the ->to point lies outside the range
Now that these are both fixed, the implementation of subtract() becomes
quite a bit clearer. I replaced the if()s with assert()s for now, but these
shouldn't trip if coverage() is working as I expect.
Also (attempt to) clarify the comments in subtract.
Rewrite Evoral::coverage() to (hopefully) do what it's supposed to.
Return OverlapNone for invalid ranges: if either of the ranges passed to
Evoral::coverage() have negative length (i.e. start > end), return OverlapNone
- it seems reasonable to say that a negative-length range can't overlap
anything. Also return OverlapNone from the fallthrough case, though this should
never happen.
Some of the tests for Evoral::RangeList::subtract() assume that ranges
don't contain their end (->to) point. This appears inconsistent with how
they are used elsewhere.
Add some ASCII art comments to the tests to try to clarify what they're
really testing for, and amend subtractTest1, subtractTest4, & subtractTest5
to incorporate the assumption that ranges include their end points.
This is not used anywhere in Evoral and is just a wrapper around the PBD
RingBuffer anyway. Towards a (once again?) independently buildable/testable
Evoral and fewer cross-dependencies.
Add a test function to test Evoral::coverage() with all possible overlap
types. The first test (line 161) that expects OverlapExternal will fail
with the current implementation of coverage().
There's possibly still a discussion to be had about what the overlap type of
ranges with negative lengths should be: there are currently places in the main
Ardour code base where coverage() is called with ranges where start > end.
Fix compile errors in libs/evoral/test/, by explicitly calling
Evoral::MusicalTime::to_double() wherever a double value is required of a
MusicalTime.
Some of the double variables should probably really be made into MusicalTime
ones instead, but I don't want to mess with this too much.
takeFiveTest still fails for me after this, but a failing test is probably
more informative in the long run than a test that won't even compile.
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 should probably hijack the same modifier as the guard points and work the
same on all automation tracks, but I did it this way to not change behaviour of
track automation where a default is much more reasonable.
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)
Serialization of Session::save_state() will already protect against most of this, but there is really no
good reason why Evoral::SMF's API should require single-threaded/explicit serialization.
Also some work towards tolerating automation controls with no automation list,
towards actually doing something for these cases, though not required just to
fix this crash (MidiTrack::set_parameter_automation_state() avoids those
paths).
It'd be nice if we could use 'ARDOUR::config_dir_name' for this purpose (or perhaps 'PROGRAM_VERSION'). However, neither is implemented widely enough at present to make this practical. Keep an eye on them though, as possible future strategies.
TODO: needs undo. only works in top quarter of automation lane. selection model feels weird sometimes. needs to show gain curve when you are using Range tool
Constrain control points to one per tick (1/1920 beats).
Prior to this it was possible to set two values to the
same time (interpolation and iteration failed).