ported bw_osc_filt to new API

This commit is contained in:
Stefano D'Angelo 2022-11-28 18:31:52 +01:00
parent f98f07009d
commit 6b4b0a3429
4 changed files with 60 additions and 108 deletions

4
TODO
View File

@ -1,6 +1,6 @@
code:
* blep etc in a module?
* osc post filter init val? osc post filter and one pole init val from input?
* osc post filter (and one pole init, slew rate, etc.) val from input? set state instead?
* audio rate optional pulse width/slope inputs?
* one pole process const input? (return also if const out)
* optimize triangle generation for constant pulse width
@ -16,6 +16,6 @@ build system:
* make autodependencies (.d?)
* make from... directories
* order-only prerequisites to avoid multiple make updates? (https://interrupt.memfault.com/blog/gnu-make-guidelines#order-only-prerequisites)
* clang + Ofast & bulk memory (maybe using old binaryen)
* clang + Ofast & bulk memory (maybe using old binaryen) - or implement memset etc.
* put common parts of Makefiles together somewhere/somehow (DRY)
* recursive make?

View File

@ -28,6 +28,7 @@
#include <bw_math.h>
#include <bw_phase_gen.h>
#include <bw_osc_pulse.h>
#include <bw_osc_filt.h>
#include <bw_noise_gen.h>
#include <bw_svf.h>
#include <bw_osc_sin.h>
@ -35,12 +36,7 @@
#include <bw_env_follow.h>
/*
#include <bw_osc_pulse.h>
#include <bw_osc_filt.h>
#include <bw_svf.h>
#include <bw_env_gen.h>
#include <bw_vol.h>
#include <bw_env_follow.h>
*/
enum {
@ -66,6 +62,7 @@ struct _bw_example_synth {
bw_phase_gen_state phase_gen_state;
bw_osc_pulse_coeffs osc_pulse_coeffs;
bw_osc_pulse_state osc_pulse_state;
bw_osc_filt_state osc_filt_state;
bw_noise_gen_coeffs noise_gen_coeffs;
bw_svf_coeffs svf_coeffs;
bw_svf_state svf_state;
@ -75,20 +72,18 @@ struct _bw_example_synth {
bw_env_follow_coeffs env_follow_coeffs;
bw_env_follow_state env_follow_state;
/*
bw_osc_pulse osc_pulse;
bw_osc_filt osc_filt;
bw_env_gen env_gen;
bw_vol vol;
bw_env_follow env_follow;
*/
// Parameters
float params[p_n];
int param_changed;
// States
uint64_t rand_state;
int note;
float level;
int note_prev;
// Buffers
//float buf[BUFFER_SIZE];
@ -115,7 +110,6 @@ bw_example_synth bw_example_synth_new() {
/*
bw_osc_pulse_init(&instance->osc_pulse);
bw_osc_filt_init(&instance->osc_filt);
bw_svf_init(&instance->svf);
bw_env_gen_init(&instance->env_gen);
bw_osc_pulse_set_antialiasing(&instance->osc_pulse, 1);
@ -147,7 +141,6 @@ void bw_example_synth_reset(bw_example_synth instance) {
bw_phase_gen_reset_coeffs(&instance->phase_gen_coeffs);
bw_phase_gen_reset_state(&instance->phase_gen_coeffs, &instance->phase_gen_state, 0.f);
bw_osc_pulse_reset_coeffs(&instance->osc_pulse_coeffs);
bw_osc_pulse_reset_state(&instance->osc_pulse_coeffs, &instance->osc_pulse_state);
bw_phase_gen_reset_coeffs(&instance->a440_phase_gen_coeffs);
bw_phase_gen_reset_state(&instance->a440_phase_gen_coeffs, &instance->a440_phase_gen_state, 0.f);
bw_svf_reset_coeffs(&instance->svf_coeffs);
@ -156,23 +149,31 @@ void bw_example_synth_reset(bw_example_synth instance) {
bw_env_follow_reset_coeffs(&instance->env_follow_coeffs);
bw_env_follow_reset_state(&instance->env_follow_coeffs, &instance->env_follow_state);
/*
bw_osc_filt_reset(&instance->osc_filt);
bw_env_gen_reset(&instance->env_gen);
*/
instance->note = -1;
instance->note_prev = -2;
instance->param_changed = ~0;
}
#define PARAM_CHANGED_MASK(x) (1 << x)
void bw_example_synth_process(bw_example_synth instance, const float** x, float** y, int n_samples) {
// TODO: I was too lazy to keep track of master tune and note and only update when needed, could be improved
if (instance->note != -1) {
if ((instance->note != instance->note_prev) || (instance->param_changed & PARAM_CHANGED_MASK(p_master_tune))) {
bw_phase_gen_set_frequency(&instance->phase_gen_coeffs,
440.f * bw_pow2f_3(8.333333333333333e-2f * ((instance->note - 69) + 2.f * instance->params[p_master_tune] - 1.f)));
}
//bw_env_gen_set_gate(&instance->env_gen, 1);
} else
;
//bw_env_gen_set_gate(&instance->env_gen, 0);
if (instance->note != -1) {
if (instance->note_prev < 0) {
bw_osc_pulse_reset_state(&instance->osc_pulse_coeffs, &instance->osc_pulse_state);
bw_osc_filt_reset_state(&instance->osc_filt_state);
}
bw_phase_gen_update_coeffs_ctrl(&instance->phase_gen_coeffs);
bw_osc_pulse_update_coeffs_ctrl(&instance->osc_pulse_coeffs);
for (int i = 0; i < n_samples; i++) {
@ -182,8 +183,8 @@ void bw_example_synth_process(bw_example_synth instance, const float** x, float*
bw_osc_pulse_update_coeffs_audio(&instance->osc_pulse_coeffs);
y[0][i] = bw_osc_pulse_process1_antialias(&instance->osc_pulse_coeffs, &instance->osc_pulse_state, phase, phase_inc);
}
bw_osc_filt_process(&instance->osc_filt_state, y[0], y[0], n_samples);
} else {
bw_osc_pulse_reset_state(&instance->osc_pulse_coeffs, &instance->osc_pulse_state); // FIXME
bw_phase_gen_process(&instance->phase_gen_coeffs, &instance->phase_gen_state, NULL, NULL, NULL, n_samples);
for (int i = 0; i < n_samples; i++)
y[0][i] = 0.f;
@ -221,10 +222,16 @@ void bw_example_synth_process(bw_example_synth instance, const float** x, float*
bw_env_follow_process(&instance->env_follow_coeffs, &instance->env_follow_state, y[0], NULL, n_samples);
instance->level = bw_clipf(bw_env_follow_get_y_z1(&instance->env_follow_state), 0.f, 1.f);
instance->param_changed = 0;
instance->note_prev = instance->note;
}
void bw_example_synth_set_parameter(bw_example_synth instance, int index, float value) {
if (instance->params[index] == value)
return;
instance->params[index] = value;
instance->param_changed |= PARAM_CHANGED_MASK(index);
switch (index) {
case p_volume:
bw_vol_set_volume(&instance->vol_coeffs, value);
@ -264,10 +271,13 @@ float bw_example_synth_get_parameter(bw_example_synth instance, int index) {
}
void bw_example_synth_note_on(bw_example_synth instance, char note, char velocity) {
if (instance->note == note)
return;
instance->note = note;
}
void bw_example_synth_note_off(bw_example_synth instance, char note) {
if (note == instance->note)
if (note != instance->note)
return;
instance->note = -1;
}

View File

@ -33,7 +33,7 @@
* <ul>
* <li>Version <strong>0.2.0</strong>:
* <ul>
* <li>Refactored API to avoid dynamic memory allocation.</li>
* <li>Refactored API.</li>
* </ul>
* </li>
* <li>Version <strong>0.1.0</strong>:
@ -52,69 +52,62 @@
extern "C" {
#endif
#include <bw_common.h>
/*! api {{{
* #### bw_osc_filt
* #### bw_osc_filt_state
* ```>>> */
typedef struct _bw_osc_filt bw_osc_filt;
typedef struct _bw_osc_filt_state bw_osc_filt_state;
/*! <<<```
* Instance object.
* >>> */
/*! ...
* #### bw_osc_filt_init()
* ```>>> */
void bw_osc_filt_init(bw_osc_filt *instance);
/*! <<<```
* Initializes the `instance` object.
* >>> */
/*! ...
* #### bw_osc_filt_set_sample_rate()
*
* There is none (not needed).
* State
* >>> */
/*! ...
* #### bw_osc_filt_reset()
* ```>>> */
void bw_osc_filt_reset(bw_osc_filt *instance);
static inline void bw_osc_filt_reset_state(bw_osc_filt_state *BW_RESTRICT state);
/*! <<<```
* Resets the given `instance` to its initial state.
* >>> */
static inline float bw_osc_filt_process1(bw_osc_filt_state *BW_RESTRICT state, float x);
/*! ...
* #### bw_osc_filt_process()
* ```>>> */
void bw_osc_filt_process(bw_osc_filt *instance, const float *x, float* y, int n_samples);
static inline void bw_osc_filt_process(bw_osc_filt_state *BW_RESTRICT state, const float *x, float* y, int n_samples);
/*! <<<```
* Lets the given `instance` process `n_samples` samples from the input
* buffer `x` and fills the corresponding `n_samples` samples in the output
* buffer `y`.
* >>> */
/*! ...
* #### bw_osc_filt_set_enabled()
* ```>>> */
void bw_osc_filt_set_enabled(bw_osc_filt *instance, char value);
/*! <<<```
* Sets whether the filter is enabled (`value` non-`0`) or bypassed (`0`)
* for the given `instance`.
*
* Default value: non-`0`.
* }}} */
/* WARNING: the internal definition of this struct is not part of the public
* API. Its content may change at any time in future versions. Please, do not
* access its members directly. */
struct _bw_osc_filt {
// Parameters
char enabled;
/*** Implementation ***/
// State
/* WARNING: This part of the file is not part of the public API. Its content may
* change at any time in future versions. Please, do not use it directly. */
struct _bw_osc_filt_state {
float x_z1;
float y_z1;
};
static inline void bw_osc_filt_reset_state(bw_osc_filt_state *BW_RESTRICT state) {
state->x_z1 = 0.f;
state->y_z1 = 0.f;
}
static inline float bw_osc_filt_process1(bw_osc_filt_state *BW_RESTRICT state, float x) {
const float y = 1.371308261611209f * x + 0.08785458027104826f * state->x_z1 - 4.591628418822578e-1f * state->y_z1;
state->x_z1 = x;
state->y_z1 = y;
return y;
}
static inline void bw_osc_filt_process(bw_osc_filt_state *BW_RESTRICT state, const float *x, float* y, int n_samples) {
for (int i = 0; i < n_samples; i++)
y[i] = bw_osc_filt_process1(state, x[i]);
}
#ifdef __cplusplus
}

View File

@ -1,51 +0,0 @@
/*
* Brickworks
*
* Copyright (C) 2022 Orastron Srl unipersonale
*
* Brickworks is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* Brickworks is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
*
* File author: Stefano D'Angelo
*/
#include <bw_osc_filt.h>
#include <bw_common.h>
void bw_osc_filt_init(bw_osc_filt *instance) {
instance->enabled = 1;
}
void bw_osc_filt_reset(bw_osc_filt *instance) {
instance->x_z1 = 0.f;
instance->y_z1 = 0.f;
}
void bw_osc_filt_process(bw_osc_filt *instance, const float *x, float* y, int n_samples) {
if (instance->enabled)
for (int i = 0; i < n_samples; i++) {
const float v = 1.371308261611209f * x[i] + 0.08785458027104826f * instance->x_z1 - 4.591628418822578e-1f * instance->y_z1;
instance->x_z1 = x[i];
instance->y_z1 = y[i];
y[i] = v;
}
else {
for (int i = 0; i < n_samples; i++)
y[i] = x[i];
instance->x_z1 = instance->y_z1 = x[n_samples - 1];
}
}
void bw_osc_filt_set_enabled(bw_osc_filt *instance, char value) {
instance->enabled = value;
}