Previously the freewheel export thread directly called
Session::butler_transport_work(). The butler thread
may concurrently call the same function. This can lead
double free or memory corruption (see below)
Now export thread summons the butler and does nothing
until it completed its work.
```
Export Thread:
3 XMLNode::~XMLNode
4 ARDOUR::AutomationList::snapshot_history
5 ARDOUR::AutomationList::start_write_pass
6 ARDOUR::Automatable::non_realtime_locate
7 ARDOUR::Route::non_realtime_locate
8 ARDOUR::Session::non_realtime_locate
9 ARDOUR::Session::butler_transport_work
10 ARDOUR::Session::process_export_fw
Butler thread:
7 XMLNode::~XMLNode
8 ARDOUR::AutomationList::snapshot_history
9 ARDOUR::AutomationList::start_write_pass
10 ARDOUR::Automatable::non_realtime_locate
11 ARDOUR::Route::non_realtime_locate
12 ARDOUR::Session::non_realtime_locate
13 ARDOUR::Session::butler_transport_work
14 ARDOUR::Butler::thread_work
15 ARDOUR::Butler::_thread_work
```
The TransportFSM is now responsible for deciding what to do at all transport state transitions. The Session (via the TransportAPI) merely
provides mechanism (locate, start, stop, set_speed). Default and most recent speed requests are managed by the TransportFSM too
This increases timeout granularity and ensure that
rt (TSFM) and butler thread can run to prepare exports.
This may fix an issue some windows users reported
"Cannot prepare transport for export", and increasing
the buffersize to 1024 or 2048 works around the isssue.
This also fixes an edge-case usecs_per_cycle() may return -1
(sleep forever) when the engine dies.
When using the export-tool, the very first callback may already be
freewheeling. In this case the first call to the butler also happens
directly from the freewheel process-callback and initial session events
are handled there. Setting PostTransportAdjustPlaybackBuffering
took the process-lock, which caused a deadlock:
Glib::Threads::Mutex::Lock::Lock(Glib::Threads::Mutex&) at /usr/include/glibmm-2.4/glibmm/threads.h:687
ARDOUR::Session::butler_transport_work() at ../libs/ardour/session_transport.cc:1157
ARDOUR::Session::process_export_fw(unsigned int) at ../libs/ardour/session_export.cc:303
ARDOUR::AudioEngine::process_callback(unsigned int) at ../libs/ardour/audioengine.cc:486
ARDOUR::DummyAudioBackend::main_process_thread() at ../libs/backends/dummy/dummy_audiobackend.cc:951
In some rare cases the butler may be busy for a long time
(e.g. seek in a large session may still be active when
export is started).
8 second timeout seems reasonable to prevent the app from hanging
without desktops showing a "unresponsive" popup in case the
timeout is reached for some reason (e.g the engine dies, and
no TFSM transition can happen).
This apparently happens on some Windows systems when exporting
a range starting at 00:00:00:00
I'm still hoping there's a better fix for these race-condition
issues, perhaps by kicking the TFSM...
This picks up where cfd95340b1 left off.
The goal is to ensure that the butler has completed all
PostTransportStop related tasks and won't meddle with transport
after exporting has started.
Previously this could happen, because realtime_stop() queues
PostTransportStop and the butler is sommoned after every
export process cycle.
Since 61e7f3176b the butler keeps calling non_realtime_stop()
every time it is woken up, until TFSM comes around and unsets the
flag in the process callback.
This cuts reverb tails and synth sounds after export.
Disabling freewheeling, continues normal processing where
export left off. This previously kept notes ringing, or reverbs
audible.
The actual issue was introduced in 61e7f3176b:
Session::non_realtime_stop() no longer unsets PostTransportStop
(other changes from that commit are not relevant).
The real issue however is a race-condition.
So far this only seems to happen on MacOS, Coreaudio.
It seems that non_realtime_stop() is called in the butler-thread
after exporting has started, even though the butler has been
paused in wait_until_finished().
Perhaps Coreaudio thread switches causes TransportFSM to
reinitialize and scheduling the butler?
The use of `usleep()` makes this rather a workaround.
However it's sufficient for the coreaudio rt thread to run
at least once.
Ardour's playback is aligned to master-out:
"When the playback clock reads 01:00:00:00, the sample corresponding
to 01:00:00:00 is audible at the speaker(s)"
When exporting, and grabbing data from output ports, the signal
is offset by the master-bus physical playback latency. This was
compensated for, but lead to initial silence in the exported file.
New approach is to start capturing export data during pre-roll,
at the time when playback is written to the output buffers.
To also shaves off a common offset to make this work with
realtime export. Effectively this emulates processing with
disconnected master-output port, while still keeping any
latency of effects on the master-bus itself.
Last but not least: jack updates latencies when freewheeling,
(setting HW latency to zero). The callback arrives asynchronously
some time after enabling freewheeling, but after Export
Ports have been configured. Those callbacks are ignored.
After exporting a time-span, the next time-span was started
directly from the rt-callback. This had various issues.
In particular with realtime export.
Post-processing of a realtime-export enables freewheeling
and is driven by freewheel callbacks. Freewheeling needs to be
safely disabled for an upcoming realtime export.
A similar issues existed when mixing realtime and non-realtime exports.
This was a leftover from changes made for Tracks Live, related to
the concept of an auto-return preference. We don't use this anywhere in Ardour
or Mixbus, and the concept should eventually be removed entirely.
* centralize signal_latency_at_***_position to processors
* update initial-delay/roll-delay when processor order changes
* consolidate signal-latency calculation: use the same method
for processor-changes and session's post_playback_latency.
* include relative output-delay in roll-delay
* fix capture processor position & optimize stem-export latency
(roll-delay fixes pending Route:roll() update)
Generated by tools/f2s. Some hand-editing will be required in a few places to fix up comments related to timecode
and video in order to keep the legible
This allows a TmpFile pointer to be either a Sync or Async (Threaded)
writer. As result we must be able to handle both RT and non RT processing.
Still, post-processing (normalization and encoding) should always
happen faster than realtime (freewheeling).
Since jack does not allow a client to change to freewheeling from within
the process-callback, the async-writer disk-thread FileFlushed is used
to initiate post-processing.
* add a threaded TmpFile Writer
* update API calls to that process_export_fw() can be used as
process_function
The idea is to re-use export infrastructure from normalization:
export to a tmp-file and then encode target formats after that.
The "Stop" button results in ExportHandlerPtr being destroyed.
This must not happen while it's in use -- in particular during
ExportHandler::start_timespan() and ExportHandler::finish_timespan()
This makes the responsibilities and ownership of non-Route related MIDI ports
more clear, and removes a few wierd bits of code. It also ensures that open/close/open
on the same session will retain connections for those MIDI ports