From 94e63840e8925a24f2873ded6cc1bc42b6def81f Mon Sep 17 00:00:00 2001 From: Stefano D'Angelo Date: Wed, 30 Nov 2022 20:36:13 +0100 Subject: [PATCH] got further with synth mono + improved bw_nosie_gen and bw_pink_filt --- TODO | 4 + .../synth_mono/src/bw_example_synth_mono.c | 318 +++++++++++++----- .../synth_mono/src/bw_example_synth_mono.h | 2 + examples/synth_mono/src/config.h | 51 ++- examples/synth_mono/web/config.js | 131 +++++++- include/bw_noise_gen.h | 6 + include/bw_osc_tri.h | 4 +- include/bw_pink_filt.h | 45 ++- 8 files changed, 452 insertions(+), 109 deletions(-) diff --git a/TODO b/TODO index 61ebf1e..679a51e 100644 --- a/TODO +++ b/TODO @@ -10,6 +10,10 @@ code: * check const restrict etc. * define BW_RESTRICT to __restrict or similar when the compiler supports it * empty functions etc. to keep consistency and forward compatibility? +* float in [-1,1] for velocity, pitch bend, mod wheel +* vst3 pitch bend, mod wheel +* velocity = 0 -> note off? +* should clip slope in triangle? build system: * make makefiles handle paths with spaces etc diff --git a/examples/synth_mono/src/bw_example_synth_mono.c b/examples/synth_mono/src/bw_example_synth_mono.c index b455a74..dc74662 100644 --- a/examples/synth_mono/src/bw_example_synth_mono.c +++ b/examples/synth_mono/src/bw_example_synth_mono.c @@ -27,9 +27,13 @@ #include #include +#include #include +#include #include #include +#include +#include #include #include #include @@ -39,37 +43,75 @@ enum { p_volume, p_master_tune, p_portamento, - p_pulse_width, - p_kbd_ctrl, - p_cutoff, - p_Q, - p_contour, + p_mod_mix, + p_vco1_mod, + p_vco1_coarse, + p_vco1_fine, + p_vco1_waveform, + p_vco1_pw_slope, + p_vco1_volume, + p_vco2_mod, + p_vco2_coarse, + p_vco2_fine, + p_vco2_waveform, + p_vco2_pw_slope, + p_vco2_volume, + p_vco3_kbd, + p_vco3_coarse, + p_vco3_fine, + p_vco3_waveform, + p_vco3_pw_slope, + p_vco3_volume, + p_noise_color, + p_noise_volume, + p_vcf_mod, + p_vcf_kbd_ctrl, + p_vcf_cutoff, + p_vcf_Q, + p_vcf_contour, p_vcf_attack, p_vcf_decay, p_vcf_sustain, p_vcf_release, - p_attack, - p_decay, - p_sustain, - p_release, + p_vca_attack, + p_vca_decay, + p_vca_sustain, + p_vca_release, p_a440, p_n }; -#define BUFFER_SIZE 128 +#define BUFFER_SIZE 32 struct _bw_example_synth_mono { // Sub-components - bw_phase_gen_coeffs phase_gen_coeffs; - bw_phase_gen_state phase_gen_state; - bw_osc_pulse_coeffs osc_pulse_coeffs; + bw_osc_saw_coeffs vco_saw_coeffs; + bw_phase_gen_coeffs vco1_phase_gen_coeffs; + bw_phase_gen_state vco1_phase_gen_state; + bw_osc_pulse_coeffs vco1_pulse_coeffs; + bw_osc_tri_coeffs vco1_tri_coeffs; + bw_vol_coeffs vco1_vol_coeffs; + bw_phase_gen_coeffs vco2_phase_gen_coeffs; + bw_phase_gen_state vco2_phase_gen_state; + bw_osc_pulse_coeffs vco2_pulse_coeffs; + bw_osc_tri_coeffs vco2_tri_coeffs; + bw_vol_coeffs vco2_vol_coeffs; + bw_phase_gen_coeffs vco3_phase_gen_coeffs; + bw_phase_gen_state vco3_phase_gen_state; + bw_osc_pulse_coeffs vco3_pulse_coeffs; + bw_osc_tri_coeffs vco3_tri_coeffs; + bw_vol_coeffs vco3_vol_coeffs; bw_osc_filt_state osc_filt_state; + bw_noise_gen_coeffs noise_gen_coeffs; + bw_pink_filt_coeffs pink_filt_coeffs; + bw_pink_filt_state pink_filt_state; + bw_vol_coeffs noise_vol_coeffs; bw_env_gen_coeffs vcf_env_gen_coeffs; bw_env_gen_state vcf_env_gen_state; - bw_svf_coeffs svf_coeffs; - bw_svf_state svf_state; - bw_env_gen_coeffs env_gen_coeffs; - bw_env_gen_state env_gen_state; + bw_svf_coeffs vcf_coeffs; + bw_svf_state vcf_state; + bw_env_gen_coeffs vca_env_gen_coeffs; + bw_env_gen_state vca_env_gen_state; bw_phase_gen_coeffs a440_phase_gen_coeffs; bw_phase_gen_state a440_phase_gen_state; bw_vol_coeffs vol_coeffs; @@ -83,9 +125,12 @@ struct _bw_example_synth_mono { uint64_t rand_state; int note; char gate; + float pitch_bend; + float mod_wheel; + char notes_pressed[128]; // Buffers - float buf[BUFFER_SIZE]; + float buf[4][BUFFER_SIZE]; }; bw_example_synth_mono bw_example_synth_mono_new() { @@ -93,16 +138,39 @@ bw_example_synth_mono bw_example_synth_mono_new() { if (instance == NULL) return NULL; - bw_phase_gen_init(&instance->phase_gen_coeffs); - bw_osc_pulse_init(&instance->osc_pulse_coeffs); + bw_osc_saw_init(&instance->vco_saw_coeffs); + bw_phase_gen_init(&instance->vco1_phase_gen_coeffs); + bw_osc_pulse_init(&instance->vco1_pulse_coeffs); + bw_osc_tri_init(&instance->vco1_tri_coeffs); + bw_vol_init(&instance->vco1_vol_coeffs); + bw_phase_gen_init(&instance->vco2_phase_gen_coeffs); + bw_osc_pulse_init(&instance->vco2_pulse_coeffs); + bw_osc_tri_init(&instance->vco2_tri_coeffs); + bw_vol_init(&instance->vco2_vol_coeffs); + bw_phase_gen_init(&instance->vco3_phase_gen_coeffs); + bw_osc_pulse_init(&instance->vco3_pulse_coeffs); + bw_osc_tri_init(&instance->vco3_tri_coeffs); + bw_vol_init(&instance->vco3_vol_coeffs); + bw_noise_gen_init(&instance->noise_gen_coeffs, &instance->rand_state); + bw_pink_filt_init(&instance->pink_filt_coeffs); + bw_vol_init(&instance->noise_vol_coeffs); bw_env_gen_init(&instance->vcf_env_gen_coeffs); - bw_svf_init(&instance->svf_coeffs); - bw_env_gen_init(&instance->env_gen_coeffs); + bw_svf_init(&instance->vcf_coeffs); + bw_env_gen_init(&instance->vca_env_gen_coeffs); bw_phase_gen_init(&instance->a440_phase_gen_coeffs); bw_vol_init(&instance->vol_coeffs); bw_env_follow_init(&instance->env_follow_coeffs); - bw_osc_pulse_set_antialiasing(&instance->osc_pulse_coeffs, 1); + bw_osc_saw_set_antialiasing(&instance->vco_saw_coeffs, 1); + bw_osc_pulse_set_antialiasing(&instance->vco1_pulse_coeffs, 1); + bw_osc_tri_set_antialiasing(&instance->vco1_tri_coeffs, 1); + bw_osc_pulse_set_antialiasing(&instance->vco2_pulse_coeffs, 1); + bw_osc_tri_set_antialiasing(&instance->vco2_tri_coeffs, 1); + bw_vol_set_volume(&instance->vco2_vol_coeffs, 0.f); + bw_osc_pulse_set_antialiasing(&instance->vco3_pulse_coeffs, 1); + bw_osc_tri_set_antialiasing(&instance->vco3_tri_coeffs, 1); + bw_vol_set_volume(&instance->vco3_vol_coeffs, 0.f); + bw_vol_set_volume(&instance->noise_vol_coeffs, 0.f); bw_phase_gen_set_frequency(&instance->a440_phase_gen_coeffs, 440.f); bw_env_follow_set_release_tau(&instance->env_follow_coeffs, 1.f); @@ -116,27 +184,51 @@ void bw_example_synth_mono_free(bw_example_synth_mono instance) { } void bw_example_synth_mono_set_sample_rate(bw_example_synth_mono instance, float sample_rate) { - bw_phase_gen_set_sample_rate(&instance->phase_gen_coeffs, sample_rate); - bw_osc_pulse_set_sample_rate(&instance->osc_pulse_coeffs, sample_rate); + bw_phase_gen_set_sample_rate(&instance->vco1_phase_gen_coeffs, sample_rate); + bw_osc_pulse_set_sample_rate(&instance->vco1_pulse_coeffs, sample_rate); + bw_osc_tri_set_sample_rate(&instance->vco1_tri_coeffs, sample_rate); + bw_vol_set_sample_rate(&instance->vco1_vol_coeffs, sample_rate); + bw_phase_gen_set_sample_rate(&instance->vco2_phase_gen_coeffs, sample_rate); + bw_osc_pulse_set_sample_rate(&instance->vco2_pulse_coeffs, sample_rate); + bw_osc_tri_set_sample_rate(&instance->vco2_tri_coeffs, sample_rate); + bw_vol_set_sample_rate(&instance->vco2_vol_coeffs, sample_rate); + bw_phase_gen_set_sample_rate(&instance->vco3_phase_gen_coeffs, sample_rate); + bw_osc_pulse_set_sample_rate(&instance->vco3_pulse_coeffs, sample_rate); + bw_osc_tri_set_sample_rate(&instance->vco3_tri_coeffs, sample_rate); + bw_vol_set_sample_rate(&instance->vco3_vol_coeffs, sample_rate); + bw_noise_gen_set_sample_rate(&instance->noise_gen_coeffs, sample_rate); + bw_pink_filt_set_sample_rate(&instance->pink_filt_coeffs, sample_rate); + bw_vol_set_sample_rate(&instance->noise_vol_coeffs, sample_rate); bw_env_gen_set_sample_rate(&instance->vcf_env_gen_coeffs, sample_rate); - bw_svf_set_sample_rate(&instance->svf_coeffs, sample_rate); - bw_env_gen_set_sample_rate(&instance->env_gen_coeffs, sample_rate); + bw_svf_set_sample_rate(&instance->vcf_coeffs, sample_rate); + bw_env_gen_set_sample_rate(&instance->vca_env_gen_coeffs, sample_rate); bw_phase_gen_set_sample_rate(&instance->a440_phase_gen_coeffs, sample_rate); bw_vol_set_sample_rate(&instance->vol_coeffs, sample_rate); bw_env_follow_set_sample_rate(&instance->env_follow_coeffs, sample_rate); } void bw_example_synth_mono_reset(bw_example_synth_mono 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_phase_gen_reset_coeffs(&instance->vco1_phase_gen_coeffs); + bw_phase_gen_reset_state(&instance->vco1_phase_gen_coeffs, &instance->vco1_phase_gen_state, 0.f); + bw_osc_pulse_reset_coeffs(&instance->vco1_pulse_coeffs); + bw_osc_tri_reset_coeffs(&instance->vco1_tri_coeffs); + bw_phase_gen_reset_coeffs(&instance->vco2_phase_gen_coeffs); + bw_phase_gen_reset_state(&instance->vco2_phase_gen_coeffs, &instance->vco2_phase_gen_state, 0.f); + bw_osc_pulse_reset_coeffs(&instance->vco2_pulse_coeffs); + bw_osc_tri_reset_coeffs(&instance->vco2_tri_coeffs); + bw_phase_gen_reset_coeffs(&instance->vco3_phase_gen_coeffs); + bw_phase_gen_reset_state(&instance->vco3_phase_gen_coeffs, &instance->vco3_phase_gen_state, 0.f); + bw_osc_pulse_reset_coeffs(&instance->vco3_pulse_coeffs); + bw_osc_tri_reset_coeffs(&instance->vco3_tri_coeffs); bw_osc_filt_reset_state(&instance->osc_filt_state); + bw_pink_filt_reset_state(&instance->pink_filt_coeffs, &instance->pink_filt_state); + bw_vol_reset_coeffs(&instance->noise_vol_coeffs); bw_env_gen_reset_coeffs(&instance->vcf_env_gen_coeffs); bw_env_gen_reset_state(&instance->vcf_env_gen_coeffs, &instance->vcf_env_gen_state); - bw_svf_reset_coeffs(&instance->svf_coeffs); - bw_svf_reset_state(&instance->svf_coeffs, &instance->svf_state); - bw_env_gen_reset_coeffs(&instance->env_gen_coeffs); - bw_env_gen_reset_state(&instance->env_gen_coeffs, &instance->env_gen_state); + bw_svf_reset_coeffs(&instance->vcf_coeffs); + bw_svf_reset_state(&instance->vcf_coeffs, &instance->vcf_state); + bw_env_gen_reset_coeffs(&instance->vca_env_gen_coeffs); + bw_env_gen_reset_state(&instance->vca_env_gen_coeffs, &instance->vca_env_gen_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_vol_reset_coeffs(&instance->vol_coeffs); @@ -144,48 +236,77 @@ void bw_example_synth_mono_reset(bw_example_synth_mono instance) { bw_env_follow_reset_state(&instance->env_follow_coeffs, &instance->env_follow_state); instance->note = 60; instance->gate = 0; + instance->pitch_bend = 0.f; + instance->mod_wheel = 0.f; + for (int i = 0; i < 128; i++) + instance->notes_pressed[i] = 0; } void bw_example_synth_mono_process(bw_example_synth_mono instance, const float** x, float** y, int n_samples) { bw_env_gen_set_gate(&instance->vcf_env_gen_coeffs, instance->gate); - bw_env_gen_set_gate(&instance->env_gen_coeffs, instance->gate); - - 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->vca_env_gen_coeffs, instance->gate); + + int n = instance->params[p_vco3_kbd] >= 0.5f ? instance->note : 0; + bw_phase_gen_set_frequency(&instance->vco3_phase_gen_coeffs, 440.f * + bw_pow2f_3(6.f * instance->params[p_vco3_coarse] - 3.f + + 2.f * instance->pitch_bend - 1.f + + 8.333333333333333e-2f * ((n - 69) + 2.f * (instance->params[p_master_tune] + instance->params[p_vco3_fine]) - 2.f))); for (int i = 0; i < n_samples; i += BUFFER_SIZE) { float *out = y[0] + i; int n = bw_minf(n_samples - i, BUFFER_SIZE); - bw_phase_gen_process(&instance->phase_gen_coeffs, &instance->phase_gen_state, NULL, out, instance->buf, n); - bw_osc_pulse_process(&instance->osc_pulse_coeffs, out, instance->buf, out, n); - bw_osc_filt_process(&instance->osc_filt_state, out, out, n); - - bw_env_gen_process(&instance->vcf_env_gen_coeffs, &instance->vcf_env_gen_state, NULL, n); - float v = instance->params[p_cutoff] + instance->params[p_contour] * bw_env_gen_get_y_z1(&instance->vcf_env_gen_state); - float cutoff = 20.f + (20e3f - 20.f) * v * v * v; - if (instance->params[p_kbd_ctrl] < (1.f / 6.f)) - /* no keyboard control */; - else if (instance->params[p_kbd_ctrl] < (1.f / 6.f + 1.f / 3.f)) - cutoff *= bw_pow2f_3((0.629960524947437f * 8.333333333333333e-2f) * (instance->note - 60)); - else if (instance->params[p_kbd_ctrl] < (1.f / 6.f + 2.f / 3.f)) - cutoff *= bw_pow2f_3((0.793700525984100f * 8.333333333333333e-2f) * (instance->note - 60)); + bw_phase_gen_process(&instance->vco3_phase_gen_coeffs, &instance->vco3_phase_gen_state, NULL, out, instance->buf[0], n); + if (instance->params[p_vco3_waveform] >= (1.f / 4.f + 1.f / 2.f)) { + bw_osc_tri_process(&instance->vco3_tri_coeffs, out, instance->buf[0], out, n); + bw_osc_pulse_reset_coeffs(&instance->vco3_pulse_coeffs); + } else if (instance->params[p_vco3_waveform] >= (1.f / 4.f)) { + bw_osc_pulse_process(&instance->vco3_pulse_coeffs, out, instance->buf[0], out, n); + bw_osc_tri_reset_coeffs(&instance->vco3_tri_coeffs); + } else { + bw_osc_saw_process(&instance->vco_saw_coeffs, out, instance->buf[0], out, n); + bw_osc_pulse_reset_coeffs(&instance->vco3_pulse_coeffs); + bw_osc_tri_reset_coeffs(&instance->vco3_tri_coeffs); + } + + bw_noise_gen_process(&instance->noise_gen_coeffs, instance->buf[0], n); + if (instance->params[p_noise_color] >= 0.5f) + bw_pink_filt_process(&instance->pink_filt_coeffs, &instance->pink_filt_state, instance->buf[0], instance->buf[0], n); else + bw_pink_filt_reset_state(&instance->pink_filt_coeffs, &instance->pink_filt_state); + + for (int i = 0; i < n; i++) + instance->buf[1][i] = out[i] + instance->params[p_mod_mix] * (instance->buf[0][i] - out[i]); + + // TODO: vco1, vco2, mix + + bw_osc_filt_process(&instance->osc_filt_state, out, out, n); + + // TODO: mix noise here + + // TODO: modulation + bw_env_gen_process(&instance->vcf_env_gen_coeffs, &instance->vcf_env_gen_state, NULL, n); + float v = instance->params[p_vcf_cutoff] + instance->params[p_vcf_contour] * bw_env_gen_get_y_z1(&instance->vcf_env_gen_state); + float cutoff = 20.f + (20e3f - 20.f) * v * v * v; + if (instance->params[p_vcf_kbd_ctrl] >= (1.f / 6.f + 2.f / 3.f)) cutoff *= bw_pow2f_3(8.333333333333333e-2f * (instance->note - 60)); - bw_svf_set_cutoff(&instance->svf_coeffs, bw_clipf(cutoff, 20.f, 20e3f)); - bw_svf_process(&instance->svf_coeffs, &instance->svf_state, out, out, NULL, NULL, n); + else if (instance->params[p_vcf_kbd_ctrl] >= (1.f / 6.f + 1.f / 3.f)) + cutoff *= bw_pow2f_3((0.793700525984100f * 8.333333333333333e-2f) * (instance->note - 60)); + else if (instance->params[p_vcf_kbd_ctrl] >= (1.f / 6.f + 2.f / 3.f)) + cutoff *= bw_pow2f_3((0.629960524947437f * 8.333333333333333e-2f) * (instance->note - 60)); + // otherwise no kbd control + bw_svf_set_cutoff(&instance->vcf_coeffs, bw_clipf(cutoff, 20.f, 20e3f)); + bw_svf_process(&instance->vcf_coeffs, &instance->vcf_state, out, out, NULL, NULL, n); - bw_env_gen_process(&instance->env_gen_coeffs, &instance->env_gen_state, instance->buf, n); + bw_env_gen_process(&instance->vca_env_gen_coeffs, &instance->vca_env_gen_state, instance->buf[0], n); for (int j = 0; j < n; j++) - out[j] *= instance->buf[j]; + out[j] *= instance->buf[0][j]; - bw_phase_gen_process(&instance->a440_phase_gen_coeffs, &instance->a440_phase_gen_state, NULL, instance->buf, NULL, n); - bw_osc_sin_process(instance->buf, instance->buf, n); + bw_phase_gen_process(&instance->a440_phase_gen_coeffs, &instance->a440_phase_gen_state, NULL, instance->buf[0], NULL, n); + bw_osc_sin_process(instance->buf[0], instance->buf[0], n); if (instance->params[p_a440] >= 0.5f) for (int j = 0; j < n; j++) - out[j] += instance->buf[j]; + out[j] += instance->buf[0][j]; bw_vol_process(&instance->vol_coeffs, out, out, n); bw_env_follow_process(&instance->env_follow_coeffs, &instance->env_follow_state, out, NULL, n); @@ -201,13 +322,36 @@ void bw_example_synth_mono_set_parameter(bw_example_synth_mono instance, int ind bw_vol_set_volume(&instance->vol_coeffs, value); break; case p_portamento: - bw_phase_gen_set_portamento_tau(&instance->phase_gen_coeffs, value); + bw_phase_gen_set_portamento_tau(&instance->vco1_phase_gen_coeffs, value); + bw_phase_gen_set_portamento_tau(&instance->vco2_phase_gen_coeffs, value); + bw_phase_gen_set_portamento_tau(&instance->vco3_phase_gen_coeffs, value); break; - case p_pulse_width: - bw_osc_pulse_set_pulse_width(&instance->osc_pulse_coeffs, value); + case p_vco1_pw_slope: + bw_osc_pulse_set_pulse_width(&instance->vco1_pulse_coeffs, value); + bw_osc_tri_set_slope(&instance->vco1_tri_coeffs, bw_clipf(value, 0.001f, 0.999f)); break; - case p_Q: - bw_svf_set_Q(&instance->svf_coeffs, 0.5f + 9.5f * value); + case p_vco1_volume: + bw_vol_set_volume(&instance->vco1_vol_coeffs, value); + break; + case p_vco2_pw_slope: + bw_osc_pulse_set_pulse_width(&instance->vco2_pulse_coeffs, value); + bw_osc_tri_set_slope(&instance->vco2_tri_coeffs, bw_clipf(value, 0.001f, 0.999f)); + break; + case p_vco2_volume: + bw_vol_set_volume(&instance->vco2_vol_coeffs, value); + break; + case p_vco3_pw_slope: + bw_osc_pulse_set_pulse_width(&instance->vco3_pulse_coeffs, value); + bw_osc_tri_set_slope(&instance->vco3_tri_coeffs, bw_clipf(value, 0.001f, 0.999f)); + break; + case p_vco3_volume: + bw_vol_set_volume(&instance->vco3_vol_coeffs, value); + break; + case p_noise_volume: + bw_vol_set_volume(&instance->noise_vol_coeffs, value); + break; + case p_vcf_Q: + bw_svf_set_Q(&instance->vcf_coeffs, 0.5f + 9.5f * value); break; case p_vcf_attack: bw_env_gen_set_attack(&instance->vcf_env_gen_coeffs, value); @@ -221,17 +365,17 @@ void bw_example_synth_mono_set_parameter(bw_example_synth_mono instance, int ind case p_vcf_release: bw_env_gen_set_release(&instance->vcf_env_gen_coeffs, value); break; - case p_attack: - bw_env_gen_set_attack(&instance->env_gen_coeffs, value); + case p_vca_attack: + bw_env_gen_set_attack(&instance->vca_env_gen_coeffs, value); break; - case p_decay: - bw_env_gen_set_decay(&instance->env_gen_coeffs, value); + case p_vca_decay: + bw_env_gen_set_decay(&instance->vca_env_gen_coeffs, value); break; - case p_sustain: - bw_env_gen_set_sustain(&instance->env_gen_coeffs, value); + case p_vca_sustain: + bw_env_gen_set_sustain(&instance->vca_env_gen_coeffs, value); break; - case p_release: - bw_env_gen_set_release(&instance->env_gen_coeffs, value); + case p_vca_release: + bw_env_gen_set_release(&instance->vca_env_gen_coeffs, value); break; } } @@ -240,12 +384,32 @@ float bw_example_synth_mono_get_parameter(bw_example_synth_mono instance, int in return index < p_n ? instance->params[index] : bw_clipf(bw_env_follow_get_y_z1(&instance->env_follow_state), 0.f, 1.f); } +static void update_note_gate(bw_example_synth_mono instance) { + for (int i = 0; i < 128; i++) + if (instance->notes_pressed[i]) { + instance->note = i; + instance->gate = 1; + return; + } + instance->gate = 0; +} + void bw_example_synth_mono_note_on(bw_example_synth_mono instance, char note, char velocity) { - instance->note = note; - instance->gate = 1; + instance->notes_pressed[note] = 1; + update_note_gate(instance); } void bw_example_synth_mono_note_off(bw_example_synth_mono instance, char note) { - if (note == instance->note) - instance->gate = 0; + if (instance->notes_pressed[note]) { + instance->notes_pressed[note] = 0; + update_note_gate(instance); + } +} + +void bw_example_synth_mono_pitch_bend(bw_example_synth_mono instance, int value) { + instance->pitch_bend = (value - 0x4000) / (float)0x4000; +} + +void bw_example_synth_mono_mod_wheel(bw_example_synth_mono instance, char value) { + instance->mod_wheel = (float)value / 0x80; } diff --git a/examples/synth_mono/src/bw_example_synth_mono.h b/examples/synth_mono/src/bw_example_synth_mono.h index 5747613..934676b 100644 --- a/examples/synth_mono/src/bw_example_synth_mono.h +++ b/examples/synth_mono/src/bw_example_synth_mono.h @@ -35,6 +35,8 @@ void bw_example_synth_mono_set_parameter(bw_example_synth_mono instance, int ind float bw_example_synth_mono_get_parameter(bw_example_synth_mono instance, int index); void bw_example_synth_mono_note_on(bw_example_synth_mono instance, char note, char velocity); void bw_example_synth_mono_note_off(bw_example_synth_mono instance, char note); +void bw_example_synth_mono_pitch_bend(bw_example_synth_mono instance, int value); +void bw_example_synth_mono_mod_wheel(bw_example_synth_mono instance, char value); #ifdef __cplusplus } diff --git a/examples/synth_mono/src/config.h b/examples/synth_mono/src/config.h index 1ea0c80..1d2a43e 100644 --- a/examples/synth_mono/src/config.h +++ b/examples/synth_mono/src/config.h @@ -61,26 +61,47 @@ static struct config_io_bus config_buses_out[NUM_BUSES_OUT] = { { "Audio out", 1, 0, 0, IO_MONO } }; -#define NUM_PARAMETERS 18 +#define NUM_PARAMETERS 39 static struct config_parameter config_parameters[NUM_PARAMETERS] = { { "Volume", "Volume", "", 0, 0, 0, 0.5f }, { "Master tune", "Master tune", "st", 0, 0, 0, 0.5f }, { "Portamento", "Portamento", "s", 0, 0, 0, 0.f }, - { "Pulse width", "PW", "%", 0, 0, 0, 0.5f }, - { "Keyboard control", "Kbd ctrl", "", 0, 0, 3, 0.f }, - { "Cutoff", "Cutoff", "Hz", 0, 0, 0, 1.f }, - { "Q", "Q", "", 0, 0, 0, 0.f }, - { "Contour", "Contour", "", 0, 0, 0, 0.f }, - { "VCF attack", "VCF attack", "", 0, 0, 0, 0.f }, - { "VCF decay", "VCF decay", "", 0, 0, 0, 0.f }, - { "VCF sustain", "VCF sustain", "", 0, 0, 0, 1.f }, - { "VCF release", "VCF release", "", 0, 0, 0, 0.f }, - { "Attack", "Attack", "s", 0, 0, 0, 0.f }, - { "Decay", "Decay", "s", 0, 0, 0, 0.f }, - { "Sustain", "Sustain", "%", 0, 0, 0, 1.f }, - { "Release", "Release", "0", 0, 0, 0, 0.f }, - { "A440", "A440", "0", 0, 0, 1, 0.f }, + { "Modulation mix", "Mod mix", "%", 0, 0, 0, 0.f }, + { "VCO1 modulation", "VCO1 mod", "%", 0, 0, 0, 0.f }, + { "VCO1 coarse", "VCO1 coarse", "", 0, 0, 6, 0.5f }, + { "VCO1 fine", "VCO1 fine", "st", 0, 0, 0, 0.5f }, + { "VCO1 waveform", "VCO1 wave", "", 0, 0, 2, 0.f }, + { "VCO1 pulse width/slope", "VCO1 pw/slope", "%", 0, 0, 0, 0.5f }, + { "VCO1 volume", "VCO1 volume", "%", 0, 0, 0, 1.f }, + { "VCO2 modulation", "VCO2 mod", "%", 0, 0, 0, 0.f }, + { "VCO2 coarse", "VCO2 coarse", "", 0, 0, 6, 0.5f }, + { "VCO2 fine", "VCO2 fine", "st", 0, 0, 0, 0.5f }, + { "VCO2 waveform", "VCO2 wave", "", 0, 0, 2, 0.f }, + { "VCO2 pulse width/slope", "VCO2 pw/slope", "%", 0, 0, 0, 0.5f }, + { "VCO2 volume", "VCO2 volume", "%", 0, 0, 0, 0.f }, + { "VCO3 kyboard control", "VCO3 kbd ctrl", "", 0, 0, 1, 1.f }, + { "VCO3 coarse", "VCO3 coarse", "", 0, 0, 6, 0.5f }, + { "VCO3 fine", "VCO3 fine", "st", 0, 0, 0, 0.5f }, + { "VCO3 waveform", "VCO3 wave", "", 0, 0, 2, 0.f }, + { "VCO3 pulse width/slope", "VCO3 pw/slope", "%", 0, 0, 0, 0.5f }, + { "VCO3 volume", "VCO3 volume", "%", 0, 0, 0, 0.f }, + { "Noise color", "Noise color", "", 0, 0, 1, 0.f }, + { "Noise volume", "Noise volume", "%", 0, 0, 0, 0.f }, + { "VCF modulation", "VCF mod", "%", 0, 0, 0, 0.f }, + { "VCF keyboard control", "VCF kbd ctrl", "", 0, 0, 3, 0.f }, + { "VCF cutoff", "VCF cutoff", "Hz", 0, 0, 0, 1.f }, + { "VCF Q", "VCF Q", "", 0, 0, 0, 0.f }, + { "VCF contour", "VCF contour", "%", 0, 0, 0, 0.f }, + { "VCF attack", "VCF attack", "s", 0, 0, 0, 0.f }, + { "VCF decay", "VCF decay", "s", 0, 0, 0, 0.f }, + { "VCF sustain", "VCF sustain", "%", 0, 0, 0, 1.f }, + { "VCF release", "VCF release", "s", 0, 0, 0, 0.f }, + { "VCA attack", "VCA attack", "s", 0, 0, 0, 0.f }, + { "VCA decay", "VCA decay", "s", 0, 0, 0, 0.f }, + { "VCA sustain", "VCA sustain", "%", 0, 0, 0, 1.f }, + { "VCA release", "VCA release", "s", 0, 0, 0, 0.f }, + { "A440", "A440", "", 0, 0, 1, 0.f }, { "Level", "Level", "", 1, 0, 0, 0.f } }; diff --git a/examples/synth_mono/web/config.js b/examples/synth_mono/web/config.js index 8aaac27..b960572 100644 --- a/examples/synth_mono/web/config.js +++ b/examples/synth_mono/web/config.js @@ -41,28 +41,141 @@ var parameters = [ defaultValue: 0.0 }, { - name: "Pulse width", + name: "Modulation mix", + output: false, + defaultValue: 0.0 + }, + { + name: "VCO1 modulation", + output: false, + defaultValue: 0.0 + }, + { + name: "VCO1 coarse", + output: false, + defaultValue: 0.5, + step: 6 + }, + { + name: "VCO1 fine", output: false, defaultValue: 0.5 }, { - name: "Keyboard control", + name: "VCO1 waveform", + output: false, + defaultValue: 0.5, + step: 2 + }, + { + name: "VCO1 pulse width/slope", + output: false, + defaultValue: 0.5 + }, + { + name: "VCO1 volume", + output: false, + defaultValue: 1.0 + }, + { + name: "VCO2 modulation", + output: false, + defaultValue: 0.0 + }, + { + name: "VCO2 coarse", + output: false, + defaultValue: 0.5, + step: 6 + }, + { + name: "VCO2 fine", + output: false, + defaultValue: 0.5 + }, + { + name: "VCO2 waveform", + output: false, + defaultValue: 0.5, + step: 2 + }, + { + name: "VCO2 pulse width/slope", + output: false, + defaultValue: 0.5 + }, + { + name: "VCO2 volume", + output: false, + defaultValue: 0.0 + }, + { + name: "VCO3 keyboard control", + output: false, + defaultValue: 1.0, + step: 1 + }, + { + name: "VCO3 coarse", + output: false, + defaultValue: 0.5, + step: 6 + }, + { + name: "VCO3 fine", + output: false, + defaultValue: 0.5 + }, + { + name: "VCO3 waveform", + output: false, + defaultValue: 0.5, + step: 2 + }, + { + name: "VCO3 pulse width/slope", + output: false, + defaultValue: 0.5 + }, + { + name: "VCO3 volume", + output: false, + defaultValue: 0.0 + }, + { + name: "Noise color", + output: false, + defaultValue: 0.0, + step: 1 + }, + { + name: "Noise volume", + output: false, + defaultValue: 0.0 + }, + { + name: "VCF modulation", + output: false, + defaultValue: 0.0 + }, + { + name: "VCF keyboard control", output: false, defaultValue: 0.0, step: 3 }, { - name: "Cutoff", + name: "VCF cutoff", output: false, defaultValue: 1.0 }, { - name: "Q", + name: "VCF Q", output: false, defaultValue: 0.0 }, { - name: "Contour", + name: "VCF contour", output: false, defaultValue: 0.0 }, @@ -87,22 +200,22 @@ var parameters = [ defaultValue: 0.0 }, { - name: "Attack", + name: "VCA dttack", output: false, defaultValue: 0.0 }, { - name: "Decay", + name: "VCA decay", output: false, defaultValue: 0.0 }, { - name: "Sustain", + name: "VCA sustain", output: false, defaultValue: 1.0 }, { - name: "Release", + name: "VCA release", output: false, defaultValue: 0.0 }, diff --git a/include/bw_noise_gen.h b/include/bw_noise_gen.h index 2a7ccef..e32f9f6 100644 --- a/include/bw_noise_gen.h +++ b/include/bw_noise_gen.h @@ -98,6 +98,8 @@ static inline void bw_noise_gen_set_sample_rate_scaling(bw_noise_gen_coeffs *BW_ * Default value: `0`. * }}} */ +static inline float bw_noise_gen_get_scaling_k(bw_noise_gen_coeffs *BW_RESTRICT coeffs); + /*** Implementation ***/ /* WARNING: This part of the file is not part of the public API. Its content may @@ -145,6 +147,10 @@ static inline void bw_noise_gen_set_sample_rate_scaling(bw_noise_gen_coeffs *BW_ coeffs->sample_rate_scaling = value; } +static inline float bw_noise_gen_get_scaling_k(bw_noise_gen_coeffs *BW_RESTRICT coeffs) { + return coeffs->scaling_k; +} + #ifdef __cplusplus } #endif diff --git a/include/bw_osc_tri.h b/include/bw_osc_tri.h index b728f63..9070d74 100644 --- a/include/bw_osc_tri.h +++ b/include/bw_osc_tri.h @@ -103,12 +103,14 @@ static inline void bw_osc_tri_set_antialiasing(bw_osc_tri_coeffs *BW_RESTRICT co * >>> */ /*! ... - * #### bw_osc_tri_set_tri_width() + * #### bw_osc_tri_set_slope() * ```>>> */ static inline void bw_osc_tri_set_slope(bw_osc_tri_coeffs *BW_RESTRICT coeffs, float value); /*! <<<``` * Sets the slope (increasing time over period) to `value` (range [`0.f`, * `1.f`]) for the given `instance`. + * + * NOT TOO CLOSE TO TO 0.f and 1.f!!! * * Default value: `0.5f`. * }}} */ diff --git a/include/bw_pink_filt.h b/include/bw_pink_filt.h index 5c991dc..0e783e0 100644 --- a/include/bw_pink_filt.h +++ b/include/bw_pink_filt.h @@ -61,6 +61,8 @@ typedef struct _bw_pink_filt_state bw_pink_filt_state; * State * >>> */ +static inline void bw_pink_filt_init(bw_pink_filt_coeffs *BW_RESTRICT coeffs); + static inline void bw_pink_filt_set_sample_rate(bw_pink_filt_coeffs *BW_RESTRICT coeffs, float sample_rate); /*! ... @@ -73,16 +75,22 @@ static inline void bw_pink_filt_reset_state(const bw_pink_filt_coeffs *BW_RESTRI static inline float bw_pink_filt_process1(const bw_pink_filt_coeffs *BW_RESTRICT coeffs, bw_pink_filt_state *BW_RESTRICT state, float x); +static inline float bw_pink_filt_process1_scaling(const bw_pink_filt_coeffs *BW_RESTRICT coeffs, bw_pink_filt_state *BW_RESTRICT state, float x); + /*! ... * #### bw_pink_filt_process() * ```>>> */ -static inline void bw_pink_filt_process(const bw_pink_filt_coeffs *BW_RESTRICT coeffs, bw_pink_filt_state *BW_RESTRICT state, const float *x, float* y, int n_samples); +static inline void bw_pink_filt_process(bw_pink_filt_coeffs *BW_RESTRICT coeffs, bw_pink_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`. * }}} */ +static inline void bw_pink_filt_set_sample_rate_scaling(bw_noise_gen_coeffs *BW_RESTRICT coeffs, char value); + +static inline float bw_pink_filt_get_scaling_k(bw_noise_gen_coeffs *BW_RESTRICT coeffs); + /*** Implementation ***/ /* WARNING: This part of the file is not part of the public API. Its content may @@ -90,7 +98,10 @@ static inline void bw_pink_filt_process(const bw_pink_filt_coeffs *BW_RESTRICT c struct _bw_pink_filt_coeffs { // Coefficients - float k; + float scaling_k; + + // Parameters + float sample_rate_scaling; }; struct _bw_pink_filt_state { @@ -100,8 +111,12 @@ struct _bw_pink_filt_state { float s4_z1; }; +static inline void bw_pink_filt_init(bw_pink_filt_coeffs *BW_RESTRICT coeffs) { + coeffs->sample_rate_scaling = 0; +} + static inline void bw_pink_filt_set_sample_rate(bw_pink_filt_coeffs *BW_RESTRICT coeffs, float sample_rate) { - coeffs->k = bw_sqrtf_2(44100.f / sample_rate); + coeffs->scaling_k = 210.f / bw_sqrtf_2(sample_rate); } static inline void bw_pink_filt_reset_state(const bw_pink_filt_coeffs *BW_RESTRICT coeffs, bw_pink_filt_state *BW_RESTRICT state) { @@ -120,12 +135,28 @@ static inline float bw_pink_filt_process1(const bw_pink_filt_coeffs *BW_RESTRICT state->s3_z1 = 0.9687905029568185f * s3 - 0.265076791546676f * s2; const float s4 = 0.3882183163519794f * s3 + state->s4_z1; state->s4_z1 = 0.6573784623288251f * s4 - 0.04559677868080467 * s3; - return coeffs->k * s4; + return s4; } -static inline void bw_pink_filt_process(const bw_pink_filt_coeffs *BW_RESTRICT coeffs, bw_pink_filt_state *BW_RESTRICT state, const float *x, float* y, int n_samples) { - for (int i = 0; i < n_samples; i++) - y[i] = bw_pink_filt_process1(coeffs, state, x[i]); +static inline float bw_pink_filt_process1_scaling(const bw_pink_filt_coeffs *BW_RESTRICT coeffs, bw_pink_filt_state *BW_RESTRICT state, float x) { + return coeffs->scaling_k * bw_pink_filt_process1(coeffs, state, x); +} + +static inline void bw_pink_filt_process(bw_pink_filt_coeffs *BW_RESTRICT coeffs, bw_pink_filt_state *BW_RESTRICT state, const float *x, float* y, int n_samples) { + if (coeffs->sample_rate_scaling) + for (int i = 0; i < n_samples; i++) + y[i] = bw_pink_filt_process1_scaling(coeffs, state, x[i]); + else + for (int i = 0; i < n_samples; i++) + y[i] = bw_pink_filt_process1(coeffs, state, x[i]); +} + +static inline void bw_pink_filt_set_sample_rate_scaling(bw_noise_gen_coeffs *BW_RESTRICT coeffs, char value) { + coeffs->sample_rate_scaling = value; +} + +static inline float bw_pink_filt_get_scaling_k(bw_noise_gen_coeffs *BW_RESTRICT coeffs) { + return coeffs->scaling_k; } #ifdef __cplusplus