diff --git a/include/avnd/binding/ossia/controls.hpp b/include/avnd/binding/ossia/controls.hpp index edf9929e..c556ce03 100644 --- a/include/avnd/binding/ossia/controls.hpp +++ b/include/avnd/binding/ossia/controls.hpp @@ -2,7 +2,7 @@ #include #include // #include -#include +#include #include #include @@ -77,7 +77,8 @@ struct controls_input_queue using i_tuple = avnd::filter_and_apply; - ossia::mpmc_queue ins_queue; + //! The latest control values, for the UI. + ossia::triple_buffer ins_buffer; std::bitset inputs_set; //! Bool controls pressed by an impulse in this tick: released once the //! tick is processed (see momentary_bool_port). @@ -92,14 +93,10 @@ struct controls_output_queue using o_tuple = avnd::filter_and_apply; - ossia::mpmc_queue outs_queue; + //! The latest control output values, for the UI. + ossia::triple_buffer outs_buffer; std::bitset outputs_set; - - //! Whether anything is draining outs_queue. The host clears it when it - //! installs no reader: enqueuing then grows the queue without bound and - //! allocates inside the audio callback for a value nobody reads. - std::atomic_bool notify_ui{true}; }; template @@ -107,6 +104,9 @@ struct controls_queue : controls_input_queue , controls_output_queue { + //! Whether the host reads the values above. When it does not, the node + //! does not copy them. + std::atomic_bool notify_ui{true}; }; } diff --git a/include/avnd/binding/ossia/node.hpp b/include/avnd/binding/ossia/node.hpp index 2b7c748b..1a601cb8 100644 --- a/include/avnd/binding/ossia/node.hpp +++ b/include/avnd/binding/ossia/node.hpp @@ -586,26 +586,38 @@ class safe_node_base : public safe_node_base_base void process_smooth() { this->smooth.smooth_all(this->impl); } - typename controls_input_queue::i_tuple make_controls_in_tuple() + // Copies the control values field by field into a buffer that was written + // before, so that the values that hold memory reuse it. + void publish_controls_in() { + auto& buf = this->control.ins_buffer.write_buffer(); // We only care about the inputs of the first one, since they're all the same for(auto state : this->impl.full_state()) { - return avnd::control_input_introspection::filter_tuple( - state.inputs, [](auto& field) { return field.value; }); + avnd::control_input_introspection::for_all_n( + state.inputs, [&](auto& field, avnd::predicate_index) { + using namespace std; + get(buf) = field.value; + }); + this->control.ins_buffer.publish(); + break; } - return {}; } - typename controls_output_queue::o_tuple make_controls_out_tuple() + void publish_controls_out() { + auto& buf = this->control.outs_buffer.write_buffer(); // Note that this does not yet make a lot of sens for polyphonic effects for(auto state : this->impl.full_state()) { - return avnd::control_output_introspection::filter_tuple( - state.outputs, [](auto& field) { return field.value; }); + avnd::control_output_introspection::for_all_n( + state.outputs, [&](auto& field, avnd::predicate_index) { + using namespace std; + get(buf) = field.value; + }); + this->control.outs_buffer.publish(); + break; } - return {}; } void finish_run() @@ -630,8 +642,9 @@ class safe_node_base : public safe_node_base_base { if(this->control.inputs_set.any()) { - // Notify the UI - this->control.ins_queue.enqueue(make_controls_in_tuple()); + // Notify the UI, unless nothing is listening + if(this->control.notify_ui.load(std::memory_order_relaxed)) + publish_controls_in(); this->control.inputs_set.reset(); } } @@ -651,7 +664,7 @@ class safe_node_base : public safe_node_base_base { // Notify the UI, unless nothing is listening if(this->control.notify_ui.load(std::memory_order_relaxed)) - this->control.outs_queue.enqueue(make_controls_out_tuple()); + publish_controls_out(); this->control.outputs_set.reset(); } }