Skip to content

Commit

Permalink
poly instances: add an example of usage by transforming the basic exp…
Browse files Browse the repository at this point in the history
… smoother
  • Loading branch information
jcelerier committed Jan 4, 2025
1 parent 8db80a1 commit 053954b
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 52 deletions.
22 changes: 8 additions & 14 deletions examples/Raw/Interpolator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"; }
Expand All @@ -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;
}
};
}
88 changes: 50 additions & 38 deletions include/avnd/binding/ossia/data_node.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace oscr

// Special case for the easy non-audio case
template <ossia_compatible_nonaudio_processor T>
requires(!(avnd::tag_cv<T> && avnd::tag_stateless<T>))
requires(!avnd::tag_cv<T>)
class safe_node<T> : public safe_node_base<T, safe_node<T>>
{
public:
Expand Down Expand Up @@ -49,7 +49,7 @@ class safe_node<T> : public safe_node_base<T, safe_node<T>>
};

template <ossia_compatible_nonaudio_processor T>
requires(avnd::tag_cv<T> && avnd::tag_stateless<T>)
requires(avnd::tag_cv<T>)
class safe_node<T> : public safe_node_base<T, safe_node<T>>
{
public:
Expand All @@ -60,8 +60,27 @@ class safe_node<T> : public safe_node_base<T, safe_node<T>>
constexpr bool scan_audio_input_channels() { return false; }
// This function goes from a host-provided tick to what the plugin expects
template <typename Tick>
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<T>)
{
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<Tick>)
{
Expand Down Expand Up @@ -102,37 +121,35 @@ class safe_node<T> : public safe_node_base<T, safe_node<T>>
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 <std::size_t N>
void operator()(std::array<float, N> v)
{
std::array<float, N> 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<ossia::value>& v)
{
std::vector<ossia::value> res;
res.reserve(v.size());

for(std::size_t i = 0; i < v.size(); i++)
self.invoke_effect(self.impl.effect, ossia::convert<float>(v[i]), tick);
for(std::size_t i = 0, N = v.size(); i < N; i++)
self.invoke_effect(i, N, ossia::convert<float>(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<float>(val), tick);
self.invoke_effect(i++, N, ossia::convert<float>(val), tick);
}
}
};
Expand All @@ -147,30 +164,24 @@ class safe_node<T> : public safe_node_base<T, safe_node<T>>
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 <std::size_t N>
void operator()(std::array<float, N> v)
{
std::array<float, N> 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);
}

Expand All @@ -180,19 +191,20 @@ class safe_node<T> : public safe_node_base<T, safe_node<T>>
std::vector<ossia::value> 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<float>(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<ValType>(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<float>(val), tick));
k, self.invoke_effect(i++, N, ossia::convert<ValType>(val), tick));
}
out.write_value(std::move(res), 0);
}
Expand All @@ -208,27 +220,27 @@ class safe_node<T> : public safe_node_base<T, safe_node<T>>
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 <std::size_t N>
Expand Down Expand Up @@ -339,7 +351,7 @@ class safe_node<T> : public safe_node_base<T, safe_node<T>>
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
Expand Down
45 changes: 45 additions & 0 deletions include/avnd/wrappers/effect_container.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,51 @@ struct effect_container<T>
auto& outputs() const noexcept { return dummy_instance; }
};

// For CV objects
template <typename T>
requires(
avnd::tag_cv<T> && !avnd::tag_stateless<T>
&& !(avnd::audio_argument_processor<T> || avnd::audio_port_processor<T>)
&& avnd::inputs_is_value<T> && avnd::outputs_is_value<T>)
struct effect_container<T>
{
using type = T;
enum
{
multi_instance
};

inline constexpr void init_channels(int input, int output) { }
std::vector<T> 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<effect_container> full_state()
{
return full_state_iterator<effect_container>{*this};
}

auto effects() { return member_iterator_poly_effect<effect_container>{*this}; }

member_iterator<decltype(T::inputs)> inputs()
{
for(auto& e : effect)
co_yield e.inputs;
}
member_iterator<decltype(T::outputs)> outputs()
{
for(auto& e : effect)
co_yield e.outputs;
}
};

template <typename T>
struct get_object_type
{
Expand Down

0 comments on commit 053954b

Please sign in to comment.