VCAManager::create_vca sets PI::order while holding the
VCAManager:lock mutex. PI order changes emit a "changed" signal
which in turn can result in querying a list of all strips
(reassign_track_numbers) which requires the VCA mutex:
See also 729ff35faf
```
#2 Glib::Threads::Mutex::Lock::Lock(Glib::Threads::Mutex&) (this=0x7fffffffb070, mutex=...) at /usr/include/glibmm-2.4/glibmm/threads.h:687
#3 ARDOUR::VCAManager::vcas[abi:cxx11]() const (this=0x5555599b6d10) at ../libs/ardour/vca_manager.cc:77
#4 ARDOUR::Session::get_stripables(std::__cxx11::list<boost::shared_ptr<ARDOUR::Stripable>, std::allocator<boost::shared_ptr<ARDOUR::Stripable> > >&, ARDOUR::PresentationInfo::Flag) const (this=0x55555bf03910, sl=std::__cxx11::list = {...}, fl=127) at ../libs/ardour/session.cc:3949
#5 ARDOUR::Session::ensure_stripable_sort_order() (this=0x55555bf03910) at ../libs/ardour/session.cc:2634
#6 ARDOUR::Session::notify_presentation_info_change(PBD::PropertyChange const&) (this=0x55555bf03910, what_changed=...) at ../libs/ardour/session.cc:7016
[ .. connect same thread ..]
#13 ARDOUR::PresentationInfo::send_static_change(PBD::PropertyChange const&) (what_changed=...) at ../libs/ardour/presentation_info.cc:113
#14 ARDOUR::PresentationInfo::set_order(unsigned int) (this=0x5555619ceca0, order=5) at ../libs/ardour/presentation_info.cc:288
#15 ARDOUR::Stripable::set_presentation_order(unsigned int) (this=0x5555619ce8a0, order=5) at ../libs/ardour/stripable.cc:55
#16 ARDOUR::VCAManager::create_vca(unsigned int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)
(this=0x5555599b6d10, howmany=1, name_template="VCA %n") at ../libs/ardour/vca_manager.cc:105
```
Control surfaces c'tor usually subscribe to signals e.g.
PortConnectedOrDisconnected. This happens after the parent BaseUI
is created, but before set_active() -> BaseUI::run() is called.
At this point in time there is no run_loop thread.
There are two options to handle AbstractUI::call_slot():
A. Queue the event in the event-loop, using the thread-local
request buffer of the caller. Then hope the BaseUI
thread is started, and calls ::handle_ui_requests() before
the memory pool runs out of space.
B. Handle the event in the calling thread. -- This may not be
rt-safe and may call functions with locks held by the caller.
It will however not accumulate events.
This takes approach (B). If _run_loop_thread is NULL, directly
handle the signal.
In the past, prior to 50abcc74b5, approach (A) was taken.
NULL never matched Glib::Threads::Thread::self().
This also reverts a prior attempt (e417495505) to address this issue.
The Model must be destroyed after the iterator, otherwise
the iterator's d'tor will cause memory corruption trying to lock
the model:
```
Invalid read of size 4
at pthread_rwlock_unlock (pthread_rwlock_unlock.c:39)
by Glib::Threads::RWLock::ReaderLock::~ReaderLock() (threads.h:828)
by void boost::checked_delete<Glib::Threads::RWLock::ReaderLock>(Glib::Threads::RWLock::ReaderLock*) (checked_delete.hpp:36)
by boost::detail::sp_counted_impl_p<Glib::Threads::RWLock::ReaderLock>::dispose() (sp_counted_impl.hpp:89)
by boost::detail::sp_counted_base::release() (sp_counted_base_gcc_atomic.hpp:120)
by boost::detail::shared_count::~shared_count() (shared_count.hpp:432)
by boost::shared_ptr<Glib::Threads::RWLock::ReaderLock>::~shared_ptr() (shared_ptr.hpp:335)
by Evoral::Sequence<Temporal::Beats>::const_iterator::~const_iterator() (Sequence.h:224)
by ARDOUR::MIDITrigger::~MIDITrigger() (triggerbox.cc:2037)
by ARDOUR::MIDITrigger::~MIDITrigger() (triggerbox.cc:2039)
by ARDOUR::TriggerBoxThread::delete_trigger(ARDOUR::Trigger*) (triggerbox.cc:4386)
by ARDOUR::TriggerBoxThread::thread_work() (triggerbox.cc:4314)
by ARDOUR::TriggerBoxThread::_thread_work(void*) (triggerbox.cc:4285)
by fake_thread_start(void*) (pthread_utils.cc:101)
by start_thread (pthread_create.c:477)
by clone (clone.S:95)
Address 0x28229798 is 24 bytes inside a block of size 56 free'd
at free (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
by Glib::Threads::RWLock::~RWLock() (in /usr/lib/x86_64-linux-gnu/libglibmm-2.4.so.1.3.0)
by Evoral::Sequence<Temporal::Beats>::~Sequence() (Sequence.h:68)
by ARDOUR::AutomatableSequence<Temporal::Beats>::~AutomatableSequence() (automatable_sequence.h:31)
by ARDOUR::MidiModel::~MidiModel() (midi_model.h:58)
by ARDOUR::MidiModel::~MidiModel() (midi_model.h:58)
by void boost::checked_delete<ARDOUR::MidiModel>(ARDOUR::MidiModel*) (checked_delete.hpp:36)
by boost::detail::sp_counted_impl_p<ARDOUR::MidiModel>::dispose() (sp_counted_impl.hpp:89)
by boost::detail::sp_counted_base::release() (sp_counted_base_gcc_atomic.hpp:120)
by boost::detail::shared_count::~shared_count() (shared_count.hpp:432)
by boost::shared_ptr<ARDOUR::MidiModel>::~shared_ptr() (shared_ptr.hpp:335)
by ARDOUR::MIDITrigger::~MIDITrigger() (triggerbox.cc:2037)
by ARDOUR::MIDITrigger::~MIDITrigger() (triggerbox.cc:2039)
by ARDOUR::TriggerBoxThread::delete_trigger(ARDOUR::Trigger*) (triggerbox.cc:4386)
by ARDOUR::TriggerBoxThread::thread_work() (triggerbox.cc:4314)
by ARDOUR::TriggerBoxThread::_thread_work(void*) (triggerbox.cc:4285)
by fake_thread_start(void*) (pthread_utils.cc:101)
by start_thread (pthread_create.c:477)
by clone (clone.S:95)
```
* intercept {pgm|bank}-change messages, and replace them with triggerbox values
* check is_set() extensively; we have arrays of pgms but most are unused
* initialize patches to GM standard (drums on 10) in case file has none
* in the case where file had none, check the Auditioner to see if user set any
* in the case where a file has patches, use those instead
* also stash the UsedChannels so we can show only the used chans in the UI
* if a file has program-changes, then it will set() my patches
* if a file does *not* have PCs, the user might choose one (which sets() it)
* if the next file does *not* have PCs, we should preserve the user's selection
* if the next file has PCs, it will set() it (losing the user selection)
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
This prevents a crash at exit if the analyzer is still analyzing
when Ardour terminates. A possible downside is that an ongoing
analysis will keep the application running for a bit longer.
This is mainly because Glib::Threads (g_system_thread_new)
uses pthread on Un*x, but on Windows relies on GThreadWin32
(HANDLE)_beginthreadex
This later causes issues e.g. in BaseUI::run()
```
unhandled exception (type Glib::Error) in signal handler:
domain: g_thread_error
code : 0
what : Error setting new thread priority: The parameter is incorrect.
```
At some point during code refactoring, we ended up setting up the 3 transition times, intedned
to define when the *previous* transition occured, as we checked to see if a transition
would occur during this ::run() call. This led to (in particular) MIDI clips ending early,
because the transition_beats value was set to the *next* (upcoming) transition, and we used
this to define the timeline position of MIDI events
This allows for a different quantization to be used when WaitingToSwitch,
used when we explicitly start a different Trigger. When "just stopping"
we continue to use 1 bar quantization; when switching to a different
Trigger we use the quantization of the next Trigger
A trigger can be in a playout state when we decide to change its
state to WaitingToStop (e.g. due to a cue marker). This design
leaves the playout condition "in effect" despite the state changing.
This uses a mild trick to pass both the object and ptr-to-member-function from the child class
to the parent class.
Note: before this commit, both instances of ::start_and_roll_to() were identical.
If we decide after fast forwarding that this TriggerBox has no triggers
active at the transport position, we must mark _currently_playing as null,
because otherwise the TB can still start the transport (in error) during
run()