diff --git a/examples/synth_mono/src/plugin.h b/examples/synth_mono/src/plugin.h index 2704c5d..33104bf 100644 --- a/examples/synth_mono/src/plugin.h +++ b/examples/synth_mono/src/plugin.h @@ -474,7 +474,7 @@ static void plugin_process(plugin *instance, const float **inputs, float **outpu const char sync = instance->sync_left == instance->sync_count; - // osc 3 + // vco 3 bw_phase_gen_process(&instance->vco3_phase_gen_coeffs, &instance->vco3_phase_gen_state, NULL, out, instance->buf[0], n); switch (instance->vco3_waveform_cur) { @@ -504,7 +504,7 @@ static void plugin_process(plugin *instance, const float **inputs, float **outpu if (sync) instance->mod_k = instance->buf[1][0]; - // osc 1 + // vco 1 bw_buf_scale(instance->buf[1], instance->vco1_modulation, instance->buf[2], n); bw_phase_gen_process(&instance->vco1_phase_gen_coeffs, &instance->vco1_phase_gen_state, instance->buf[2], instance->buf[2], instance->buf[3], n); @@ -520,7 +520,7 @@ static void plugin_process(plugin *instance, const float **inputs, float **outpu break; } - // osc 2 + // vco 2 bw_buf_scale(instance->buf[1], instance->vco2_modulation, instance->buf[1], n); bw_phase_gen_process(&instance->vco2_phase_gen_coeffs, &instance->vco2_phase_gen_state, instance->buf[1], instance->buf[1], instance->buf[3], n); diff --git a/examples/synth_poly/src/plugin.h b/examples/synth_poly/src/plugin.h index 21fbc05..52dad44 100644 --- a/examples/synth_poly/src/plugin.h +++ b/examples/synth_poly/src/plugin.h @@ -89,6 +89,7 @@ typedef struct plugin { voice voices[N_VOICES]; size_t sync_count; + float noise_kv[2]; uint64_t rand_state; float master_tune; @@ -116,11 +117,20 @@ typedef struct plugin { float mod_wheel; size_t sync_left; char vco3_waveform_cur; - char noise_color_cur; char vco1_waveform_cur; char vco2_waveform_cur; float buf[BUFFER_SIZE]; + + float * b0[N_VOICES]; + float * b1[N_VOICES]; + float * b2[N_VOICES]; + float * b3[N_VOICES]; + float * b4[N_VOICES]; + bw_osc_filt_state * osc_filt_states[N_VOICES]; + bw_pink_filt_state * pink_filt_states[N_VOICES]; + bw_env_gen_state * vcf_env_gen_states[N_VOICES]; + bw_env_gen_state * vca_env_gen_states[N_VOICES]; } plugin; static void plugin_init(plugin *instance) { @@ -160,6 +170,19 @@ static void plugin_init(plugin *instance) { bw_phase_gen_set_frequency(&instance->a440_phase_gen_coeffs, 440.f); instance->rand_state = 0xbaddecaf600dfeed; + + for (int i = 0; i < N_VOICES; j++) { + instance->b0[i] = instance->voices[i].buf[0]; + instance->b1[i] = instance->voices[i].buf[1]; + instance->b2[i] = instance->voices[i].buf[2]; + instance->b3[i] = instance->voices[i].buf[3]; + instance->b4[i] = instance->voices[i].buf[4]; + instance->osc_filt_states[i] = &instance->voices[i].osc_filt_state; + instance->pink_filt_states[i] = &instance->voices[i].pink_filt_state; + instance->vcf_env_gen_states[i] = &instance->voices[i].vcf_env_gen_state; + instance->vca_env_gen_states[i] = &instance->voices[i].vca_env_gen_state; + } + } static void plugin_fini(plugin *instance) { @@ -194,6 +217,9 @@ static void plugin_set_sample_rate(plugin *instance, float sample_rate) { } instance->sync_count = (size_t)bw_roundf(sample_rate * SYNC_RATE); + + instance->noise_kv[0] = 6.f * bw_noise_gen_get_scaling_k(&instance->noise_gen_coeffs) * bw_pink_filt_get_scaling_k(&instance->pink_filt_coeffs); + instance->noise_kv[1] = 0.1f * bw_noise_gen_get_scaling_k(&instance->noise_gen_coeffs); } static size_t plugin_mem_req(plugin *instance) { @@ -256,7 +282,6 @@ static void plugin_reset(plugin *instance) { instance->mod_wheel = 0.f; instance->sync_left = instance->sync_count; instance->vco3_waveform_cur = instance->vco3_waveform; - instance->noise_color_cur = instance->noise_color; instance->vco1_waveform_cur = instance->vco1_waveform; instance->vco2_waveform_cur = instance->vco2_waveform; } @@ -467,7 +492,7 @@ static void plugin_process(plugin *instance, const float **inputs, float **outpu const float df3 = instance->vco3_coarse + instance->pitch_bend + (8.333333333333333e-2f * 0.01f) * instance->vco3_fine; for (int i = 0; i < N_VOICES; i++) { int n = instance->voices[i].note - 69; - int n3 = instance->vco3_kbd_ctrl ? instance->voices[i].note - 69 : -69; + int n3 = instance->vco3_kbd_ctrl ? n : -69; bw_phase_gen_set_frequency(&instance->voices[i].vco1_phase_gen_coeffs, instance->master_tune * bw_pow2f(df1 + 8.333333333333333e-2f * n)); bw_phase_gen_set_frequency(&instance->voices[i].vco2_phase_gen_coeffs, instance->master_tune * bw_pow2f(df2 + 8.333333333333333e-2f * n)); bw_phase_gen_set_frequency(&instance->voices[i].vco3_phase_gen_coeffs, instance->master_tune * bw_pow2f(df3 + 8.333333333333333e-2f * n3)); @@ -485,13 +510,6 @@ static void plugin_process(plugin *instance, const float **inputs, float **outpu instance->vco3_waveform_cur = instance->vco3_waveform; } - if (instance->noise_color_cur != instance->noise_color) { - if (instance->noise_color == 2) - for (int j = 0; j < N_VOICES; j++) - bw_pink_filt_reset_state(&instance->pink_filt_coeffs, &instance->voices[j].pink_filt_state, 0.f); - instance->noise_color_cur = instance->noise_color; - } - if (instance->vco1_waveform_cur != instance->vco1_waveform) { switch (instance->vco1_waveform) { case 2: @@ -516,25 +534,24 @@ static void plugin_process(plugin *instance, const float **inputs, float **outpu instance->vco2_waveform_cur = instance->vco2_waveform; } - // synchronous control-rate and audio-rate operations + const float cutoff_unmapped = 0.1447648273010839f * bw_logf(0.05f * instance->vcf_cutoff); + static const float cutoff_kbd_kv[4] = { + 0.f, // off + 0.629960524947437f * 8.333333333333333e-2f, // 1/3 + 0.793700525984100f * 8.333333333333333e-2f, // 2/3 + 8.333333333333333e-2f // full + }; + float cutoff_kbd_k[N_VOICES]; + for (int i = 0; i < N_VOICES; i++) + cutoff_kbd_k[i] = bw_pow2f(cutoff_kbd_kv[instance->vcf_kbd_ctrl - 1] * (instance->voices[i].note - 60)); + + const float noise_k = instance->noise_kv[instance->noise_color - 1]; - float *b0[N_VOICES], *b1[N_VOICES], *b2[N_VOICES], *b3[N_VOICES], *b4[N_VOICES]; char gates[N_VOICES]; - bw_osc_filt_state *osc_filt_states[N_VOICES]; - bw_pink_filt_state *pink_filt_states[N_VOICES]; - bw_env_gen_state *vcf_env_gen_states[N_VOICES], *vca_env_gen_states[N_VOICES]; - for (int j = 0; j < N_VOICES; j++) { - b0[j] = instance->voices[j].buf[0]; - b1[j] = instance->voices[j].buf[1]; - b2[j] = instance->voices[j].buf[2]; - b3[j] = instance->voices[j].buf[3]; - b4[j] = instance->voices[j].buf[4]; - gates[j] = instance->voices[j].gate; - osc_filt_states[j] = &instance->voices[j].osc_filt_state; - pink_filt_states[j] = &instance->voices[j].pink_filt_state; - vcf_env_gen_states[j] = &instance->voices[j].vcf_env_gen_state; - vca_env_gen_states[j] = &instance->voices[j].vca_env_gen_state; - } + for (int i = 0; i < N_VOICES; i++) + gates[i] = instance->voices[i].gate; + + // synchronous control-rate and audio-rate operations for (size_t i = 0; i < n_samples; ) { float *out = outputs[0] + i; @@ -542,132 +559,116 @@ static void plugin_process(plugin *instance, const float **inputs, float **outpu const char sync = instance->sync_left == instance->sync_count; - // osc 3 + // vco 3 for (int j = 0; j < N_VOICES; j++) - bw_phase_gen_process(&instance->voices[j].vco3_phase_gen_coeffs, &instance->voices[j].vco3_phase_gen_state, NULL, b0[j], b1[j], n); + bw_phase_gen_process(&instance->voices[j].vco3_phase_gen_coeffs, &instance->voices[j].vco3_phase_gen_state, NULL, instance->b0[j], instance->b1[j], n); switch (instance->vco3_waveform_cur) { case 1: - bw_osc_saw_process_multi(&instance->vco_saw_coeffs, (const float **)b0, (const float **)b1, b0, N_VOICES, n); + bw_osc_saw_process_multi(&instance->vco_saw_coeffs, (const float **)instance->b0, (const float **)instance->b1, instance->b0, N_VOICES, n); break; case 2: - bw_osc_pulse_process_multi(&instance->vco3_pulse_coeffs, (const float **)b0, (const float **)b1, b0, N_VOICES, n); + bw_osc_pulse_process_multi(&instance->vco3_pulse_coeffs, (const float **)instance->b0, (const float **)instance->b1, instance->b0, N_VOICES, n); break; default: - bw_osc_tri_process_multi(&instance->vco3_tri_coeffs, (const float **)b0, (const float **)b1, b0, N_VOICES, n); + bw_osc_tri_process_multi(&instance->vco3_tri_coeffs, (const float **)instance->b0, (const float **)instance->b1, instance->b0, N_VOICES, n); break; } // noise generator - bw_noise_gen_process_multi(&instance->noise_gen_coeffs, b1, N_VOICES, n); - if (instance->noise_color_cur == 2) - bw_pink_filt_process_multi(&instance->pink_filt_coeffs, pink_filt_states, (const float **)b1, b1, N_VOICES, n); - bw_buf_scale_multi((const float * const *)b1, 5.f, b1, N_VOICES, n); + bw_noise_gen_process_multi(&instance->noise_gen_coeffs, instance->b1, N_VOICES, n); + if (instance->noise_color == 2) + bw_pink_filt_process_multi(&instance->pink_filt_coeffs, instance->pink_filt_states, (const float **)instance->b1, instance->b1, N_VOICES, n); + // no need to ever reset pink filt, as inputs are noise and filters are static + bw_buf_scale_multi((const float * const *)instance->b1, 5.f, instance->b1, N_VOICES, n); // modulation signals for (int j = 0; j < N_VOICES; j++) { for (int k = 0; k < n; k++) - b2[j][k] = instance->mod_wheel * (b0[j][k] + instance->modulation_mix * (b1[j][k] - b0[j][k])); + instance->b2[j][k] = instance->mod_wheel * (instance->b0[j][k] + instance->modulation_mix * (instance->b1[j][k] - instance->b0[j][k])); } if (sync) for (int j = 0; j < N_VOICES; j++) - instance->voices[j].mod_k = b2[j][0]; + instance->voices[j].mod_k = instance->b2[j][0]; - // osc 1 + // vco 1 for (int j = 0; j < N_VOICES; j++) { - bw_buf_scale(b2[j], instance->vco1_modulation, b3[j], n); - bw_phase_gen_process(&instance->voices[j].vco1_phase_gen_coeffs, &instance->voices[j].vco1_phase_gen_state, b3[j], b3[j], b4[j], n); + bw_buf_scale(instance->b2[j], instance->vco1_modulation, instance->b3[j], n); + bw_phase_gen_process(&instance->voices[j].vco1_phase_gen_coeffs, &instance->voices[j].vco1_phase_gen_state, instance->b3[j], instance->b3[j], instance->b4[j], n); } switch (instance->vco1_waveform_cur) { case 1: - bw_osc_saw_process_multi(&instance->vco_saw_coeffs, (const float **)b3, (const float **)b4, b3, N_VOICES, n); + bw_osc_saw_process_multi(&instance->vco_saw_coeffs, (const float **)instance->b3, (const float **)instance->b4, instance->b3, N_VOICES, n); break; case 2: - bw_osc_pulse_process_multi(&instance->vco1_pulse_coeffs, (const float **)b3, (const float **)b4, b3, N_VOICES, n); + bw_osc_pulse_process_multi(&instance->vco1_pulse_coeffs, (const float **)instance->b3, (const float **)instance->b4, instance->b3, N_VOICES, n); break; default: - bw_osc_tri_process_multi(&instance->vco1_tri_coeffs, (const float **)b3, (const float **)b4, b3, N_VOICES, n); + bw_osc_tri_process_multi(&instance->vco1_tri_coeffs, (const float **)instance->b3, (const float **)instance->b4, instance->b3, N_VOICES, n); break; } - // osc 2 + // vco 2 for (int j = 0; j < N_VOICES; j++) { - bw_buf_scale(b2[j], instance->vco2_modulation, b2[j], n); - bw_phase_gen_process(&instance->voices[j].vco2_phase_gen_coeffs, &instance->voices[j].vco2_phase_gen_state, b2[j], b2[j], b4[j], n); + bw_buf_scale(instance->b2[j], instance->vco2_modulation, instance->b2[j], n); + bw_phase_gen_process(&instance->voices[j].vco2_phase_gen_coeffs, &instance->voices[j].vco2_phase_gen_state, instance->b2[j], instance->b2[j], instance->b4[j], n); } switch (instance->vco2_waveform_cur) { case 1: - bw_osc_saw_process_multi(&instance->vco_saw_coeffs, (const float **)b2, (const float **)b4, b2, N_VOICES, n); + bw_osc_saw_process_multi(&instance->vco_saw_coeffs, (const float **)instance->b2, (const float **)instance->b4, instance->b2, N_VOICES, n); break; case 2: - bw_osc_pulse_process_multi(&instance->vco2_pulse_coeffs, (const float **)b2, (const float **)b4, b2, N_VOICES, n); + bw_osc_pulse_process_multi(&instance->vco2_pulse_coeffs, (const float **)instance->b2, (const float **)instance->b4, instance->b2, N_VOICES, n); break; default: - bw_osc_tri_process_multi(&instance->vco2_tri_coeffs, (const float **)b2, (const float **)b4, b2, N_VOICES, n); + bw_osc_tri_process_multi(&instance->vco2_tri_coeffs, (const float **)instance->b2, (const float **)instance->b4, instance->b2, N_VOICES, n); break; } // mixer - bw_gain_process_multi(&instance->vco1_gain_coeffs, (const float **)b3, b3, N_VOICES, n); - bw_gain_process_multi(&instance->vco2_gain_coeffs, (const float **)b2, b2, N_VOICES, n); - bw_gain_process_multi(&instance->vco3_gain_coeffs, (const float **)b0, b0, N_VOICES, n); - bw_gain_process_multi(&instance->noise_gain_coeffs, (const float **)b1, b1, N_VOICES, n); - bw_buf_mix_multi((const float * const *)b0, (const float * const *)b2, b0, N_VOICES, n); - bw_buf_mix_multi((const float * const *)b0, (const float * const *)b3, b0, N_VOICES, n); + bw_gain_process_multi(&instance->vco1_gain_coeffs, (const float **)instance->b3, instance->b3, N_VOICES, n); + bw_gain_process_multi(&instance->vco2_gain_coeffs, (const float **)instance->b2, instance->b2, N_VOICES, n); + bw_gain_process_multi(&instance->vco3_gain_coeffs, (const float **)instance->b0, instance->b0, N_VOICES, n); + bw_gain_process_multi(&instance->noise_gain_coeffs, (const float **)instance->b1, instance->b1, N_VOICES, n); + bw_buf_mix_multi((const float * const *)instance->b0, (const float * const *)instance->b2, instance->b0, N_VOICES, n); + bw_buf_mix_multi((const float * const *)instance->b0, (const float * const *)instance->b3, instance->b0, N_VOICES, n); - bw_osc_filt_process_multi(osc_filt_states, (const float **)b0, b0, N_VOICES, n); + bw_osc_filt_process_multi(instance->osc_filt_states, (const float **)instance->b0, instance->b0, N_VOICES, n); - const float k = instance->noise_color_cur == 2 - ? 6.f * bw_noise_gen_get_scaling_k(&instance->noise_gen_coeffs) * bw_pink_filt_get_scaling_k(&instance->pink_filt_coeffs) - : 0.1f * bw_noise_gen_get_scaling_k(&instance->noise_gen_coeffs); - bw_buf_scale_multi((const float * const *)b1, k, b1, N_VOICES, n); - bw_buf_mix_multi((const float * const *)b0, (const float * const *)b1, b0, N_VOICES, n); + bw_buf_scale_multi((const float * const *)instance->b1, noise_k, instance->b1, N_VOICES, n); + bw_buf_mix_multi((const float * const *)instance->b0, (const float * const *)instance->b1, instance->b0, N_VOICES, n); // vcf - bw_env_gen_process_multi(&instance->vcf_env_gen_coeffs, vcf_env_gen_states, gates, NULL, N_VOICES, n); + bw_env_gen_process_multi(&instance->vcf_env_gen_coeffs, instance->vcf_env_gen_states, gates, NULL, N_VOICES, n); if (sync) - for (int j = 0; j < N_VOICES; j++) - instance->voices[j].vcf_env_k = bw_env_gen_get_y_z1(vcf_env_gen_states[j]); - const float cutoff_unmapped = 0.1447648273010839f * bw_logf(0.05f * instance->vcf_cutoff); - for (int j = 0; j < N_VOICES; j++) { - const float cutoff_vpos = - cutoff_unmapped - + instance->vcf_contour * instance->voices[j].vcf_env_k - + 0.3f * instance->vcf_modulation * instance->voices[j].mod_k; - float cutoff = 20.f * bw_expf(6.907755278982137 * cutoff_vpos); - switch (instance->vcf_kbd_ctrl) { - case 2: // 1/3 - cutoff *= bw_pow2f((0.629960524947437f * 8.333333333333333e-2f) * (instance->voices[j].note - 60)); - break; - case 3: // 2/3 - cutoff *= bw_pow2f((0.793700525984100f * 8.333333333333333e-2f) * (instance->voices[j].note - 60)); - break; - case 4: // full - cutoff *= bw_pow2f(8.333333333333333e-2f * (instance->voices[j].note - 60)); - break; - default: // off, do nothing - break; + for (int j = 0; j < N_VOICES; j++) { + instance->voices[j].vcf_env_k = bw_env_gen_get_y_z1(instance->vcf_env_gen_states[j]); + const float cutoff_vpos = + cutoff_unmapped + + instance->vcf_contour * instance->voices[j].vcf_env_k + + 0.3f * instance->vcf_modulation * instance->voices[j].mod_k; + float cutoff = cutoff_kbd_k[j] * 20.f * bw_expf(6.907755278982137 * cutoff_vpos); + bw_svf_set_cutoff(&instance->voices[j].vcf_coeffs, bw_clipf(cutoff, 20.f, 20e3f)); } - bw_svf_set_cutoff(&instance->voices[j].vcf_coeffs, bw_clipf(cutoff, 20.f, 20e3f)); - bw_svf_process(&instance->voices[j].vcf_coeffs, &instance->voices[j].vcf_state, b0[j], b0[j], NULL, NULL, n); - } + for (int j = 0; j < N_VOICES; j++) + bw_svf_process(&instance->voices[j].vcf_coeffs, &instance->voices[j].vcf_state, instance->b0[j], instance->b0[j], NULL, NULL, n); // vca - bw_env_gen_process_multi(&instance->vca_env_gen_coeffs, vca_env_gen_states, gates, b1, N_VOICES, n); - bw_buf_mul_multi((const float * const *)b0, (const float * const *)b1, b0, N_VOICES, n); + bw_env_gen_process_multi(&instance->vca_env_gen_coeffs, instance->vca_env_gen_states, gates, instance->b1, N_VOICES, n); + bw_buf_mul_multi((const float * const *)instance->b0, (const float * const *)instance->b1, instance->b0, N_VOICES, n); // mix voices bw_buf_fill(0.f, out, n); for (int j = 0; j < N_VOICES; j++) - bw_buf_mix(out, b0[j], out, n); + bw_buf_mix(out, instance->b0[j], out, n); // A 440 Hz osc diff --git a/examples/synthpp_poly/src/impl.cpp b/examples/synthpp_poly/src/impl.cpp index c33c5e5..6c2db6b 100644 --- a/examples/synthpp_poly/src/impl.cpp +++ b/examples/synthpp_poly/src/impl.cpp @@ -93,6 +93,7 @@ public: Voice voices[N_VOICES]; size_t syncCount; + float noiseKV[2]; uint64_t randState; float masterTune; @@ -120,11 +121,16 @@ public: float modWheel; size_t syncLeft; char vco3WaveformCur; - char noiseColorCur; char vco1WaveformCur; char vco2WaveformCur; float buf[BUFFER_SIZE]; + + float * b0[N_VOICES]; + float * b1[N_VOICES]; + float * b2[N_VOICES]; + float * b3[N_VOICES]; + float * b4[N_VOICES]; }; extern "C" { @@ -148,6 +154,11 @@ impl impl_new(void) { for (int i = 0; i < N_VOICES; i++) { instance->voices[i].instance = instance; instance->voices[i].index = i; + instance->b0[i] = instance->voices[i].buf[0]; + instance->b1[i] = instance->voices[i].buf[1]; + instance->b2[i] = instance->voices[i].buf[2]; + instance->b3[i] = instance->voices[i].buf[3]; + instance->b4[i] = instance->voices[i].buf[4]; } return reinterpret_cast(instance); @@ -190,6 +201,9 @@ void impl_set_sample_rate(impl handle, float sample_rate) { } instance->syncCount = (size_t)bw_roundf(sample_rate * SYNC_RATE); + + instance->noiseKV[0] = 6.f * instance->noiseGen.getScalingK() * instance->pinkFilt.getScalingK(); + instance->noiseKV[1] = 0.1f * instance->noiseGen.getScalingK(); } void impl_reset(impl handle) { @@ -233,7 +247,6 @@ void impl_reset(impl handle) { instance->modWheel = 0.f; instance->syncLeft = instance->syncCount; instance->vco3WaveformCur = instance->vco3Waveform; - instance->noiseColorCur = instance->noiseColor; instance->vco1WaveformCur = instance->vco1Waveform; instance->vco2WaveformCur = instance->vco2Waveform; } @@ -450,7 +463,7 @@ void impl_process(impl handle, const float **inputs, float **outputs, size_t n_s const float df3 = instance->vco3Coarse + instance->pitchBend + (8.333333333333333e-2f * 0.01f) * instance->vco3Fine; for (int i = 0; i < N_VOICES; i++) { int n = instance->voices[i].note - 69; - int n3 = instance->vco3KbdCtrl ? instance->voices[i].note - 69 : -69; + int n3 = instance->vco3KbdCtrl ? n : -69; instance->voices[i].vco1PhaseGen.setFrequency(instance->masterTune * bw_pow2f(df1 + 8.333333333333333e-2f * n)); instance->voices[i].vco2PhaseGen.setFrequency(instance->masterTune * bw_pow2f(df2 + 8.333333333333333e-2f * n)); instance->voices[i].vco3PhaseGen.setFrequency(instance->masterTune * bw_pow2f(df3 + 8.333333333333333e-2f * n3)); @@ -468,12 +481,6 @@ void impl_process(impl handle, const float **inputs, float **outputs, size_t n_s instance->vco3WaveformCur = instance->vco3Waveform; } - if (instance->noiseColorCur != instance->noiseColor) { - if (instance->noiseColor == 2) - instance->pinkFilt.reset(); - instance->noiseColorCur = instance->noiseColor; - } - if (instance->vco1WaveformCur != instance->vco1Waveform) { switch (instance->vco1Waveform) { case 2: @@ -498,18 +505,24 @@ void impl_process(impl handle, const float **inputs, float **outputs, size_t n_s instance->vco2WaveformCur = instance->vco2Waveform; } - // synchronous control-rate and audio-rate operations + const float cutoffUnmapped = 0.1447648273010839f * bw_logf(0.05f * instance->vcfCutoff); + static const float cutoffKbdKV[4] = { + 0.f, // off + 0.629960524947437f * 8.333333333333333e-2f, // 1/3 + 0.793700525984100f * 8.333333333333333e-2f, // 2/3 + 8.333333333333333e-2f // full + }; + float cutoffKbdK[N_VOICES]; + for (int i = 0; i < N_VOICES; i++) + cutoffKbdK[i] = bw_pow2f(cutoffKbdKV[instance->vcfKbdCtrl - 1] * (instance->voices[i].note - 60)); - float *b0[N_VOICES], *b1[N_VOICES], *b2[N_VOICES], *b3[N_VOICES], *b4[N_VOICES]; - char g[N_VOICES]; - for (int j = 0; j < N_VOICES; j++) { - b0[j] = instance->voices[j].buf[0]; - b1[j] = instance->voices[j].buf[1]; - b2[j] = instance->voices[j].buf[2]; - b3[j] = instance->voices[j].buf[3]; - b4[j] = instance->voices[j].buf[4]; - g[j] = instance->voices[j].gate; - } + const float noiseK = instance->noiseKV[instance->noiseColor - 1]; + + char gates[N_VOICES]; + for (int j = 0; j < N_VOICES; j++) + gates[j] = instance->voices[j].gate; + + // synchronous control-rate and audio-rate operations for (size_t i = 0; i < n_samples; ) { float *out = outputs[0] + i; @@ -527,32 +540,33 @@ void impl_process(impl handle, const float **inputs, float **outputs, size_t n_s } switch (instance->vco3WaveformCur) { case 1: - instance->vco3OscSaw.process(b0, b1, b0, n); + instance->vco3OscSaw.process(instance->b0, instance->b1, instance->b0, n); break; case 2: - instance->vco3OscPulse.process(b0, b1, b0, n); + instance->vco3OscPulse.process(instance->b0, instance->b1, instance->b0, n); break; default: - instance->vco3OscTri.process(b0, b1, b0, n); + instance->vco3OscTri.process(instance->b0, instance->b1, instance->b0, n); break; } // noise generator - instance->noiseGen.process(b1, n); - if (instance->noiseColorCur == 2) - instance->pinkFilt.process(b1, b1, n); - bufScale(b1, 5.f, b1, n); + instance->noiseGen.process(instance->b1, n); + if (instance->noiseColor == 2) + instance->pinkFilt.process(instance->b1, instance->b1, n); + // no need to ever reset pink filt, as inputs are noise and filters are static + bufScale(instance->b1, 5.f, instance->b1, n); // modulation signals for (int j = 0; j < N_VOICES; j++) { for (int k = 0; k < n; k++) - b2[j][k] = instance->modWheel * (b0[j][k] + instance->modulationMix * (b1[j][k] - b0[j][k])); + instance->b2[j][k] = instance->modWheel * (instance->b0[j][k] + instance->modulationMix * (instance->b1[j][k] - instance->b0[j][k])); } if (sync) for (int j = 0; j < N_VOICES; j++) - instance->voices[j].modK = b2[j][0]; + instance->voices[j].modK = instance->b2[j][0]; // vco 1 @@ -565,13 +579,13 @@ void impl_process(impl handle, const float **inputs, float **outputs, size_t n_s } switch (instance->vco1WaveformCur) { case 1: - instance->vco1OscSaw.process(b3, b4, b3, n); + instance->vco1OscSaw.process(instance->b3, instance->b4, instance->b3, n); break; case 2: - instance->vco1OscPulse.process(b3, b4, b3, n); + instance->vco1OscPulse.process(instance->b3, instance->b4, instance->b3, n); break; default: - instance->vco1OscTri.process(b3, b4, b3, n); + instance->vco1OscTri.process(instance->b3, instance->b4, instance->b3, n); break; } @@ -585,68 +599,52 @@ void impl_process(impl handle, const float **inputs, float **outputs, size_t n_s } switch (instance->vco2WaveformCur) { case 1: - instance->vco2OscSaw.process(b2, b4, b2, n); + instance->vco2OscSaw.process(instance->b2, instance->b4, instance->b2, n); break; case 2: - instance->vco2OscPulse.process(b2, b4, b2, n); + instance->vco2OscPulse.process(instance->b2, instance->b4, instance->b2, n); break; default: - instance->vco2OscTri.process(b2, b4, b2, n); + instance->vco2OscTri.process(instance->b2, instance->b4, instance->b2, n); break; } // mixer - instance->vco1Gain.process(b3, b3, n); - instance->vco2Gain.process(b2, b2, n); - instance->vco3Gain.process(b0, b0, n); - instance->noiseGain.process(b1, b1, n); - bufMix(b0, b2, b0, n); - bufMix(b0, b3, b0, n); + instance->vco1Gain.process(instance->b3, instance->b3, n); + instance->vco2Gain.process(instance->b2, instance->b2, n); + instance->vco3Gain.process(instance->b0, instance->b0, n); + instance->noiseGain.process(instance->b1, instance->b1, n); + bufMix(instance->b0, instance->b2, instance->b0, n); + bufMix(instance->b0, instance->b3, instance->b0, n); - instance->oscFilt.process(b0, b0, n); + instance->oscFilt.process(instance->b0, instance->b0, n); - const float k = instance->noiseColorCur == 2 - ? 6.f * instance->noiseGen.getScalingK() * instance->pinkFilt.getScalingK() - : 0.1f * instance->noiseGen.getScalingK(); - bufScale(b1, k, b1, n); - bufMix(b0, b1, b0, n); + bufScale(instance->b1, noiseK, instance->b1, n); + bufMix(instance->b0, instance->b1, instance->b0, n); // vcf - instance->vcfEnvGen.process(g, nullptr, n); + instance->vcfEnvGen.process(gates, nullptr, n); if (sync) - for (int j = 0; j < N_VOICES; j++) + for (int j = 0; j < N_VOICES; j++) { instance->voices[j].vcfEnvK = instance->vcfEnvGen.getYZ1(j); - const float cutoffUnmapped = 0.1447648273010839f * bw_logf(0.05f * instance->vcfCutoff); - for (int j = 0; j < N_VOICES; j++) { - const float cutoffVpos = - cutoffUnmapped - + instance->vcfContour * instance->voices[j].vcfEnvK - + 0.3f * instance->vcfModulation * instance->voices[j].modK; - float cutoff = 20.f * bw_expf(6.907755278982137 * cutoffVpos); - switch (instance->vcfKbdCtrl) { - case 2: // 1/3 - cutoff *= bw_pow2f((0.629960524947437f * 8.333333333333333e-2f) * (instance->voices[j].note - 60)); - break; - case 3: // 2/3 - cutoff *= bw_pow2f((0.793700525984100f * 8.333333333333333e-2f) * (instance->voices[j].note - 60)); - break; - case 4: // full - cutoff *= bw_pow2f(8.333333333333333e-2f * (instance->voices[j].note - 60)); - break; - default: // off, do nothing - break; + const float cutoffVpos = + cutoffUnmapped + + instance->vcfContour * instance->voices[j].vcfEnvK + + 0.3f * instance->vcfModulation * instance->voices[j].modK; + float cutoff = cutoffKbdK[j] * 20.f * bw_expf(6.907755278982137 * cutoffVpos); + instance->voices[j].vcf.setCutoff(bw_clipf(cutoff, 20.f, 20e3f)); } - instance->voices[j].vcf.setCutoff(bw_clipf(cutoff, 20.f, 20e3f)); + for (int j = 0; j < N_VOICES; j++) { float *vb0[1] = {instance->voices[j].buf[0]}; instance->voices[j].vcf.process(vb0, vb0, nullptr, nullptr, n); } // vca - instance->vcaEnvGen.process(g, b1, n); - bufMul(b0, b1, b0, n); + instance->vcaEnvGen.process(gates, instance->b1, n); + bufMul(instance->b0, instance->b1, instance->b0, n); // mix voices