Skip to content

Commit

Permalink
Add fft port support to the ossia backend
Browse files Browse the repository at this point in the history
  • Loading branch information
jcelerier committed Apr 23, 2022
1 parent 6375b39 commit 369826d
Show file tree
Hide file tree
Showing 10 changed files with 488 additions and 65 deletions.
93 changes: 93 additions & 0 deletions examples/Helpers/PeakBandFFTPort.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#pragma once
#include <halp/audio.hpp>
#include <halp/controls.hpp>
#include <halp/meta.hpp>
#include <halp/fft.hpp>
#include <cmath>

namespace examples::helpers
{

/**
* For the usual case where we just want the spectrum of an input buffer,
* no need to template: we can ask it to be precomputed beforehand by the host.
*/
struct PeakBandFFTPort
{
halp_meta(name, "Peak band")
halp_meta(c_name, "avnd_peak_band")
halp_meta(uuid, "5610b62e-ef1f-4a34-abe0-e57816bc44c2")

// TODO implement user-controllable buffering to allow various fft sizes...
int buffer_size = 1024;

struct
{
// Here the host will fill audio.spectrum with a non-windowed FFT.
// Option A (an helper type is provided)
halp::audio_spectrum_channel<"In", double> audio;

// Option B with the raw spectrum:
struct {
halp_meta(name, "In 2");

double* channel{};
// complex numbers... using value_type = double[2] is also ok
std::complex<double>* spectrum{};
} audio_2;
} inputs;

struct
{
halp::val_port<"Peak", double> peak;
halp::val_port<"Band", int> band;

halp::val_port<"Peak 2", double> peak_2;
halp::val_port<"Band 2", int> band_2;
} outputs;

void operator()(int frames)
{
// Process with option A
{
outputs.peak = 0.;

// Compute the band with the highest amplitude
for (int k = 0; k < frames / 2; k++)
{
const double ampl = inputs.audio.spectrum.amplitude[k];
const double phas = inputs.audio.spectrum.phase[k];
const double mag_squared = ampl * ampl + phas * phas;

if (mag_squared > outputs.peak)
{
outputs.peak = mag_squared;
outputs.band = k;
}
}

outputs.peak = std::sqrt(outputs.peak);
}

// Process with option B
{
outputs.peak_2 = 0.;

// Compute the band with the highest amplitude
for (int k = 0; k < frames / 2; k++)
{
const double mag_squared = std::norm(inputs.audio_2.spectrum[k]);

if (mag_squared > outputs.peak_2)
{
outputs.peak_2 = mag_squared;
outputs.band_2 = k;
}
}

outputs.peak_2 = std::sqrt(outputs.peak_2);
}
}
};

}
117 changes: 117 additions & 0 deletions include/avnd/binding/ossia/ffts.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
#pragma once

/* SPDX-License-Identifier: GPL-3.0-or-later */

#include <avnd/concepts/all.hpp>
#include <avnd/concepts/parameter.hpp>
#include <avnd/introspection/input.hpp>
#include <avnd/introspection/output.hpp>
#include <avnd/introspection/port.hpp>
#include <ossia/dataflow/nodes/media.hpp>
#include <ossia/audio/fft.hpp>
#include <boost/mp11.hpp>

namespace oscr
{
template <typename Field>
using spectrum_fft_type
= ossia::fft;

template <typename T>
struct spectrum_split_channel_input_storage
{
void init(avnd::effect_container<T>& t, int buffer_size) { }
};

template <typename T>
struct spectrum_complex_channel_input_storage
{
void init(avnd::effect_container<T>& t, int buffer_size) { }
};

// Field:
// struct { T* amplitude; T* phase; } spectrum;
template <typename T>
requires(avnd::spectrum_split_channel_input_introspection<T>::size > 0)
struct spectrum_split_channel_input_storage<T>
{
using sc_in = avnd::spectrum_split_channel_input_introspection<T>;

using fft_tuple = avnd::filter_and_apply<
spectrum_fft_type,
avnd::spectrum_split_channel_input_introspection,
T>;

// std::tuple< ossia::fft, ossia::fft >
[[no_unique_address]] fft_tuple ffts;

void init(avnd::effect_container<T>& t, int buffer_size)
{
if constexpr (sc_in::size > 0)
{
auto init_raw_in = [&]<auto Idx, typename M>(M& port, avnd::predicate_index<Idx>)
{
// Get the matching fft in our storage
ossia::fft& fft = get<Idx>(this->ffts);

// Reserve space for the current buffer size
fft.reset(buffer_size);

port.spectrum.amplitude = nullptr;
port.spectrum.phase = nullptr;
};
sc_in::for_all_n(avnd::get_inputs(t), init_raw_in);
}
}
};

template <typename T>
requires(avnd::spectrum_complex_channel_input_introspection<T>::size > 0)
struct spectrum_complex_channel_input_storage<T>
{
using sc_in = avnd::spectrum_complex_channel_input_introspection<T>;

using fft_tuple = avnd::filter_and_apply<
spectrum_fft_type,
avnd::spectrum_complex_channel_input_introspection,
T>;

// std::tuple< ossia::fft, ossia::fft >
[[no_unique_address]] fft_tuple ffts;

void init(avnd::effect_container<T>& t, int buffer_size)
{
if constexpr (sc_in::size > 0)
{
auto init_raw_in = [&]<auto Idx, typename M>(M& port, avnd::predicate_index<Idx>)
{
// Get the matching fft in our storage
ossia::fft& fft = get<Idx>(this->ffts);

// Reserve space for the current buffer size
fft.reset(buffer_size);

port.spectrum = nullptr;
};
sc_in::for_all_n(avnd::get_inputs(t), init_raw_in);
}
}
};


template <typename T>
struct spectrum_storage
{
[[no_unique_address]]
spectrum_split_channel_input_storage<T> split_channel;
[[no_unique_address]]
spectrum_complex_channel_input_storage<T> complex_channel;

void init(avnd::effect_container<T>& t, int buffer_size)
{
split_channel.init(t, buffer_size);
complex_channel.init(t, buffer_size);
}
};

}
24 changes: 9 additions & 15 deletions include/avnd/binding/ossia/node.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <avnd/binding/ossia/port_run_postprocess.hpp>
#include <avnd/binding/ossia/port_run_preprocess.hpp>
#include <avnd/binding/ossia/port_setup.hpp>
#include <avnd/binding/ossia/ffts.hpp>
#include <avnd/binding/ossia/soundfiles.hpp>
#include <avnd/concepts/audio_port.hpp>
#include <avnd/concepts/gfx.hpp>
Expand Down Expand Up @@ -95,13 +96,6 @@ static void safety_checks(const ossia::token_request& tk, ossia::exec_state_faca

// void process(double** in, double** out, int N)

struct ui_communication
{
// Sends and receives info from the processor, e.g. audio signal, controls

// Sets the controls from the UI
};

template <typename T>
struct builtin_arg_audio_ports
{
Expand Down Expand Up @@ -205,13 +199,13 @@ class safe_node_base_base : public ossia::nonowning_graph_node

[[no_unique_address]] avnd::effect_container<T> impl;

[[no_unique_address]] builtin_arg_audio_ports<T> audio_ports;
[[no_unique_address]] oscr::builtin_arg_audio_ports<T> audio_ports;

[[no_unique_address]] builtin_message_value_ports<T> message_ports;
[[no_unique_address]] oscr::builtin_message_value_ports<T> message_ports;

[[no_unique_address]] inlet_storage<T> ossia_inlets;
[[no_unique_address]] oscr::inlet_storage<T> ossia_inlets;

[[no_unique_address]] outlet_storage<T> ossia_outlets;
[[no_unique_address]] oscr::outlet_storage<T> ossia_outlets;

[[no_unique_address]] avnd::process_adapter<T> processor;

Expand All @@ -225,8 +219,7 @@ class safe_node_base_base : public ossia::nonowning_graph_node

[[no_unique_address]] oscr::soundfile_storage<T> soundfiles;

// [[no_unique_address]]
// controls_mirror<T> feedback;
[[no_unique_address]] oscr::spectrum_storage<T> spectrums;

[[no_unique_address]] controls_queue<T> control;

Expand Down Expand Up @@ -262,6 +255,7 @@ class safe_node_base : public safe_node_base_base<T>
this->audio_ports.init(this->m_inlets, this->m_outlets);
this->message_ports.init(this->m_inlets);
this->soundfiles.init(this->impl);
this->spectrums.init(this->impl, buffer_size);

// constexpr const int total_input_channels = avnd::input_channels<T>(-1);
// constexpr const int total_output_channels = avnd::output_channels<T>(-1);
Expand All @@ -285,7 +279,7 @@ class safe_node_base : public safe_node_base_base<T>
using namespace tpl;
(f(avnd::pfr::get<Index>(in),
tuplet::get<Index>(this->ossia_inlets.ports),
avnd::num<Index>{}),
avnd::field_index<Index>{}),
...);
}
(typename info::indices_n{});
Expand All @@ -304,7 +298,7 @@ class safe_node_base : public safe_node_base_base<T>
{
(f(avnd::pfr::get<Index>(in),
tuplet::get<Index>(this->ossia_outlets.ports),
avnd::num<Index>{}),
avnd::field_index<Index>{}),
...);
}
(typename info::indices_n{});
Expand Down
Loading

0 comments on commit 369826d

Please sign in to comment.