Stop the engine if it was started for latency measurement
but measurement was aborted.
Previously the engine was kept running and the "Start' button
was not available.
If this happened during initial session loading, a user
would have to manually restart the engine to proceed.
When the screen-height is insufficient, the loudness-graph is
not included in the dialog. It is still visible in the image
saved with the export (save-export-analysis-image).
For tiny screens the conformity analysis may also be skipped.
The previous design had a race condition. When WaveViewThreads::stop_threads() was called, it would
first acquire the mutex, then set _quit, then call condition.broadcast(). But worker threads would
check _quit without holding the mutex. It was therefore for a thread to be delayed in its
own lock acquisition by the ::stop_threads() caller, then end up back in cond.wait() AFTER
the cond.broadcast() was done. Such a thread would sleep forever and never wake up.
This new design removes WaveViewDrawRequestQueue, which was a clean encapsulation of the
queueing aspects of WaveViewThreads, but unfortunately made correct mutex acquisition
and condition signalling/waiting needlessly complex. THe mutex, condition variable
and actual queue were moved into WaveViewThreads, and all worker threads execute a method
of the class which gives the appropriate code easy access to the mutex and condition var,
which must always be used together.
```
gtk2_ardour/ardour_ui.cc:2060: error: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second:
/usr/include/architecture/i386/math.h:343: note: candidate 1: double pow(double, double)
/usr/include/c++/4.2.1/cmath:357: note: candidate 2: float std::pow(float, float)
```
This replaces a Mutex and adds additional read-locks.
This is needed to address some threading issues with rt-threads
calling auto_loop_location() while the GUI changes locations.
Since locations are C-pointers this is still not entirely safe!
Locations::remove() may delete a location while a pointer
to it is being used in another thread.
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
```
Previously x-axis threshold was in samples. This is
useless unless zoomed in to the max. So in most cases
the first-move was always in x-direction, making constrained
y-axis drags near impossible.
Furthermore the threshold for copy-drags was increased
as per #8686
Region positions were updated in the GUI, before the playlist
was catching up.
The butler thread reads a region using the region's new position,
but the playlist's old range.
Thread 1 (GUI)
```
#22 ARDOUR::Playlist::notify_layering_changed()
#26 ARDOUR::AudioPlaylist::region_changed
#27 ARDOUR::Playlist::region_changed_proxy
#35 ARDOUR::Region::send_change
#36 ARDOUR::Region::set_position
#37 RegionRippleDrag::remove_unselected_from_views
#38 RegionRippleDrag::finished
```
LayeringChanged() also triggers DiskIOProcessor::playlist_modified
which schedules a pending-override and summons the butler.
Note that when moving only a few regions the butler starts after all
updates have been completed.
Butler thread:
```
#4 ARDOUR::AudioRegion::read_at
#5 ARDOUR::AudioPlaylist::read
#6 ARDOUR::DiskReader::audio_read
#7 ARDOUR::DiskReader::overwrite_existing_audio
#8 ARDOUR::DiskReader::overwrite_existing_buffers
#9 ARDOUR::Track::overwrite_existing_buffers
#10 ARDOUR::Session::non_realtime_overwrite
```
Region read fails:
```
libs/ardour/audioregion.cc:503 assert (position >= _position);
(gdb) p position
$1 = 1312000
(gdb) p _position
$2 = {<PBD::PropertyTemplate<long>> = {_have_old = true,
_current = 1336000, _old = 1312000} }
```