From 40c1cee05d957a2a896e9f7a35a2359668f457d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Micha=C3=ABl=20Celerier?= Date: Mon, 9 Dec 2024 15:06:33 -0500 Subject: [PATCH] ossia: work on making avnd objects support polyphonic controls --- include/avnd/binding/ossia/node.hpp | 13 +- .../binding/ossia/port_run_postprocess.hpp | 1 + .../binding/ossia/port_run_preprocess.hpp | 118 ++++++++++++++++-- include/avnd/binding/ossia/port_setup.hpp | 2 + include/avnd/binding/ossia/time_controls.hpp | 2 +- include/avnd/wrappers/effect_container.hpp | 52 +++++--- 6 files changed, 153 insertions(+), 35 deletions(-) diff --git a/include/avnd/binding/ossia/node.hpp b/include/avnd/binding/ossia/node.hpp index 74d287b6..1e365fcf 100644 --- a/include/avnd/binding/ossia/node.hpp +++ b/include/avnd/binding/ossia/node.hpp @@ -236,10 +236,11 @@ class safe_node_base : public safe_node_base_base template void process_all_ports(Args&&... args) { + int poly_instance = 0; for(auto [impl, i, o] : this->impl.full_state()) { static_assert(std::is_reference_v); - Functor f{*this, impl, args...}; + Functor f{*this, impl, poly_instance++, args...}; if constexpr(avnd::inputs_type::size > 0) process_inputs_impl(f, i); if constexpr(avnd::outputs_type::size > 0) @@ -439,11 +440,11 @@ class safe_node_base : public safe_node_base_base if constexpr(time_controls::size > 0) { this->tempo = new_tempo; - time_controls::for_all_n2( - this->impl.inputs(), - [this, new_tempo](auto& field, auto pred_idx, auto f_idx) { - this->time_controls.set_tempo(this->impl, pred_idx, f_idx, new_tempo); - }); + for(const auto& [eff, ins, outs] : this->impl.full_state()) + time_controls::for_all_n2( + ins, [this, new_tempo](auto& field, auto pred_idx, auto f_idx) { + this->time_controls.set_tempo(this->impl, pred_idx, f_idx, new_tempo); + }); } } diff --git a/include/avnd/binding/ossia/port_run_postprocess.hpp b/include/avnd/binding/ossia/port_run_postprocess.hpp index 3d865c02..a3d27ad7 100644 --- a/include/avnd/binding/ossia/port_run_postprocess.hpp +++ b/include/avnd/binding/ossia/port_run_postprocess.hpp @@ -19,6 +19,7 @@ struct process_after_run { Exec_T& self; Obj_T& impl; + int instance{}; int& start = self.start_frame_for_this_tick; int& frames = self.frame_count_for_this_tick; diff --git a/include/avnd/binding/ossia/port_run_preprocess.hpp b/include/avnd/binding/ossia/port_run_preprocess.hpp index 95ce96d1..c538ae88 100644 --- a/include/avnd/binding/ossia/port_run_preprocess.hpp +++ b/include/avnd/binding/ossia/port_run_preprocess.hpp @@ -16,30 +16,129 @@ #include #include #include +#include #include #include namespace oscr { -template -inline void update_value( - auto& node, auto& obj, Field& field, const ossia::value& src, auto& dst, - avnd::field_index idx) + +// If we have a polyphonic object +// And we get an array input that corresponds to the kind of data expected +// Then we apply the value element-wise to each monophonic instance +// +// To do this we must make sure that we always create as many "input" objects as needed +template + requires std::is_arithmetic_v +struct apply_value_polyphonic_control { - if(node.from_ossia_value(field, src, dst, idx)) + Value& res; + int instance; + bool operator()() const { return false; } + bool operator()(ossia::impulse) const { - if_possible(field.update(obj)); + if_possible(res = {}); + return true; } -} + + bool operator()(float v) const + { + res = v; + return true; + } + + bool operator()(int v) const + { + res = v; + return true; + } + + bool operator()(bool v) const + { + res = v ? 1. : 0.; + return true; + } + + bool operator()(const std::string& v) const + { + if(auto f = ossia::parse_relax(v)) + { + res = *f; + return true; + } + return false; + } + + template + bool operator()(const std::array& v) const + { + if(instance >= 0 && instance < N) + { + res = v[instance]; + return true; + } + return false; + } + bool operator()(const std::vector& v) const + { + if(instance >= 0 && instance < v.size()) + { + res = ossia::convert(v[instance]); + return true; + } + return false; + } + bool operator()(const ossia::value_map_type& v) const { return false; } +}; template struct process_before_run { Exec_T& self; Obj_T& impl; + int instance{}; int start{}; int frames{}; + template + OSSIA_INLINE void update_value( + Field& field, const ossia::value& src, Value& dst, + avnd::field_index idx) const + { + // TODO + // if constexpr(oscr::real_good_mono_processor) + if(self.from_ossia_value(field, src, dst, idx)) + { + if_possible(field.update(impl)); + } + } + + // Some heads up for the most common cases, + handle mono deconstruction + template + requires std::is_arithmetic_v + OSSIA_INLINE void update_value( + Field& field, const ossia::value& src, Value& dst, + avnd::field_index idx) const + { + // FIXME time controls & smooth + using eff_t = avnd::effect_container; + static constexpr bool multi_instance = requires { sizeof(eff_t::multi_instance); }; + if constexpr( + multi_instance && !avnd::smooth_parameter && !avnd::time_control) + { + if(src.apply(apply_value_polyphonic_control{dst, instance})) + { + if_possible(field.update(impl)); + } + } + else + { + if(self.from_ossia_value(field, src, dst, idx)) + { + if_possible(field.update(impl)); + } + } + } template requires ossia_port void init_value(Field& ctrl, auto& port, avnd::field_index idx) const noexcept @@ -60,7 +159,7 @@ struct process_before_run if(!port.data.get_data().empty()) { auto& last = port.data.get_data().back().value; - update_value(self, impl, ctrl, last, ctrl.value, idx); + update_value(ctrl, last, ctrl.value, idx); if constexpr(avnd::control) { @@ -169,8 +268,7 @@ struct process_before_run { if(ts >= start && ts < start + frames) { - update_value( - self, impl, ctrl, val, ctrl.values[ts - start], avnd::field_index{}); + update_value(ctrl, val, ctrl.values[ts - start], avnd::field_index{}); } } } diff --git a/include/avnd/binding/ossia/port_setup.hpp b/include/avnd/binding/ossia/port_setup.hpp index db053bf4..ce2e0b29 100644 --- a/include/avnd/binding/ossia/port_setup.hpp +++ b/include/avnd/binding/ossia/port_setup.hpp @@ -517,6 +517,7 @@ struct setup_variable_audio_ports { Exec_T& self; Obj_T& impl; + int instance{}; using in_refl = avnd::audio_bus_input_introspection; using out_refl = avnd::audio_bus_output_introspection; @@ -549,6 +550,7 @@ struct setup_raw_ossia_ports { Exec_T& self; Obj_T& impl; + int instance{}; template void operator()(Field& ctrl, auto& port, avnd::field_index) const noexcept diff --git a/include/avnd/binding/ossia/time_controls.hpp b/include/avnd/binding/ossia/time_controls.hpp index e09d7ce8..ac756cca 100644 --- a/include/avnd/binding/ossia/time_controls.hpp +++ b/include/avnd/binding/ossia/time_controls.hpp @@ -57,7 +57,7 @@ struct time_control_storage : time_control_input_storage for(auto state : t.full_state()) { auto& port = avnd::pfr::get(state.inputs); - port.value = to_seconds(g.value, new_tempo); + port.value = to_seconds(g.value, new_tempo); // FIXME support multi-instance if_possible(port.update(state.effect)); } } diff --git a/include/avnd/wrappers/effect_container.hpp b/include/avnd/wrappers/effect_container.hpp index 50f30992..91cef541 100644 --- a/include/avnd/wrappers/effect_container.hpp +++ b/include/avnd/wrappers/effect_container.hpp @@ -298,14 +298,16 @@ struct effect_container multi_instance }; - typename T::inputs inputs_storage; + struct state + { + T effect; + typename T::inputs inputs_storage; + }; - std::vector effect; + std::vector effect; void init_channels(int input, int output) { effect.resize(std::max(input, output)); } - auto& inputs() noexcept { return inputs_storage; } - auto& inputs() const noexcept { return inputs_storage; } auto& outputs() noexcept { return dummy_instance; } auto& outputs() const noexcept { return dummy_instance; } @@ -313,16 +315,23 @@ struct effect_container { T& effect; typename T::inputs& inputs; - [[no_unique_address]] avnd::dummy outputs; }; - ref full_state(int i) { return {effect[i], this->inputs_storage, dummy_instance}; } + ref full_state(int i) + { + return {effect[i].effect, effect[i].inputs_storage, dummy_instance}; + } full_state_iterator full_state() { return full_state_iterator{*this}; } + member_iterator inputs() + { + for(auto& e : effect) + co_yield e.inputs_storage; + } auto effects() { return member_iterator_poly_effect{*this}; @@ -339,11 +348,10 @@ struct effect_container multi_instance }; - typename T::inputs inputs_storage; - struct state { T effect; + typename T::inputs inputs_storage; typename T::outputs outputs_storage; }; @@ -351,9 +359,6 @@ struct effect_container void init_channels(int input, int output) { effect.resize(std::max(input, output)); } - auto& inputs() noexcept { return inputs_storage; } - auto& inputs() const noexcept { return inputs_storage; } - struct ref { T& effect; @@ -363,7 +368,7 @@ struct effect_container ref full_state(int i) { - return {effect[i].effect, this->inputs_storage, effect[i].outputs_storage}; + return {effect[i].effect, effect[i].inputs_storage, effect[i].outputs_storage}; } full_state_iterator full_state() @@ -376,6 +381,11 @@ struct effect_container return member_iterator_poly_effect{*this}; } + member_iterator inputs() + { + for(auto& e : effect) + co_yield e.inputs_storage; + } member_iterator outputs() { for(auto& e : effect) @@ -393,9 +403,13 @@ struct effect_container multi_instance }; - typename T::inputs inputs_storage; + struct state + { + T effect; + typename T::inputs inputs_storage; + }; - std::vector effect; + std::vector effect; void init_channels(int input, int output) { @@ -403,9 +417,6 @@ struct effect_container effect.resize(input); } - auto& inputs() noexcept { return inputs_storage; } - auto& inputs() const noexcept { return inputs_storage; } - struct ref { T& effect; @@ -415,7 +426,7 @@ struct effect_container ref full_state(int i) { - return {effect[i].effect, this->inputs_storage, effect[i].effect.outputs}; + return {effect[i].effect, effect[i].inputs, effect[i].effect.outputs}; } full_state_iterator full_state() @@ -428,6 +439,11 @@ struct effect_container return member_iterator_poly_effect{*this}; } + member_iterator inputs() + { + for(auto& e : effect) + co_yield e.inputs_storage; + } member_iterator outputs() { for(auto& e : effect)