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);
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<Temporal::BBT_Offset> _follow_length;
PBD::Property<bool> _use_follow_length;
PBD::Property<bool> _legato;
PBD::Property<std::string> _name;
PBD::Property<gain_t> _gain;
PBD::Property<float> _midi_velocity_effect;
PBD::Property<bool> _stretchable;
PBD::Property<bool> _isolated;
PBD::Property<color_t> _color;
PBD::Property<bool> _cue_isolated;
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;
/* 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))
, _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<Region> r, bool use_thread)
{
@ -1418,6 +1385,8 @@ AudioTrigger::load_data (boost::shared_ptr<AudioRegion> 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<Region> 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 ();
}
}