if a slot becomes active, and the transport is not rolling, it still
asks the transport to roll. however, (1) there's only 1 request across
all triggerboxen (2) the request will not be handled till the next
process cycle (3) the triggerbox returns after the request i.e. it
waits until the transport is rolling
This is otherwise only caught later in the rt-thread after
scheduling a RealTimeOperation via AutomationControl::check_rt.
There is no need to schedule cross-thread events when the
value is not about to be changed.
This can greatly reduce the number of signals emitted by
restoring a slot, which improves performance and also prevents
the EventPool from filling up with useless events.
It can happen that the EventQueue fills up with
SessionEvent::RealTimeOperation. Those are to scheduled to be
free()ed later the GUI thread via event_loop->call_slot().
However it can happen that the GUI EventPool is full, so the request
to call Session::rt_cleanup, is never executed.
In this case the SessionEvent pool can fill up with RealTime
Operations which remain there permanently.
It can happen that ::get_request() returns NULL if the
EventPool is full. In that case the slot is never called.
In this case the caller can now take action.
Plugins on Apple can be multi-arch (Intel + ARM), or AUs may
be bridged by Rosetta.
A user can also run Intel binary of Ardour or M1 binary on
M1 CPUs on macOS. Each can see different plugins - notably
VST3 plugins that are Intel only can be scanned using the
Intel binary, but are later not available with the M1 build.
There is no longer an extra set of rt-threads, but existing
process-graph threads are reused.
There are two main benefits to this approach: graph-threads
have a SessioEvent pool and ProcessThread buffers. They are
also joined to work-groups (on macOS), or JACK created threads
(cgroups).
The process-graph trigger_queue only needs to call
::prep() and ::run() without knowing any further details.
This is in preparation for using the graph-threads for rt-tasks
Route::silence() runs plugins. With a heavy session
that relies on multi-core processing, sending silence
to processors will have to be done in parallel.
Otherwise it can result in large DSP load and xruns.
This may allow to revert b3497b3f8f, which was
a prior attempt to resolve this, before understanding
the actual cause.
When auditioning, ardour's process_audition calls Route::silence
for all routes. PluginInsert::silence simply fed all [ports of all
[replicated] plugins with silence, regardless of port-mapping.
This can cause some VST2/3 plugins to change state, and enable ports
(e.g. stereo VST2 plugins on mono tracks), causing glitches.
This commit is mainly intended to investigate how significant
this glitch is when starting to audition.
* don't create a new MixerScene if one already exists
* only resize the vector when necessary
* adding a new scene at position N should not shrink the vector size
Previously it was possible to cause a 64bit signed to 32bit
unsigned overflow. `from_stretcher` is pframes_t aka. uint32_t.
With int64_t arguments a std::min() expression producing negative
result will result in large 32bit values:
(pframes_t) std::min<int64_t>(1024, 176400 - 187392) = 4294956304
This produced a segfault when used as n_samples to copy in
buf.accumulate_from()
Because a bartime point IS-A tempo point and IS-A meter point, we cannot just delete the tempo
point passed into core_add_tempo() if the new point replaces an existing one. Ditto for meter.
So, leave that logic up to the caller
timecnt_t and timepos_t constructors with the initial argument as an int64_t
are assumed to be using samples. We need to use the explicit factory methods
instead.
Session handles the case when the button is used to enable cue triggering, by
forcing a locate to the current transport position.
TriggerBox now connects to the correct configuration object to notice when cue
triggering is disabled, and sets _cancel_locate_armed appropriately.
No behavior should be changed by this modification; the argument has a default value of false, which
matches previous semantics, and every instance where the argument is specified, it is given as false.
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 also allows to disable the resampler, effectively disabling
varispeed support, for the benefit of adding no additional latency.
By default 2 * 16 samples latency are added, due to port-resampler,
this is not desirable if Ardour is used as mixer only.
Session::route_processors_changed accumulates signals emitted
in realtime and processing is delegated to a dedicated rt-safe
thread. Previously this resulted in any changed to be converted
to a `GeneralChange`, which unconditionally triggered a route-reorder.
Record-arming a track causes a MeterPointChange (meters change to "in"),
and this caused routes to be resorted and a latency-update.
While the former is reasonable (Ardour prefers to process
rec-armed routes first), the latter certainly is not.
Even if we start at time T, if the tempo point before T is ramped, we need to
adjust it's omega value based on the beat time of the following tempo point. If
we don't do this, then using that tempo to compute ::superclock_at() for
subsequent points will be incorrect.
Note: there may be an ordering issue here with Tempo/Meter points. We might
need to pass over all tempo points first, then process meters etc. Something
like that.
libs/ardour/session_state.cc:1500:13: note: in instantiation of
function template specialization 'XMLNode::set_property<unsigned long>'
requested here
libs/pbd/pbd/string_convert.h:151:11: error:
type 'unsigned long' cannot be used prior to '::' because it
has no members
The master needs to be restored first, otherwise setting
the master value with be propagated. Furthermore the
actual value is saved at the time of VCA assignment.
Restoring the vaule needs to scale by the master-reduced value.
changes in tempo can be negative (decelerando/slowing down), and thus so can _omega. We cannot call log() or its
cousins on negative values, so in this scenario use an alternate expression for "t from b" in the tempo.pdf paper
We discovered in the past that the C++ API for GSource/Glib::Source has some fatal and unfixable flaws. Copy similar
code and just use the C API for GSource instead
This is based on code from earlier commits that were later reversed, but we need some mechanism
to ensure that threads have a thread local tempo map ptr set. The big difference is that this
time we do not implement this for all instances of an AbstractUI - implementation is left to
each thread/event loop
Events in the delay-buffer must be shifted back every cycle.
Also in case of fixed-delay-lines events may not be in sequence
since events are only sorted at backend port-level.
this solves the oft-recurring problem where we assign
a beat-count of '3' or '7' because minipm mis-detected the tempo,
and that situation is vanishingly rare. it's better to assume 4 or 8
the user always has the option to change the number of beats (and
therefore the detected tempo) manually, for those clips that are
in a different time signature. but minibpm cannot be expected
to help us there.
NOTE: this is a fallback to make things 'just work' lacking any other context.
* if the tempo is detected in the filename, we use that instead
* clips that were recorded to the timeline use that bpm+timesig
* in the future we can use file-metadata (acidized wave?) instead
* this is audio-only: midi files can have an embedded timesignature
but... given no other information, experience says the vast majority
of downloaded/purchased clips will be 1,2 or 4 bars at 4/4.
omega can be computed from Beat or superclock duration. This gives rise to
different units for omega, and we must use the correct value in a given
context.
This commit also changes the way that the audio time omega is computed during
TempoMap::reset_starting_at()
Rampable only existed to provide exclusive access to ::set_end() for the
TempoMap. More idiomatic C++ but now that _type has also gone away, so has
::set_ramped() and it really was not worth keeping it around.
Ramped/Constant is really a function of start/end note_types_per_minute. Having
a separate member is really just caching it and leads to errors or risk thereof.
MidiBuffer::read_from() clears the buffer, so events were not
accumulated into the delay-buffer.
This also includes a small optimization and comments for the
variable delayline.
* when you copy a Range into a playlist, the playlist's 'timeline' should
begin at the start of the selected Range. Regions should retain the
offset they had from the Range's start.
* this fixes the Draw tool when adding notes; if you tried to draw in
a region with a trimmed front, the note would not get added in the correct
location
syntax for beginning and ending a diff command is:
"new_diff_command" -> "apply_diff_command"
syntax for applying a diff_command is:
"_as_commit" : Begins and Commits a standalone undo Command
"_as_subcommand" : adds to undo but does not Begin or Commit a Command
"_only" : (new) applies the note_diff but does not have any effect on undo
This is useful after a freewheel export, where a realtime-stop
is called, but the actual Locate/MustStop event is only processed
later. Once the session switches back to normal processing after
export the transport is still rolling, TSFM schedules a de-click
locate. This may play some remaining audio.
This new API allows to hard stop the transport, without going
via any session-events and process_with_events. It is intended to
be called from the freewheel process-callback in the last export
cycle.
The idea is to run a plugin outside the process graph, and provide
its I/O as port (much like an external JACK app).
The intended use-case is NDI (provide additional I/O), but it could
also be useful for other cases.
This allows to pass any GraphChain to the Graph to process.
It removes the need to use a mutex to swap two dedicated
chains (setup-chain <> active-chain, pending-chain).
Also various special cases pertaining to graph interaction
while auditioning and route-deletion can be removed.
This also unconditionally creates a graph-thread for GraphChains
to be processed, even if the main callback uses a special-cased
sorted RouteList if there is only one process thread.
The process-graph should only be concerned with GraphNodes,
which may or may not be Routes.
This also removes intrinsic connection information from
the graph-node. Connection information is to be kept separate
from the nodes.
When the graph is re-calculated in the background, old information
has to be retained until the new graph becomes active.
Previously *new* information was already stored in the nodes
while the graph is sorted, even though the new graph was not
active.
In all years of using these assert()s never triggered. Besides
there are valid_port() tests in other strategic locations that
are not periodically hit in realtime context.
The set uses a custom sort-by-name comparator.
Previously it was possible to have an inconsistent set iterator.
std::set::find() did not find a given port while std::find() did.
This fixes using std::set::find() on the PortIndex set.
Temporal::most_recent_engine_sample_rate is used in performance-critical code so shouldn't be accessed via a 'get()' function. But (via the TEMPORAL_SAMPLE_RATE #define) it does get accessed outside of libtemporal and therefore needs to get exported.