-
-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add fft port support to the ossia backend
- Loading branch information
Showing
10 changed files
with
488 additions
and
65 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} | ||
}; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
}; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.