Skip to content

Commit

Permalink
[ossia] Many improvements to bindings
Browse files Browse the repository at this point in the history
- Improve available variables for managing timing
- Refactor in more, smaller files
- Improved support for messages inputs
- Improved support for using ossia-specific datatypes in ossia objects,
  e.g. ossia::token_request, etc. to ease porting objects to avendish
- Define new tags for objects, to handle various ways of specifying temporal behaviours
- Add a specific log slider / log knob type
- Add generic control / no_control widget types
- Add support for using magic_enum for reflecting enumeration values
- Add many helpers to midi messages busses
- Add many helpers for processing musical quantification
  • Loading branch information
jcelerier committed Oct 14, 2024
1 parent ccd98fc commit 27caa41
Show file tree
Hide file tree
Showing 27 changed files with 1,122 additions and 421 deletions.
1 change: 0 additions & 1 deletion examples/Advanced/Utilities/LFO.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ struct LFO
"Waveform", Sin, Sin, Triangle, Saw, Square, SampleAndHold, Noise1, Noise2,
Noise3)
} waveform;

} inputs;
struct
{
Expand Down
47 changes: 47 additions & 0 deletions include/avnd/binding/ossia/builtin_ports.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#pragma once
#include <avnd/concepts/processor.hpp>
#include <avnd/introspection/messages.hpp>
#include <ossia/dataflow/graph_node.hpp>
namespace oscr
{

template <typename T>
struct builtin_arg_audio_ports
{
void init(ossia::inlets& inlets, ossia::outlets& outlets) { }
};

template <avnd::audio_argument_processor T>
struct builtin_arg_audio_ports<T>
{
ossia::audio_inlet in;
ossia::audio_outlet out;

void init(ossia::inlets& inlets, ossia::outlets& outlets)
{
inlets.push_back(&in);
outlets.push_back(&out);
}
};

template <typename T>
struct builtin_message_value_ports
{
void init(ossia::inlets& inlets) { }
};

template <typename T>
requires(avnd::messages_introspection<T>::size > 0)
struct builtin_message_value_ports<T>
{
ossia::value_inlet message_inlets[avnd::messages_introspection<T>::size];
void init(ossia::inlets& inlets)
{
for(auto& in : message_inlets)
{
inlets.push_back(&in);
}
}
};

}
82 changes: 82 additions & 0 deletions include/avnd/binding/ossia/callbacks.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#pragma once
#include <avnd/binding/ossia/to_value.hpp>
#include <ossia/dataflow/value_port.hpp>

namespace oscr
{

template <typename Self, std::size_t Idx, typename Field, typename R, typename... Args>
struct do_callback;

template <typename Self, std::size_t Idx, typename Field, typename R, typename... Args>
requires(!requires { Field::timestamp; })
struct do_callback<Self, Idx, Field, R, Args...>
{
Self& self;
Field& field;
R operator()(Args... args)
{
// Idx is the index of the port in the complete input array.
// We need to map it to the callback index.
ossia::value_outlet& port = tuplet::get<Idx>(self.ossia_outlets.ports);
if constexpr(sizeof...(Args) == 0)
{
port.data.write_value(ossia::impulse{}, self.start_frame_for_this_tick);
}
else if constexpr(sizeof...(Args) == 1)
{
port.data.write_value(
to_ossia_value(field, args...), self.start_frame_for_this_tick);
}
else
{
std::vector<ossia::value> vec{to_ossia_value(field, args)...};
port.data.write_value(std::move(vec), self.start_frame_for_this_tick);
}

if constexpr(!std::is_void_v<R>)
return R{};
}
};

template <typename Self, std::size_t Idx, typename Field, typename R, typename... Args>
requires(requires { Field::timestamp; })
struct do_callback<Self, Idx, Field, R, Args...>
{
Self& self;
Field& field;

void do_call(int64_t ts)
{
// Idx is the index of the port in the complete input array.
// We need to map it to the callback index.
ossia::value_outlet& port = tuplet::get<Idx>(self.ossia_outlets.ports);
port.data.write_value(ossia::impulse{}, self.start_frame_for_this_tick + ts);
}

template <typename U>
void do_call(int64_t ts, U&& u)
{
// Idx is the index of the port in the complete input array.
// We need to map it to the callback index.
ossia::value_outlet& port = tuplet::get<Idx>(self.ossia_outlets.ports);
port.data.write_value(
to_ossia_value(field, std::forward<U>(u)), self.start_frame_for_this_tick + ts);
}

template <typename... U>
void do_call(int64_t ts, U&&... us)
{
ossia::value_outlet& port = tuplet::get<Idx>(self.ossia_outlets.ports);
std::vector<ossia::value> vec{to_ossia_value(field, us)...};
port.data.write_value(std::move(vec), self.start_frame_for_this_tick + ts);
}

R operator()(Args... args)
{
do_call(static_cast<Args&&>(args)...);
if constexpr(!std::is_void_v<R>)
return R{};
}
};
}
81 changes: 81 additions & 0 deletions include/avnd/binding/ossia/controls.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#pragma once
#include <avnd/introspection/input.hpp>
#include <avnd/introspection/output.hpp>
#include <boost/smart_ptr/atomic_shared_ptr.hpp>
#include <ossia/detail/lockfree_queue.hpp>

namespace oscr
{

template <typename Field>
using controls_type = std::decay_t<decltype(Field::value)>;

template <typename T>
using atomic_shared_ptr = boost::atomic_shared_ptr<T>;

template <typename T>
struct controls_mirror
{
static constexpr int i_size = avnd::control_input_introspection<T>::size;
static constexpr int o_size = avnd::control_output_introspection<T>::size;
using i_tuple
= avnd::filter_and_apply<controls_type, avnd::control_input_introspection, T>;
using o_tuple
= avnd::filter_and_apply<controls_type, avnd::control_output_introspection, T>;

controls_mirror()
{
inputs.load(new i_tuple);
outputs.load(new o_tuple);
}

[[no_unique_address]] atomic_shared_ptr<i_tuple> inputs;
[[no_unique_address]] atomic_shared_ptr<o_tuple> outputs;

std::bitset<i_size> inputs_bits;
std::bitset<o_size> outputs_bits;
};

template <typename T>
struct controls_input_queue
{
using i_tuple = std::tuple<>;
};
template <typename T>
struct controls_output_queue
{
using o_tuple = std::tuple<>;
};
template <typename T>
requires(avnd::control_input_introspection<T>::size > 0)
struct controls_input_queue<T>
{
static constexpr int i_size = avnd::control_input_introspection<T>::size;
using i_tuple
= avnd::filter_and_apply<controls_type, avnd::control_input_introspection, T>;

ossia::mpmc_queue<i_tuple> ins_queue;
std::bitset<i_size> inputs_set;
};

template <typename T>
requires(avnd::control_output_introspection<T>::size > 0)
struct controls_output_queue<T>
{
static constexpr int o_size = avnd::control_output_introspection<T>::size;
using o_tuple
= avnd::filter_and_apply<controls_type, avnd::control_output_introspection, T>;

ossia::mpmc_queue<o_tuple> outs_queue;

std::bitset<o_size> outputs_set;
};

template <typename T>
struct controls_queue
: controls_input_queue<T>
, controls_output_queue<T>
{
};

}
5 changes: 3 additions & 2 deletions include/avnd/binding/ossia/data_node.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class safe_node<T> : public safe_node_base<T, safe_node<T>>

auto [start, frames] = st.timings(tk);

if(!this->prepare_run(tk, start, frames))
if(!this->prepare_run(tk, st, start, frames))
{
this->finish_run();
return;
Expand All @@ -40,7 +40,8 @@ class safe_node<T> : public safe_node_base<T, safe_node<T>>
this->process_smooth();

avnd::invoke_effect(
this->impl, avnd::get_tick_or_frames(this->impl, tick_info{tk, st, frames}));
this->impl,
avnd::get_tick_or_frames(this->impl, tick_info{*this, tk, st, frames}));

this->finish_run();
}
Expand Down
6 changes: 6 additions & 0 deletions include/avnd/binding/ossia/from_value.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1389,6 +1389,12 @@ inline void from_ossia_value(auto& field, const ossia::value& src, ossia::value&
dst = src;
}

inline void
from_ossia_value(auto& field, const ossia::value& src, std::optional<ossia::value>& dst)
{
dst = src;
}

inline void from_ossia_value(auto& field, const ossia::value& src, auto& dst)
{
from_ossia_value(src, dst);
Expand Down
Loading

0 comments on commit 27caa41

Please sign in to comment.