Original code is of questionable historical provenance, and
was needlessly (it seems) complexity. New code is relatively
simple arithmetic linear interpolation.
The actual goal here is to use direct InterpolationStyle serialization
in MidiSource (identical to AutomationList). enum_2_string()
does not work for Evoral types.
As side-effect virtual base-classes have been changed to pass
Parameters as const references
Automation Controls (and controls in general) are now
only updated in realtime context. Either via automation-playback,
or via SessioEvent. This directly sets the Control:_user_value
(before emitting the Changed signal).
The GUI does not need to evaluate the control at a given point
in time, so the API call can be removed and unified.
This commit first removes all calls to "get_double" to ensure
that no special cases exist.
This uses boost::multiprecision::int512_t when multiplying and dividing by the numerator
and denominator of a ratio_t. 128 bits would be sufficient but for some reason, the boost
docs show the 512 bit variant being very slightly faster.
This is a better solution than using a double, which although it will prevent overflow
has fairly limited resolution.
Region fades would sometimes get in a mode with weird behaviour. They
would be drawn in 2d with crossing lines, mainly moving back and forth
horizontally - not as a function of time. It would sound as it looked.
The fade would sometimes jump around when resizing. It could be worked
around by resetting the fade shape. It turned out the problem could be
reproduced by making minute long fades.
This change fixes or works around the problem.
Back story:
timepos_t (in temporal/timeline.h) uses 62 bit for integer value and the
max value is thus 4611686018427387904 ~ 5e18. timepos_t counts
superclocks, where superclock_ticks_per_second is 56448000 ~ 6e7. It can
thus store up to 8e10 seconds - thousands of years.
ratio_t (in temporal/types.h) can represent fractions as 64 bit (signed)
numerator and denominator. timepos_t avoids floating point operations,
but has operator* with ratio_t. To avoid crazy loss of precision it will
multiply the superclock count with the numerator before dividing with
the denominator.
Audio region fade in and out uses a number of increasing timepos_t
values (in a ControlList) up to the length of fade. When dragging to
resize, these values are (in_x_scale) multiplied with the ratio_t of the
new and old fade length. The problem is that the 62 bits will overflow
if using fades more than sqrt(5e18) ~ 2e9 superclock ticks ~ 38 seconds.
It will overflow into the "beat" flag and (at 58 seconds) into the sign
bit. The timepos_t values in the fade will thus jump and can be negative
or change to count beats.
To work around that problem, this changeset just use floating point
values for scaling the timepos_t values. All scaled values are stored as
integer anyway, so it should not make any actual difference for this use
case. There might however be other uses of ControlList where it matters.
As an implementation detail of this "workaround" of using double, it
could perhaps also be nice to implement timepos_t operator* (or
operator*=) for double. But I'm not sure we want floating point support
in timepos_t.
An alternative (and better) solution would be to convince the fraction
multiplication to use 128 bits. It is essential to avoid overflow -
mainly in static analysis, alternatively as runtime checks or asserts.
This note-mode had no effect on anything at all, at least as far back
as 5.12. There is a note-mode in the GUI which affects the duration of notes
added using the GUI, and that remains in place. It is not clear
if the _percussive member of Evoral::Sequence ever had any effect on
the actual MIDI event stream the Sequence could generate.
Code within the method was using @param start_time rather than start, which is a modified
value required to generate the correct results.
This comment also contains some logical reordering, optimization and commenting
on this rather complex method.
Note that the value is still defined in Beats (ticks) rather than seconds
which means that the interpolation density is tempo-dependent. This
should still likely change one day.
Region-gain (unlike other automation) is specific to the
region and independent of the source. Region::start() offset
does not apply. When region-start is trimmed the region's
envelope is modified (not just offset). The event-list is truncated.
Any audio-region envelope does (and must) have a point exactly
at the start and end of the region.
truncate_start() can thus calculate the earliest position of
valid events with the new length relative to the last event.
The mathematical operator for that is subtraction, not distance.
we screen midi files for some aggregate info:
used channels, used patches, and note-count
you can't do this from open() because there are cases (after importing)
when the source exists but it is not yet written to disk
Files that have many tracks, each with tempo information
were near impossible to load (30+ mins on modern 4.2Ghz CPU!),
because tempo is parsed incrementally:
```
For each new track:
for each new tempo-event:
rewind()
for each loaded track so far:
for each event on this track so far
```
This reduces the complexity from O(tracks^2 * tempos^2)
to O(tracks * tempos).
"Come Thou Fount Tempo Map.mid" has 238 Tracks and 56168 total
Tempo Changes (236 per track). This now requires only 56168 iterations
in smf_create_tempo_map_and_compute_seconds, rather than 1.64e+9
iterations
Now that we use superclock_t for audio time, it is possible for the square of an audio time value to overflow int64_t quite easily.
This change fixes that (and cleans up other code a little), but probably a different solution would be a good idea