When a track's output is not connected, but the track feeds
other tracks via sends (common case in Mixbus) the stem
export of the track was not correctly aligned.
The track is correctly latency compensated (due to sends), but
the unconnected port's latency is not set.
However stem-export uses the private latency of the port as alignment.
* audio files can be named "0bpm" to make them un-stretchable
* however they still need a sensible beatcnt, and therefore tempo
* assume tempo 120bpm and assign a beatcnt based on the file's length
* the user might later enable stretch, and we need to be sensible about it
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.