13
0

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()
This commit is contained in:
Paul Davis 2022-01-25 18:06:42 -07:00
parent f7b826841b
commit ccf90a9181
2 changed files with 218 additions and 183 deletions

View File

@ -115,8 +115,8 @@ class LIBARDOUR_API Trigger : public PBD::Stateful {
void set_stretchable (bool yn); void set_stretchable (bool yn);
bool stretchable () const { return _stretchable; } bool stretchable () const { return _stretchable; }
void set_scene_isolated (bool isolate); void set_cue_isolated (bool isolate);
bool scene_isolated () const { return _isolated; } bool cue_isolated () const { return _cue_isolated; }
/* Calling ::bang() will cause this Trigger to be placed in its owning /* Calling ::bang() will cause this Trigger to be placed in its owning
TriggerBox's queue. TriggerBox's queue.
@ -174,7 +174,8 @@ class LIBARDOUR_API Trigger : public PBD::Stateful {
void set_launch_style (LaunchStyle); void set_launch_style (LaunchStyle);
FollowAction follow_action (uint32_t n) const { assert (n < 2); return n ? _follow_action1 : _follow_action0; } 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; } color_t color() const { return _color; }
void set_color (color_t); void set_color (color_t);
@ -298,14 +299,73 @@ class LIBARDOUR_API Trigger : public PBD::Stateful {
PBD::Property<Temporal::BBT_Offset> _follow_length; PBD::Property<Temporal::BBT_Offset> _follow_length;
PBD::Property<bool> _use_follow_length; PBD::Property<bool> _use_follow_length;
PBD::Property<bool> _legato; PBD::Property<bool> _legato;
PBD::Property<std::string> _name;
PBD::Property<gain_t> _gain; PBD::Property<gain_t> _gain;
PBD::Property<float> _midi_velocity_effect; PBD::Property<float> _midi_velocity_effect;
PBD::Property<bool> _stretchable; PBD::Property<bool> _stretchable;
PBD::Property<bool> _isolated; PBD::Property<bool> _cue_isolated;
PBD::Property<color_t> _color;
PBD::Property<StretchMode> _stretch_mode; PBD::Property<StretchMode> _stretch_mode;
/* Properties that are not CAS-updated at retrigger */
PBD::Property<std::string> _name;
PBD::Property<color_t> _color;
public:
/* this is positioner here so that we can easily keep it in sync
with the properties list above.
*/
struct UIState {
std::atomic<int> 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; bool cue_launched;
/* computed from data */ /* computed from data */

View File

@ -122,13 +122,13 @@ Trigger::Trigger (uint32_t n, TriggerBox& b)
, _follow_length (Properties::follow_length, Temporal::BBT_Offset (1, 0, 0)) , _follow_length (Properties::follow_length, Temporal::BBT_Offset (1, 0, 0))
, _use_follow_length (Properties::use_follow_length, false) , _use_follow_length (Properties::use_follow_length, false)
, _legato (Properties::legato, false) , _legato (Properties::legato, false)
, _name (Properties::name, "")
, _gain (Properties::gain, 1.0) , _gain (Properties::gain, 1.0)
, _midi_velocity_effect (Properties::velocity_effect, 0.) , _midi_velocity_effect (Properties::velocity_effect, 0.)
, _stretchable (Properties::stretchable, true) , _stretchable (Properties::stretchable, true)
, _isolated (Properties::isolated, false) , _cue_isolated (Properties::isolated, false)
, _color (Properties::color, 0xBEBEBEFF)
, _stretch_mode (Properties::stretch_mode, Trigger::Crisp) , _stretch_mode (Properties::stretch_mode, Trigger::Crisp)
, _name (Properties::name, "")
, _color (Properties::color, 0xBEBEBEFF)
, cue_launched (false) , cue_launched (false)
, _estimated_tempo (0.) , _estimated_tempo (0.)
, _segment_tempo (0.) , _segment_tempo (0.)
@ -150,9 +150,11 @@ Trigger::Trigger (uint32_t n, TriggerBox& b)
add_property (_gain); add_property (_gain);
add_property (_midi_velocity_effect); add_property (_midi_velocity_effect);
add_property (_stretchable); add_property (_stretchable);
add_property (_isolated); add_property (_cue_isolated);
add_property (_color); add_property (_color);
add_property (_stretch_mode); add_property (_stretch_mode);
copy_to_ui_state ();
} }
void void
@ -161,6 +163,109 @@ Trigger::request_trigger_delete (Trigger* t)
TriggerBox::worker->request_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 void
Trigger::set_pending (Trigger* t) 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); (_follow_action0.val().type == FollowAction::None && _follow_action1.val().type == FollowAction::None);
} }
void #define TRIGGER_CAS_SET(name,type) \
Trigger::set_name (std::string const & str) void \
{ Trigger::set_ ## name (type val) \
if (_name == str) { { \
return; 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())); \
_name = str;
PropertyChanged (Properties::name);
_box.session().set_dirty();
} }
void
Trigger::set_scene_isolated (bool i)
{
if (_isolated == i) {
return;
}
_isolated = i; TRIGGER_CAS_SET (cue_isolated,bool)
PropertyChanged (ARDOUR::Properties::isolated); TRIGGER_CAS_SET (stretchable, bool)
_box.session().set_dirty(); 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 (name, std::string const &)
Trigger::set_color (color_t c) TRIGGER_SET (color, color_t)
{
if (_color == c) {
return;
}
_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 void
Trigger::set_ui (void* p) Trigger::set_ui (void* p)
@ -258,63 +353,6 @@ Trigger::unbang ()
DEBUG_TRACE (DEBUG::Triggers, string_compose ("un-bang on %1\n", _index)); 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::LaunchStyle
Trigger::launch_style () const Trigger::launch_style () const
{ {
@ -324,19 +362,6 @@ Trigger::launch_style () const
return _launch_style; 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& XMLNode&
Trigger::get_state (void) Trigger::get_state (void)
{ {
@ -382,75 +407,17 @@ Trigger::set_state (const XMLNode& node, int version)
node.get_property (X_("index"), _index); node.get_property (X_("index"), _index);
set_values (node); set_values (node);
copy_to_ui_state ();
return 0; 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 bool
Trigger::internal_use_follow_length () const Trigger::internal_use_follow_length () const
{ {
return (_follow_action0.val().type != FollowAction::None) && _use_follow_length; 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 void
Trigger::set_region (boost::shared_ptr<Region> r, bool use_thread) Trigger::set_region (boost::shared_ptr<Region> r, bool use_thread)
{ {
@ -1418,6 +1385,8 @@ AudioTrigger::load_data (boost::shared_ptr<AudioRegion> ar)
void void
AudioTrigger::retrigger () AudioTrigger::retrigger ()
{ {
update_properties ();
read_index = _start_offset + _legato_offset; read_index = _start_offset + _legato_offset;
process_index = 0; process_index = 0;
retrieved = 0; retrieved = 0;
@ -1960,6 +1929,8 @@ MIDITrigger::set_region_in_worker_thread (boost::shared_ptr<Region> r)
void void
MIDITrigger::retrigger () MIDITrigger::retrigger ()
{ {
update_properties ();
/* XXX need to deal with bar offsets */ /* XXX need to deal with bar offsets */
// const Temporal::BBT_Offset o = _start_offset + _legato_offset; // const Temporal::BBT_Offset o = _start_offset + _legato_offset;
iter = model->begin(); iter = model->begin();
@ -2431,7 +2402,11 @@ void
TriggerBox::set_all_follow_action (ARDOUR::FollowAction const & fa, uint32_t fa_n) TriggerBox::set_all_follow_action (ARDOUR::FollowAction const & fa, uint32_t fa_n)
{ {
for (uint64_t n = 0; n < all_triggers.size(); ++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) { if (_active_scene >= 0) {
DEBUG_TRACE (DEBUG::Triggers, string_compose ("tb noticed active scene %1\n", _active_scene)); DEBUG_TRACE (DEBUG::Triggers, string_compose ("tb noticed active scene %1\n", _active_scene));
if (_active_scene < (int32_t) all_triggers.size()) { 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 (); all_triggers[_active_scene]->bang ();
} }
} }