Skip to content

Commit

Permalink
[pd] Update audio processor to also handle more varied message types
Browse files Browse the repository at this point in the history
  • Loading branch information
jcelerier committed Oct 1, 2024
1 parent 9e4d8bf commit efbcbbf
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 61 deletions.
7 changes: 7 additions & 0 deletions examples/Raw/Aggregate.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ namespace examples
{
/**
* This example showcases using user-defined aggregates for value ports.
*
* Note that it only makes sense in environments that support hierarchic data structures:
* - ossia
* - Max/MSP with a dict as input
* - programming languages like Python
*
* For instance, it does not make sense in PureData or audio plugins
*/
struct Aggregate
{
Expand Down
58 changes: 7 additions & 51 deletions include/avnd/binding/pd/audio_processor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include <avnd/binding/pd/helpers.hpp>
#include <avnd/binding/pd/init.hpp>
#include <avnd/binding/pd/inputs.hpp>
#include <avnd/binding/pd/messages.hpp>
#include <avnd/common/export.hpp>
#include <avnd/concepts/object.hpp>
Expand Down Expand Up @@ -58,6 +59,7 @@ struct audio_processor
avnd::process_adapter<T> processor;

std::array<t_int, dsp_input_count> dsp_inputs;
inputs<T> input_setup;

[[no_unique_address]] init_arguments<T> init_setup;
[[no_unique_address]] messages<T> messages_setup;
Expand All @@ -84,6 +86,8 @@ struct audio_processor
for(int i = 0; i < output_channels; i++)
outlet_new(&x_obj, &s_signal);

// control_inputs is *not* initialized here, all the messages will go through 1st inlet.

/// Initialize controls
if constexpr(avnd::has_inputs<T>)
{
Expand Down Expand Up @@ -160,61 +164,13 @@ struct audio_processor
return ++w;
}

void process_inlet_control(t_symbol* s, int argc, t_atom* argv)
{
for(auto state : implementation.full_state())
{
switch(argv[0].a_type)
{
case A_FLOAT: {
// Note: teeeechnically, one could store a map of string -> {void*,typeid} and then cast...
// but most pd externals seem to just do a chain of if() so this is equivalent
float res = argv[0].a_w.w_float;
avnd::for_each_field_ref(state.inputs, [s, res, &state]<typename C>(C& ctl) {
if constexpr(
avnd::float_parameter<C> || avnd::int_parameter<C>
|| avnd::bool_parameter<C>)
{
static constexpr auto control_name = pd::get_name_symbol<C>();
if(control_name == s->s_name)
{
avnd::apply_control(ctl, res);
if_possible(ctl.update(state.effect));
}
}
});
break;
}

case A_SYMBOL: {
// TODO ?
std::string_view res = argv[0].a_w.w_symbol->s_name;
// thread_local for perf ?
avnd::for_each_field_ref(state.inputs, [s, &res, &state]<typename C>(C& ctl) {
if constexpr(avnd::string_parameter<C>)
{
static constexpr auto control_name = pd::get_name_symbol<C>();
if(control_name == s->s_name)
{
avnd::apply_control(ctl, std::move(res));
if_possible(ctl.update(state.effect));
}
}
});
break;
}

default:
break;
}
}
}

void process(t_symbol* s, int argc, t_atom* argv)
{
// First try to process messages handled explicitely in the object
if(messages_setup.process_messages(implementation, s, argc, argv))
return;
if(input_setup.process_inputs(implementation, s, argc, argv))
return;

// Then some default behaviour
switch(argc)
Expand All @@ -237,7 +193,7 @@ struct audio_processor
}
default: {
// Apply the data to the inlets.
process_inlet_control(s, argc, argv);
// process_inlet_control(s, argc, argv); // -> done in input_setup.process_inputs

// Then bang
// output_setup.commit(implementation);
Expand Down
35 changes: 26 additions & 9 deletions include/avnd/binding/pd/inputs.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,19 @@ struct inputs
return true;
}
}
else
{
static std::once_flag f;
std::call_once(f, [] {
post("Field type not supported as input: %s",
#if defined(_MSC_VER)
__FUNCSIG__
#else
__PRETTY_FUNCTION__
#endif
);
});
}
return false;
}

Expand All @@ -382,15 +395,19 @@ struct inputs
{
bool ok = false;
std::string_view symname = s->s_name;
avnd::parameter_input_introspection<T>::for_all(
avnd::get_inputs(implementation), [&]<typename M>(M& field) {
if(ok)
return;
if(symname == pd::get_name_symbol<M>())
{
ok = process_inlet_control(implementation.effect, field, argc, argv);
}
});

for(auto state : implementation.full_state())
{
avnd::parameter_input_introspection<T>::for_all(
state.inputs, [&,&obj=state.effect]<typename M>(M& field) {
if(ok)
return;
if(symname == pd::get_name_symbol<M>())
{
ok = process_inlet_control(obj, field, argc, argv);
}
});
}
return ok;
}
return false;
Expand Down
1 change: 0 additions & 1 deletion include/avnd/binding/pd/message_processor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,6 @@ struct message_processor

void process(t_symbol* s, int argc, t_atom* argv)
{

// First try to process messages handled explicitely in the object
if(messages_setup.process_messages(implementation, s, argc, argv))
return;
Expand Down

0 comments on commit efbcbbf

Please sign in to comment.