From ccf90a9181f96520daaeecfec1259a4d683a08d2 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Tue, 25 Jan 2022 18:06:42 -0700 Subject: [PATCH] triggerbox: change to a CAS-based mechanism for setting UI-controlled properties UIs only set a "shadow" value of most trigger properties, and use CAS to interlock (contention is not expected to ever be an issue, it would imply two UIs being used to control this at precisely the same time. The actual properties are updated whenever the trigger calls ::retrigger() --- libs/ardour/ardour/triggerbox.h | 72 ++++++- libs/ardour/triggerbox.cc | 329 +++++++++++++++----------------- 2 files changed, 218 insertions(+), 183 deletions(-) diff --git a/libs/ardour/ardour/triggerbox.h b/libs/ardour/ardour/triggerbox.h index 06a82f4a10..e93391cfb6 100644 --- a/libs/ardour/ardour/triggerbox.h +++ b/libs/ardour/ardour/triggerbox.h @@ -115,8 +115,8 @@ class LIBARDOUR_API Trigger : public PBD::Stateful { void set_stretchable (bool yn); bool stretchable () const { return _stretchable; } - void set_scene_isolated (bool isolate); - bool scene_isolated () const { return _isolated; } + void set_cue_isolated (bool isolate); + bool cue_isolated () const { return _cue_isolated; } /* Calling ::bang() will cause this Trigger to be placed in its owning TriggerBox's queue. @@ -174,7 +174,8 @@ class LIBARDOUR_API Trigger : public PBD::Stateful { void set_launch_style (LaunchStyle); FollowAction follow_action (uint32_t n) const { assert (n < 2); return n ? _follow_action1 : _follow_action0; } - void set_follow_action (FollowAction, uint32_t n); + void set_follow_action0 (FollowAction); + void set_follow_action1 (FollowAction); color_t color() const { return _color; } void set_color (color_t); @@ -298,14 +299,73 @@ class LIBARDOUR_API Trigger : public PBD::Stateful { PBD::Property _follow_length; PBD::Property _use_follow_length; PBD::Property _legato; - PBD::Property _name; PBD::Property _gain; PBD::Property _midi_velocity_effect; PBD::Property _stretchable; - PBD::Property _isolated; - PBD::Property _color; + PBD::Property _cue_isolated; PBD::Property _stretch_mode; + /* Properties that are not CAS-updated at retrigger */ + + PBD::Property _name; + PBD::Property _color; + + public: + /* this is positioner here so that we can easily keep it in sync + with the properties list above. + */ + struct UIState { + std::atomic generation; /* used for CAS */ + + LaunchStyle launch_style; + FollowAction follow_action0; + FollowAction follow_action1; + int follow_action_probability; /* 1 .. 100 */ + uint32_t follow_count; + Temporal::BBT_Offset quantization; + Temporal::BBT_Offset follow_length; + bool use_follow_length; + bool legato; + gain_t gain; + float midi_velocity_effect; + bool stretchable; + bool cue_isolated; + StretchMode stretch_mode; + + UIState() : generation (0) {} + + UIState& operator= (UIState const & other) { + + /* we do not copy generation */ + + generation = 0; + + launch_style = other.launch_style; + follow_action0 = other.follow_action0; + follow_action1 = other.follow_action1; + follow_action_probability = other.follow_action_probability; + follow_count = other.follow_count; + quantization = other.quantization; + follow_length = other.follow_length; + use_follow_length = other.use_follow_length; + legato = other.legato; + gain = other.gain; + midi_velocity_effect = other.midi_velocity_effect; + stretchable = other.stretchable; + cue_isolated = other.cue_isolated; + stretch_mode = other.stretch_mode; + + return *this; + } + }; + + UIState ui_state; + + protected: + void copy_ui_state (UIState&); + void copy_to_ui_state (); + void update_properties (); + bool cue_launched; /* computed from data */ diff --git a/libs/ardour/triggerbox.cc b/libs/ardour/triggerbox.cc index e363412ebf..d047c07f40 100644 --- a/libs/ardour/triggerbox.cc +++ b/libs/ardour/triggerbox.cc @@ -122,13 +122,13 @@ Trigger::Trigger (uint32_t n, TriggerBox& b) , _follow_length (Properties::follow_length, Temporal::BBT_Offset (1, 0, 0)) , _use_follow_length (Properties::use_follow_length, false) , _legato (Properties::legato, false) - , _name (Properties::name, "") , _gain (Properties::gain, 1.0) , _midi_velocity_effect (Properties::velocity_effect, 0.) , _stretchable (Properties::stretchable, true) - , _isolated (Properties::isolated, false) - , _color (Properties::color, 0xBEBEBEFF) + , _cue_isolated (Properties::isolated, false) , _stretch_mode (Properties::stretch_mode, Trigger::Crisp) + , _name (Properties::name, "") + , _color (Properties::color, 0xBEBEBEFF) , cue_launched (false) , _estimated_tempo (0.) , _segment_tempo (0.) @@ -150,9 +150,11 @@ Trigger::Trigger (uint32_t n, TriggerBox& b) add_property (_gain); add_property (_midi_velocity_effect); add_property (_stretchable); - add_property (_isolated); + add_property (_cue_isolated); add_property (_color); add_property (_stretch_mode); + + copy_to_ui_state (); } void @@ -161,6 +163,109 @@ Trigger::request_trigger_delete (Trigger* t) TriggerBox::worker->request_delete_trigger (t); } +void +Trigger::copy_ui_state (UIState& uis) +{ + int g = ui_state.generation.load (); + + do { uis = ui_state; } while (!ui_state.generation.compare_exchange_strong (g, g+1)); +} + +void +Trigger::update_properties () +{ + UIState uis; + + copy_ui_state (uis); + PropertyChange pc; + + /* ONLY CAS-set properties should appear here */ + + if (_launch_style != uis.launch_style) { + _launch_style = uis.launch_style; + pc.add (Properties::launch_style); + } + if (_follow_action0 != uis.follow_action0) { + _follow_action0 = uis.follow_action0; + pc.add (Properties::follow_action0); + } + if (_follow_action1 != uis.follow_action1) { + _follow_action1 = uis.follow_action1; + pc.add (Properties::follow_action1); + } + if (_follow_action_probability != uis.follow_action_probability) { + _follow_action_probability = uis.follow_action_probability; + pc.add (Properties::follow_action_probability); + } + if (_follow_count != uis.follow_count) { + _follow_count = uis.follow_count; + pc.add (Properties::follow_count); + } + if (_quantization != uis.quantization) { + _quantization = uis.quantization; + pc.add (Properties::quantization); + } + if (_follow_length != uis.follow_length) { + _follow_length = uis.follow_length; + pc.add (Properties::follow_length); + } + if (_use_follow_length != uis.use_follow_length) { + _use_follow_length = uis.use_follow_length; + pc.add (Properties::use_follow_length); + } + if (_legato != uis.legato) { + _legato = uis.legato; + pc.add (Properties::legato); + } + if (_gain != uis.gain) { + _gain = uis.gain; + pc.add (Properties::gain); + } + if (_midi_velocity_effect != uis.midi_velocity_effect) { + _midi_velocity_effect = uis.midi_velocity_effect; + pc.add (Properties::velocity_effect); + } + if (_stretchable != uis.stretchable) { + _stretchable = uis.stretchable; + pc.add (Properties::stretchable); + } + if (_cue_isolated != uis.cue_isolated) { + _cue_isolated = uis.cue_isolated; + pc.add (Properties::isolated); + } + if (_stretch_mode != uis.stretch_mode) { + _stretch_mode = uis.stretch_mode; + pc.add (Properties::stretch_mode); + } + + if (pc != PropertyChange()) { + + PropertyChanged (pc); /* EMIT SIGNAL */ + _box.session().set_dirty (); + } +} + +void +Trigger::copy_to_ui_state () +{ + /* usable only at object creation */ + + ui_state.launch_style = _launch_style; + ui_state.follow_action0 = _follow_action0; + ui_state.follow_action1 = _follow_action1; + ui_state.follow_action_probability = _follow_action_probability; + ui_state.follow_count = _follow_count; + ui_state.quantization = _quantization; + ui_state.follow_length = _follow_length; + ui_state.use_follow_length = _use_follow_length; + ui_state.legato = _legato; + ui_state.gain = _gain; + ui_state.midi_velocity_effect = _midi_velocity_effect; + ui_state.stretchable = _stretchable; + ui_state.cue_isolated = _cue_isolated; + ui_state.stretch_mode = _stretch_mode; +} + void Trigger::set_pending (Trigger* t) { @@ -184,53 +289,43 @@ Trigger::will_not_follow () const (_follow_action0.val().type == FollowAction::None && _follow_action1.val().type == FollowAction::None); } -void -Trigger::set_name (std::string const & str) -{ - if (_name == str) { - return; - } - - _name = str; - PropertyChanged (Properties::name); - _box.session().set_dirty(); +#define TRIGGER_CAS_SET(name,type) \ +void \ +Trigger::set_ ## name (type val) \ +{ \ + int g = ui_state.generation.load(); \ + do { ui_state.name = val; } while (!ui_state.generation.compare_exchange_strong (g, g+1)); \ + DEBUG_TRACE (DEBUG::Triggers, string_compose ("trigger %1 property cas-set: %2\n", _ ## name.property_name())); \ } -void -Trigger::set_scene_isolated (bool i) -{ - if (_isolated == i) { - return; - } - _isolated = i; - PropertyChanged (ARDOUR::Properties::isolated); - _box.session().set_dirty(); +TRIGGER_CAS_SET (cue_isolated,bool) +TRIGGER_CAS_SET (stretchable, bool) +TRIGGER_CAS_SET (gain, gain_t) +TRIGGER_CAS_SET (midi_velocity_effect, float) +TRIGGER_CAS_SET (follow_count, uint32_t) +TRIGGER_CAS_SET (follow_action0, FollowAction) +TRIGGER_CAS_SET (follow_action1, FollowAction) +TRIGGER_CAS_SET (launch_style, LaunchStyle) +TRIGGER_CAS_SET (follow_length, Temporal::BBT_Offset const &) +TRIGGER_CAS_SET (use_follow_length, bool) +TRIGGER_CAS_SET (legato, bool) +TRIGGER_CAS_SET (follow_action_probability, int) +TRIGGER_CAS_SET (quantization, Temporal::BBT_Offset const &) + +#define TRIGGER_SET(name,type) \ +void \ +Trigger::set_ ## name (type val) \ +{ \ + if (_ ## name == val) { return; } \ + _ ## name = val; \ + PropertyChanged (Properties::name); /* EMIT SIGNAL */ \ + _box.session().set_dirty (); \ } -void -Trigger::set_color (color_t c) -{ - if (_color == c) { - return; - } +TRIGGER_SET (name, std::string const &) +TRIGGER_SET (color, color_t) - _color = c; - PropertyChanged (ARDOUR::Properties::color); - _box.session().set_dirty(); -} - -void -Trigger::set_stretchable (bool s) -{ - if (_stretchable == s) { - return; - } - - _stretchable = s; - PropertyChanged (ARDOUR::Properties::stretchable); - _box.session().set_dirty(); -} void Trigger::set_ui (void* p) @@ -258,63 +353,6 @@ Trigger::unbang () DEBUG_TRACE (DEBUG::Triggers, string_compose ("un-bang on %1\n", _index)); } -void -Trigger::set_gain (gain_t g) -{ - if (_gain == g) { - return; - } - - _gain = g; - PropertyChanged (Properties::gain); - _box.session().set_dirty(); -} - -void -Trigger::set_midi_velocity_effect (float mve) -{ - if (_midi_velocity_effect == mve) { - return; - } - - _midi_velocity_effect = std::min (1.f, std::max (0.f, mve)); - PropertyChanged (Properties::velocity_effect); - _box.session().set_dirty(); -} - -void -Trigger::set_follow_count (uint32_t n) -{ - if (_follow_count == n) { - return; - } - - _follow_count = n; - PropertyChanged (Properties::follow_count); - _box.session().set_dirty(); -} - -void -Trigger::set_follow_action (FollowAction f, uint32_t n) -{ - assert (n < 2); - - if (n == 0) { - if (_follow_action0 == f) { - return; - } - _follow_action0 = f; - PropertyChanged (Properties::follow_action0); - } else { - if (_follow_action1 == f) { - return; - } - _follow_action1 = f; - PropertyChanged (Properties::follow_action1); - } - _box.session().set_dirty(); -} - Trigger::LaunchStyle Trigger::launch_style () const { @@ -324,19 +362,6 @@ Trigger::launch_style () const return _launch_style; } -void -Trigger::set_launch_style (LaunchStyle l) -{ - if (_launch_style == l) { - return; - } - - _launch_style = l; - - PropertyChanged (Properties::launch_style); - _box.session().set_dirty(); -} - XMLNode& Trigger::get_state (void) { @@ -382,75 +407,17 @@ Trigger::set_state (const XMLNode& node, int version) node.get_property (X_("index"), _index); set_values (node); + copy_to_ui_state (); + return 0; } -void -Trigger::set_follow_length (Temporal::BBT_Offset const & bbo) -{ - if (_use_follow_length == bbo) { - return; - } - _follow_length = bbo; - PropertyChanged (Properties::use_follow_length); - _box.session().set_dirty(); -} - -void -Trigger::set_use_follow_length (bool ufl) -{ - if (_use_follow_length == ufl) { - return; - } - _use_follow_length = ufl; - PropertyChanged (Properties::use_follow_length); - _box.session().set_dirty(); -} - bool Trigger::internal_use_follow_length () const { return (_follow_action0.val().type != FollowAction::None) && _use_follow_length; } -void -Trigger::set_legato (bool yn) -{ - if (_legato == yn) { - return; - } - _legato = yn; - PropertyChanged (Properties::legato); - _box.session().set_dirty(); -} - -void -Trigger::set_follow_action_probability (int n) -{ - if (_follow_action_probability == n) { - return; - } - - n = std::min (100, n); - n = std::max (0, n); - - _follow_action_probability = n; - PropertyChanged (Properties::follow_action_probability); - _box.session().set_dirty(); -} - -void -Trigger::set_quantization (Temporal::BBT_Offset const & q) -{ - if (_quantization == q) { - return; - } - - _quantization = q; - PropertyChanged (Properties::quantization); - _box.session().set_dirty(); -} - void Trigger::set_region (boost::shared_ptr r, bool use_thread) { @@ -1418,6 +1385,8 @@ AudioTrigger::load_data (boost::shared_ptr ar) void AudioTrigger::retrigger () { + update_properties (); + read_index = _start_offset + _legato_offset; process_index = 0; retrieved = 0; @@ -1960,6 +1929,8 @@ MIDITrigger::set_region_in_worker_thread (boost::shared_ptr r) void MIDITrigger::retrigger () { + update_properties (); + /* XXX need to deal with bar offsets */ // const Temporal::BBT_Offset o = _start_offset + _legato_offset; iter = model->begin(); @@ -2431,7 +2402,11 @@ void TriggerBox::set_all_follow_action (ARDOUR::FollowAction const & fa, uint32_t fa_n) { for (uint64_t n = 0; n < all_triggers.size(); ++n) { - all_triggers[n]->set_follow_action (fa, fa_n); + if (fa_n == 0) { + all_triggers[n]->set_follow_action0 (fa); + } else { + all_triggers[n]->set_follow_action1 (fa); + } } } @@ -2739,7 +2714,7 @@ TriggerBox::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp if (_active_scene >= 0) { DEBUG_TRACE (DEBUG::Triggers, string_compose ("tb noticed active scene %1\n", _active_scene)); if (_active_scene < (int32_t) all_triggers.size()) { - if (!all_triggers[_active_scene]->scene_isolated()) { + if (!all_triggers[_active_scene]->cue_isolated()) { all_triggers[_active_scene]->bang (); } }