diff --git a/examples/Raw/Interpolator.hpp b/examples/Raw/Interpolator.hpp index b91f6d2f..26a5d9ad 100644 --- a/examples/Raw/Interpolator.hpp +++ b/examples/Raw/Interpolator.hpp @@ -13,14 +13,14 @@ struct ExponentialSmoothing static constexpr auto description() { return "Simplest real-time value smoothing"; } static constexpr auto uuid() { return "971739ae-14a3-4283-b0a0-96dbd367ce66"; } - struct + // Allows the object to be seen as a value, not audio, processor + enum { - struct - { - static constexpr auto name() { return "Input"; } - double value{}; - } in; + cv + }; + struct + { struct { static constexpr auto name() { return "Alpha"; } @@ -38,21 +38,15 @@ struct ExponentialSmoothing struct { - struct - { - static constexpr auto name() { return "Output"; } - double value{}; - } out; } outputs; double filtered{}; - void operator()() + double operator()(double in) { - const double in = this->inputs.in.value; const double a = this->inputs.alpha.value; filtered = in * a + filtered * (1.0f - a); - outputs.out.value = filtered; + return filtered; } }; } diff --git a/include/avnd/binding/ossia/data_node.hpp b/include/avnd/binding/ossia/data_node.hpp index bb9e86dc..c7c3dd23 100644 --- a/include/avnd/binding/ossia/data_node.hpp +++ b/include/avnd/binding/ossia/data_node.hpp @@ -7,7 +7,7 @@ namespace oscr // Special case for the easy non-audio case template - requires(!(avnd::tag_cv && avnd::tag_stateless)) + requires(!avnd::tag_cv) class safe_node : public safe_node_base> { public: @@ -49,7 +49,7 @@ class safe_node : public safe_node_base> }; template - requires(avnd::tag_cv && avnd::tag_stateless) + requires(avnd::tag_cv) class safe_node : public safe_node_base> { public: @@ -60,8 +60,27 @@ class safe_node : public safe_node_base> constexpr bool scan_audio_input_channels() { return false; } // This function goes from a host-provided tick to what the plugin expects template - static auto invoke_effect(T& obj, auto&& val, const Tick& t) + auto invoke_effect(int index, int N, auto&& val, const Tick& t) { + T* pobj{}; + if constexpr(avnd::tag_stateless) + { + pobj = &this->impl.effect; + } + else + { + this->impl.effect.reserve(N); + while(this->impl.effect.size() < N) + { + // FIXME this should be done before prepare, port copy, etc + if(this->impl.effect.empty()) + this->impl.effect.resize(1); + else + this->impl.effect.push_back(this->impl.effect[0]); + } + pobj = &this->impl.effect[index]; + } + auto& obj = *pobj; // clang-format off if constexpr(std::is_integral_v) { @@ -102,37 +121,35 @@ class safe_node : public safe_node_base> const Tick& tick; int ts{}; void operator()() { } - void operator()(ossia::impulse) { self.invoke_effect(self.impl.effect, 0, tick); } - void operator()(bool v) { self.invoke_effect(self.impl.effect, v ? 1 : 0, tick); } - void operator()(int v) { self.invoke_effect(self.impl.effect, v, tick); } - void operator()(float v) { self.invoke_effect(self.impl.effect, v, tick); } + void operator()(ossia::impulse) { self.invoke_effect(0, 1, 0, tick); } + void operator()(bool v) { self.invoke_effect(0, 1, v ? 1 : 0, tick); } + void operator()(int v) { self.invoke_effect(0, 1, v, tick); } + void operator()(float v) { self.invoke_effect(0, 1, v, tick); } void operator()(const std::string& v) { - self.invoke_effect(self.impl.effect, std::stof(v), tick); + self.invoke_effect(0, 1, std::stof(v), tick); } template void operator()(std::array v) { std::array res; for(int i = 0; i < N; i++) - res[i] = self.invoke_effect(self.impl.effect, v[i], tick); + res[i] = self.invoke_effect(i, N, v[i], tick); } // FIXME handle recursion void operator()(const std::vector& v) { - std::vector res; - res.reserve(v.size()); - - for(std::size_t i = 0; i < v.size(); i++) - self.invoke_effect(self.impl.effect, ossia::convert(v[i]), tick); + for(std::size_t i = 0, N = v.size(); i < N; i++) + self.invoke_effect(i, N, ossia::convert(v[i]), tick); } void operator()(const ossia::value_map_type& v) { - ossia::value_map_type res; + int N = v.size(); + int i = 0; for(auto& [k, val] : v) { - self.invoke_effect(self.impl.effect, ossia::convert(val), tick); + self.invoke_effect(i++, N, ossia::convert(val), tick); } } }; @@ -147,30 +164,24 @@ class safe_node : public safe_node_base> void operator()() { } void operator()(ossia::impulse) { - out.write_value(self.invoke_effect(self.impl.effect, 0, tick), 0); + out.write_value(self.invoke_effect(0, 1, 0, tick), 0); } void operator()(bool v) { - out.write_value(self.invoke_effect(self.impl.effect, v ? 1 : 0, tick), 0); - } - void operator()(int v) - { - out.write_value(self.invoke_effect(self.impl.effect, v, tick), 0); - } - void operator()(float v) - { - out.write_value(self.invoke_effect(self.impl.effect, v, tick), 0); + out.write_value(self.invoke_effect(0, 1, v ? 1 : 0, tick), 0); } + void operator()(int v) { out.write_value(self.invoke_effect(0, 1, v, tick), 0); } + void operator()(float v) { out.write_value(self.invoke_effect(0, 1, v, tick), 0); } void operator()(const std::string& v) { - out.write_value(self.invoke_effect(self.impl.effect, std::stof(v), tick), 0); + out.write_value(self.invoke_effect(0, 1, std::stof(v), tick), 0); } template void operator()(std::array v) { std::array res; for(int i = 0; i < N; i++) - res[i] = self.invoke_effect(self.impl.effect, v[i], tick); + res[i] = self.invoke_effect(i, N, v[i], tick); out.write_value(res, 0); } @@ -180,19 +191,20 @@ class safe_node : public safe_node_base> std::vector res; res.reserve(v.size()); - for(std::size_t i = 0; i < v.size(); i++) - res.push_back( - self.invoke_effect(self.impl.effect, ossia::convert(v[i]), tick)); + for(std::size_t i = 0, N = v.size(); i < N; i++) + res.push_back(self.invoke_effect(i, N, ossia::convert(v[i]), tick)); out.write_value(std::move(res), 0); } void operator()(const ossia::value_map_type& v) { + int N = v.size(); + int i = 0; ossia::value_map_type res; for(auto& [k, val] : v) { res.emplace_back( - k, self.invoke_effect(self.impl.effect, ossia::convert(val), tick)); + k, self.invoke_effect(i++, N, ossia::convert(val), tick)); } out.write_value(std::move(res), 0); } @@ -208,27 +220,27 @@ class safe_node : public safe_node_base> void operator()() { } void operator()(ossia::impulse) { - if(auto res = self.invoke_effect(self.impl.effect, 0, tick)) + if(auto res = self.invoke_effect(0, 1, 0, tick)) out.write_value(*res, 0); } void operator()(bool v) { - if(auto res = self.invoke_effect(self.impl.effect, v ? 1 : 0, tick)) + if(auto res = self.invoke_effect(0, 1, v ? 1 : 0, tick)) out.write_value(*res, 0); } void operator()(int v) { - if(auto res = self.invoke_effect(self.impl.effect, v, tick)) + if(auto res = self.invoke_effect(0, 1, v, tick)) out.write_value(*res, 0); } void operator()(float v) { - if(auto res = self.invoke_effect(self.impl.effect, v, tick)) + if(auto res = self.invoke_effect(0, 1, v, tick)) out.write_value(*res, 0); } void operator()(const std::string& v) { - if(auto res = self.invoke_effect(self.impl.effect, std::stof(v), tick)) + if(auto res = self.invoke_effect(0, 1, std::stof(v), tick)) out.write_value(*res, 0); } template @@ -339,7 +351,7 @@ class safe_node : public safe_node_base> for(const ossia::timed_value& val : this->arg_value_ports.in->get_data()) { this->arg_value_ports.out->write_value( - invoke_effect(this->impl.effect, val.value, tick), 0); + invoke_effect(0, 1, val.value, tick), 0); } } else diff --git a/include/avnd/wrappers/effect_container.hpp b/include/avnd/wrappers/effect_container.hpp index 91cef541..326152ff 100644 --- a/include/avnd/wrappers/effect_container.hpp +++ b/include/avnd/wrappers/effect_container.hpp @@ -565,6 +565,51 @@ struct effect_container auto& outputs() const noexcept { return dummy_instance; } }; +// For CV objects +template + requires( + avnd::tag_cv && !avnd::tag_stateless + && !(avnd::audio_argument_processor || avnd::audio_port_processor) + && avnd::inputs_is_value && avnd::outputs_is_value) +struct effect_container +{ + using type = T; + enum + { + multi_instance + }; + + inline constexpr void init_channels(int input, int output) { } + std::vector effect; + + struct ref + { + T& effect; + decltype(T::inputs)& inputs; + decltype(T::outputs)& outputs; + }; + + ref full_state(int i) { return {effect[i], effect[i].inputs, effect[i].outputs}; } + + full_state_iterator full_state() + { + return full_state_iterator{*this}; + } + + auto effects() { return member_iterator_poly_effect{*this}; } + + member_iterator inputs() + { + for(auto& e : effect) + co_yield e.inputs; + } + member_iterator outputs() + { + for(auto& e : effect) + co_yield e.outputs; + } +}; + template struct get_object_type {