diff --git a/examples/synth_poly/old/bw_example_synth_poly.c b/examples/synth_poly/old/bw_example_synth_poly.c
deleted file mode 100644
index 239dbc3..0000000
--- a/examples/synth_poly/old/bw_example_synth_poly.c
+++ /dev/null
@@ -1,428 +0,0 @@
-/*
- * Brickworks
- *
- * Copyright (C) 2023 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
- * along with Brickworks. If not, see .
- *
- * File author: Stefano D'Angelo
- */
-
-#include "bw_example_synth_poly.h"
-
-#include
-#include
-#include
-
-void bw_example_synth_poly_init(bw_example_synth_poly *instance) {
- bw_osc_saw_init(&instance->vco_saw_coeffs);
- bw_osc_pulse_init(&instance->vco1_pulse_coeffs);
- bw_osc_tri_init(&instance->vco1_tri_coeffs);
- bw_gain_init(&instance->vco1_gain_coeffs);
- bw_osc_pulse_init(&instance->vco2_pulse_coeffs);
- bw_osc_tri_init(&instance->vco2_tri_coeffs);
- bw_gain_init(&instance->vco2_gain_coeffs);
- bw_osc_pulse_init(&instance->vco3_pulse_coeffs);
- bw_osc_tri_init(&instance->vco3_tri_coeffs);
- bw_gain_init(&instance->vco3_gain_coeffs);
- bw_noise_gen_init(&instance->noise_gen_coeffs, &instance->rand_state);
- bw_pink_filt_init(&instance->pink_filt_coeffs);
- bw_gain_init(&instance->noise_gain_coeffs);
- bw_env_gen_init(&instance->vcf_env_gen_coeffs);
- bw_env_gen_init(&instance->vca_env_gen_coeffs);
- bw_phase_gen_init(&instance->a440_phase_gen_coeffs);
- bw_gain_init(&instance->gain_coeffs);
- bw_ppm_init(&instance->ppm_coeffs);
-
- for (int i = 0; i < N_VOICES; i++) {
- bw_phase_gen_init(&instance->voices[i].vco1_phase_gen_coeffs);
- bw_phase_gen_init(&instance->voices[i].vco2_phase_gen_coeffs);
- bw_phase_gen_init(&instance->voices[i].vco3_phase_gen_coeffs);
- bw_svf_init(&instance->voices[i].vcf_coeffs);
- }
-
- 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_osc_pulse_set_antialiasing(&instance->vco3_pulse_coeffs, 1);
- bw_osc_tri_set_antialiasing(&instance->vco3_tri_coeffs, 1);
- bw_phase_gen_set_frequency(&instance->a440_phase_gen_coeffs, 440.f);
-
- instance->rand_state = 0xbaddecaf600dfeed;
-}
-
-void bw_example_synth_poly_set_sample_rate(bw_example_synth_poly *instance, float sample_rate) {
- bw_osc_saw_set_sample_rate(&instance->vco_saw_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_gain_set_sample_rate(&instance->vco1_gain_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_gain_set_sample_rate(&instance->vco2_gain_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_gain_set_sample_rate(&instance->vco3_gain_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_gain_set_sample_rate(&instance->noise_gain_coeffs, sample_rate);
- bw_env_gen_set_sample_rate(&instance->vcf_env_gen_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_gain_set_sample_rate(&instance->gain_coeffs, sample_rate);
- bw_ppm_set_sample_rate(&instance->ppm_coeffs, sample_rate);
-
- for (int i = 0; i < N_VOICES; i++) {
- bw_phase_gen_set_sample_rate(&instance->voices[i].vco1_phase_gen_coeffs, sample_rate);
- bw_phase_gen_set_sample_rate(&instance->voices[i].vco2_phase_gen_coeffs, sample_rate);
- bw_phase_gen_set_sample_rate(&instance->voices[i].vco3_phase_gen_coeffs, sample_rate);
- bw_svf_set_sample_rate(&instance->voices[i].vcf_coeffs, sample_rate);
- }
-}
-
-void bw_example_synth_poly_reset(bw_example_synth_poly *instance) {
- const float v = instance->params[p_vcf_cutoff];
- const float cutoff = 20.f + (20e3f - 20.f) * v * v * v;
- for (int i = 0; i < N_VOICES; i++)
- bw_svf_set_cutoff(&instance->voices[i].vcf_coeffs, bw_clipf(cutoff, 20.f, 20e3f));
-
- bw_note_queue_reset(&instance->note_queue);
- bw_osc_saw_reset_coeffs(&instance->vco_saw_coeffs);
- bw_osc_pulse_reset_coeffs(&instance->vco1_pulse_coeffs);
- bw_osc_tri_reset_coeffs(&instance->vco1_tri_coeffs);
- bw_gain_reset_coeffs(&instance->vco1_gain_coeffs);
- bw_osc_pulse_reset_coeffs(&instance->vco2_pulse_coeffs);
- bw_osc_tri_reset_coeffs(&instance->vco2_tri_coeffs);
- bw_gain_reset_coeffs(&instance->vco2_gain_coeffs);
- bw_osc_pulse_reset_coeffs(&instance->vco3_pulse_coeffs);
- bw_osc_tri_reset_coeffs(&instance->vco3_tri_coeffs);
- bw_gain_reset_coeffs(&instance->vco3_gain_coeffs);
- bw_noise_gen_reset_coeffs(&instance->noise_gen_coeffs);
- bw_pink_filt_reset_coeffs(&instance->pink_filt_coeffs);
- bw_gain_reset_coeffs(&instance->noise_gain_coeffs);
- bw_env_gen_reset_coeffs(&instance->vcf_env_gen_coeffs);
- bw_env_gen_reset_coeffs(&instance->vca_env_gen_coeffs);
- bw_phase_gen_reset_coeffs(&instance->a440_phase_gen_coeffs);
- float p, pi;
- bw_phase_gen_reset_state(&instance->a440_phase_gen_coeffs, &instance->a440_phase_gen_state, 0.f, &p, &pi);
- bw_gain_reset_coeffs(&instance->gain_coeffs);
- bw_ppm_reset_coeffs(&instance->ppm_coeffs);
- bw_ppm_reset_state(&instance->ppm_coeffs, &instance->ppm_state, 0.f);
-
- for (int i = 0; i < N_VOICES; i++) {
- bw_phase_gen_reset_coeffs(&instance->voices[i].vco1_phase_gen_coeffs);
- bw_phase_gen_reset_coeffs(&instance->voices[i].vco2_phase_gen_coeffs);
- bw_phase_gen_reset_coeffs(&instance->voices[i].vco3_phase_gen_coeffs);
- bw_svf_reset_coeffs(&instance->voices[i].vcf_coeffs);
-
- bw_phase_gen_reset_state(&instance->voices[i].vco1_phase_gen_coeffs, &instance->voices[i].vco1_phase_gen_state, 0.f, &p, &pi);
- bw_phase_gen_reset_state(&instance->voices[i].vco2_phase_gen_coeffs, &instance->voices[i].vco2_phase_gen_state, 0.f, &p, &pi);
- bw_phase_gen_reset_state(&instance->voices[i].vco3_phase_gen_coeffs, &instance->voices[i].vco3_phase_gen_state, 0.f, &p, &pi);
- bw_osc_filt_reset_state(&instance->voices[i].osc_filt_state, 0.f);
- bw_pink_filt_reset_state(&instance->pink_filt_coeffs, &instance->voices[i].pink_filt_state, 0.f);
- float lp, bp, hp;
- bw_svf_reset_state(&instance->voices[i].vcf_coeffs, &instance->voices[i].vcf_state, 0.f, &lp, &bp, &hp);
- bw_env_gen_reset_state(&instance->vcf_env_gen_coeffs, &instance->voices[i].vcf_env_gen_state, 0.f);
- bw_env_gen_reset_state(&instance->vca_env_gen_coeffs, &instance->voices[i].vca_env_gen_state, 0.f);
-
- instance->voices[i].note = 69;
- instance->voices[i].gate = 0;
- }
-
- instance->pitch_bend = 0.f;
- instance->mod_wheel = 0.f;
-}
-
-static void note_on(void *BW_RESTRICT voice, unsigned char note, float velocity) {
- (void)velocity;
- bw_example_synth_poly_voice *v = (bw_example_synth_poly_voice *)voice;
- v->note = note;
- v->gate = 1;
-}
-
-static void note_off(void *BW_RESTRICT voice, float velocity) {
- (void)velocity;
- bw_example_synth_poly_voice *v = (bw_example_synth_poly_voice *)voice;
- v->gate = 0;
-}
-
-static unsigned char get_note(const void *BW_RESTRICT voice) {
- bw_example_synth_poly_voice *v = (bw_example_synth_poly_voice *)voice;
- return v->note;
-}
-
-static char is_free(const void *BW_RESTRICT voice) {
- bw_example_synth_poly_voice *v = (bw_example_synth_poly_voice *)voice;
- bw_env_gen_phase phase = bw_env_gen_get_phase(&v->vca_env_gen_state);
- return !v->gate && phase == bw_env_gen_phase_off;
-}
-
-void bw_example_synth_poly_process(bw_example_synth_poly *instance, const float** x, float** y, int n_samples) {
- // FIXME: control-rate modulations are asynchronous here...
- // it's all good as long as hosts gives us buffers whose length is a multiple of 32,
- // otherwise it's probably still ok but a bit "swingy"
-
- (void)x;
-
- static bw_voice_alloc_opts alloc_opts = { bw_voice_alloc_priority_low, note_on, note_off, get_note, is_free };
- void *voices[N_VOICES];
- for (int i = 0; i < N_VOICES; i++)
- voices[i] = (void *)(instance->voices + i);
- bw_voice_alloc(&alloc_opts, &instance->note_queue, voices, N_VOICES);
- bw_note_queue_clear(&instance->note_queue);
-
- const float df1 =
- 6.f * instance->params[p_vco1_coarse] - 3.f
- + 2.f * instance->pitch_bend - 1.f
- + 8.333333333333333e-2f * (2.f * (instance->params[p_master_tune] + instance->params[p_vco1_fine]) - 71.f);
- const float df2 =
- 6.f * instance->params[p_vco2_coarse] - 3.f
- + 2.f * instance->pitch_bend - 1.f
- + 8.333333333333333e-2f * (2.f * (instance->params[p_master_tune] + instance->params[p_vco2_fine]) - 71.f);
- const float df3 =
- 6.f * instance->params[p_vco3_coarse] - 3.f
- + 2.f * instance->pitch_bend - 1.f
- + 8.333333333333333e-2f * (2.f * (instance->params[p_master_tune] + instance->params[p_vco3_fine]) - 71.f);
- for (int i = 0; i < N_VOICES; i++) {
- int n3 = instance->params[p_vco3_kbd] >= 0.5f ? instance->voices[i].note : 0;
- bw_phase_gen_set_frequency(&instance->voices[i].vco1_phase_gen_coeffs, 440.f * bw_pow2f(df1 + 8.333333333333333e-2f * instance->voices[i].note));
- bw_phase_gen_set_frequency(&instance->voices[i].vco2_phase_gen_coeffs, 440.f * bw_pow2f(df2 + 8.333333333333333e-2f * instance->voices[i].note));
- bw_phase_gen_set_frequency(&instance->voices[i].vco3_phase_gen_coeffs, 440.f * bw_pow2f(df3 + 8.333333333333333e-2f * n3));
- }
-
- const float vcf_mod_k = 0.3f * instance->params[p_vcf_mod];
-
- 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_samples; i += BUFFER_SIZE) {
- float *out = y[0] + i;
- int n = bw_minf(n_samples - i, BUFFER_SIZE);
-
- 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);
- if (instance->params[p_vco3_waveform] >= (1.f / 4.f + 1.f / 2.f)) {
- bw_osc_tri_process_multi(&instance->vco3_tri_coeffs, (const float **)b0, (const float **)b1, b0, N_VOICES, 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_multi(&instance->vco3_pulse_coeffs, (const float **)b0, (const float **)b1, b0, N_VOICES, n);
- bw_osc_tri_reset_coeffs(&instance->vco3_tri_coeffs);
- } else {
- bw_osc_saw_process_multi(&instance->vco_saw_coeffs, (const float **)b0, (const float **)b1, b0, N_VOICES, n);
- bw_osc_pulse_reset_coeffs(&instance->vco3_pulse_coeffs);
- bw_osc_tri_reset_coeffs(&instance->vco3_tri_coeffs);
- }
-
- bw_noise_gen_process_multi(&instance->noise_gen_coeffs, b1, N_VOICES, n);
- if (instance->params[p_noise_color] >= 0.5f)
- bw_pink_filt_process_multi(&instance->pink_filt_coeffs, pink_filt_states, (const float **)b1, b1, N_VOICES, n);
- else
- for (int j = 0; j < N_VOICES; j++)
- bw_pink_filt_reset_state(&instance->pink_filt_coeffs, pink_filt_states[j], 0.f); // FIXME: calling this here is sloppy coding
- bw_buf_scale_multi((const float * const *)b1, 5.f, b1, N_VOICES, n);
-
- float vcf_mod[N_VOICES];
- 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->params[p_mod_mix] * (b1[j][k] - b0[j][k]));
- vcf_mod[j] = vcf_mod_k * b2[j][0];
- }
-
- for (int j = 0; j < N_VOICES; j++) {
- bw_buf_scale(b2[j], instance->params[p_vco1_mod], 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);
- }
- if (instance->params[p_vco1_waveform] >= (1.f / 4.f + 1.f / 2.f)) {
- bw_osc_tri_process_multi(&instance->vco1_tri_coeffs, (const float **)b3, (const float **)b4, b3, N_VOICES, n);
- bw_osc_pulse_reset_coeffs(&instance->vco1_pulse_coeffs);
- } else if (instance->params[p_vco1_waveform] >= (1.f / 4.f)) {
- bw_osc_pulse_process_multi(&instance->vco1_pulse_coeffs, (const float **)b3, (const float **)b4, b3, N_VOICES, n);
- bw_osc_tri_reset_coeffs(&instance->vco1_tri_coeffs);
- } else {
- bw_osc_saw_process_multi(&instance->vco_saw_coeffs, (const float **)b3, (const float **)b4, b3, N_VOICES, n);
- bw_osc_pulse_reset_coeffs(&instance->vco1_pulse_coeffs);
- bw_osc_tri_reset_coeffs(&instance->vco1_tri_coeffs);
- }
-
- for (int j = 0; j < N_VOICES; j++) {
- bw_buf_scale(b2[j], instance->params[p_vco2_mod], 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);
- }
- if (instance->params[p_vco2_waveform] >= (1.f / 4.f + 1.f / 2.f)) {
- bw_osc_tri_process_multi(&instance->vco2_tri_coeffs, (const float **)b2, (const float **)b4, b2, N_VOICES, n);
- bw_osc_pulse_reset_coeffs(&instance->vco2_pulse_coeffs);
- } else if (instance->params[p_vco2_waveform] >= (1.f / 4.f)) {
- bw_osc_pulse_process_multi(&instance->vco2_pulse_coeffs, (const float **)b2, (const float **)b4, b2, N_VOICES, n);
- bw_osc_tri_reset_coeffs(&instance->vco2_tri_coeffs);
- } else {
- bw_osc_saw_process_multi(&instance->vco_saw_coeffs, (const float **)b2, (const float **)b4, b2, N_VOICES, n);
- bw_osc_pulse_reset_coeffs(&instance->vco2_pulse_coeffs);
- bw_osc_tri_reset_coeffs(&instance->vco2_tri_coeffs);
- }
-
- 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_osc_filt_process_multi(osc_filt_states, (const float **)b0, b0, N_VOICES, n);
-
- const float k = instance->params[p_noise_color] >= 0.5f
- ? 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_env_gen_process_multi(&instance->vcf_env_gen_coeffs, vcf_env_gen_states, gates, NULL, N_VOICES, n);
- for (int j = 0; j < N_VOICES; j++) {
- float v = instance->params[p_vcf_cutoff] + instance->params[p_vcf_contour] * bw_env_gen_get_y_z1(vcf_env_gen_states[j]) + vcf_mod[j];
- 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(8.333333333333333e-2f * (instance->voices[j].note - 60));
- else if (instance->params[p_vcf_kbd_ctrl] >= (1.f / 6.f + 1.f / 3.f))
- cutoff *= bw_pow2f((0.793700525984100f * 8.333333333333333e-2f) * (instance->voices[j].note - 60));
- else if (instance->params[p_vcf_kbd_ctrl] >= (1.f / 6.f + 2.f / 3.f))
- cutoff *= bw_pow2f((0.629960524947437f * 8.333333333333333e-2f) * (instance->voices[j].note - 60));
- // otherwise no kbd control
- 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);
- }
-
- 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_buf_fill(0.f, out, n);
- for (int j = 0; j < N_VOICES; j++)
- bw_buf_mix(out, b0[j], out, n);
-
- 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);
- if (instance->params[p_a440] >= 0.5f)
- bw_buf_mix(out, instance->buf, out, n);
-
- bw_gain_process(&instance->gain_coeffs, out, out, n);
- bw_ppm_process(&instance->ppm_coeffs, &instance->ppm_state, out, NULL, n);
- }
-}
-
-void bw_example_synth_poly_set_parameter(bw_example_synth_poly *instance, int index, float value) {
- instance->params[index] = value;
- switch (index) {
- case p_volume:
- bw_gain_set_gain_lin(&instance->gain_coeffs, value * value * value);
- break;
- case p_portamento:
- for (int i = 0; i < N_VOICES; i++) {
- bw_phase_gen_set_portamento_tau(&instance->voices[i].vco1_phase_gen_coeffs, value);
- bw_phase_gen_set_portamento_tau(&instance->voices[i].vco2_phase_gen_coeffs, value);
- bw_phase_gen_set_portamento_tau(&instance->voices[i].vco3_phase_gen_coeffs, value);
- }
- break;
- 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_vco1_level:
- bw_gain_set_gain_lin(&instance->vco1_gain_coeffs, value * value * 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_level:
- bw_gain_set_gain_lin(&instance->vco2_gain_coeffs, value * value * 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_level:
- bw_gain_set_gain_lin(&instance->vco3_gain_coeffs, value * value * value);
- break;
- case p_noise_level:
- bw_gain_set_gain_lin(&instance->noise_gain_coeffs, value * value * value);
- break;
- case p_vcf_Q:
- {
- const float v = 0.5f + 9.5f * value;
- for (int i = 0; i < N_VOICES; i++)
- bw_svf_set_Q(&instance->voices[i].vcf_coeffs, v);
- }
- break;
- case p_vcf_attack:
- bw_env_gen_set_attack(&instance->vcf_env_gen_coeffs, value);
- break;
- case p_vcf_decay:
- bw_env_gen_set_decay(&instance->vcf_env_gen_coeffs, value);
- break;
- case p_vcf_sustain:
- bw_env_gen_set_sustain(&instance->vcf_env_gen_coeffs, value);
- break;
- case p_vcf_release:
- bw_env_gen_set_release(&instance->vcf_env_gen_coeffs, value);
- break;
- case p_vca_attack:
- bw_env_gen_set_attack(&instance->vca_env_gen_coeffs, bw_maxf(0.002f, value));
- break;
- case p_vca_decay:
- bw_env_gen_set_decay(&instance->vca_env_gen_coeffs, value);
- break;
- case p_vca_sustain:
- bw_env_gen_set_sustain(&instance->vca_env_gen_coeffs, value);
- break;
- case p_vca_release:
- bw_env_gen_set_release(&instance->vca_env_gen_coeffs, bw_maxf(0.002f, value));
- break;
- }
-}
-
-float bw_example_synth_poly_get_parameter(bw_example_synth_poly *instance, int index) {
- if (index < p_n)
- return instance->params[index];
- const float v = bw_ppm_get_y_z1(&instance->ppm_state);
- return v < -200.f ? 0.f : bw_clipf(0.01666666666666666f * v + 1.f, 0.f, 1.f);
-}
-
-void bw_example_synth_poly_note_on(bw_example_synth_poly *instance, char note, char velocity) {
- bw_note_queue_add(&instance->note_queue, note, velocity != 0, (1.f / 127.f) * velocity, 0);
-}
-
-void bw_example_synth_poly_note_off(bw_example_synth_poly *instance, char note) {
- bw_note_queue_add(&instance->note_queue, note, 0, 0, 0);
-}
-
-void bw_example_synth_poly_pitch_bend(bw_example_synth_poly *instance, int value) {
- instance->pitch_bend = (value - 0x2000) / (float)0x4000;
-}
-
-void bw_example_synth_poly_mod_wheel(bw_example_synth_poly *instance, char value) {
- instance->mod_wheel = (float)value / 0x80;
-}
diff --git a/examples/synth_poly/old/bw_example_synth_poly.h b/examples/synth_poly/old/bw_example_synth_poly.h
deleted file mode 100644
index b6bda39..0000000
--- a/examples/synth_poly/old/bw_example_synth_poly.h
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * Brickworks
- *
- * Copyright (C) 2023 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
- * along with Brickworks. If not, see .
- *
- * File author: Stefano D'Angelo
- */
-
-#ifndef _BW_EXAMPLE_SYNTH_POLY_H
-#define _BW_EXAMPLE_SYNTH_POLY_H
-
-#include "platform.h"
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-enum {
- p_volume,
- p_master_tune,
- p_portamento,
- p_mod_mix,
- p_vco1_mod,
- p_vco1_coarse,
- p_vco1_fine,
- p_vco1_waveform,
- p_vco1_pw_slope,
- p_vco1_level,
- p_vco2_mod,
- p_vco2_coarse,
- p_vco2_fine,
- p_vco2_waveform,
- p_vco2_pw_slope,
- p_vco2_level,
- p_vco3_kbd,
- p_vco3_coarse,
- p_vco3_fine,
- p_vco3_waveform,
- p_vco3_pw_slope,
- p_vco3_level,
- p_noise_color,
- p_noise_level,
- 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_vca_attack,
- p_vca_decay,
- p_vca_sustain,
- p_vca_release,
- p_a440,
- p_n
-};
-
-#define BUFFER_SIZE 32
-#define N_VOICES 8
-
-struct _bw_example_synth_poly_voice {
- bw_phase_gen_coeffs vco1_phase_gen_coeffs;
- bw_phase_gen_coeffs vco2_phase_gen_coeffs;
- bw_phase_gen_coeffs vco3_phase_gen_coeffs;
- bw_svf_coeffs vcf_coeffs;
-
- bw_phase_gen_state vco1_phase_gen_state;
- bw_phase_gen_state vco2_phase_gen_state;
- bw_phase_gen_state vco3_phase_gen_state;
- bw_osc_filt_state osc_filt_state;
- bw_pink_filt_state pink_filt_state;
- bw_env_gen_state vcf_env_gen_state;
- bw_svf_state vcf_state;
- bw_env_gen_state vca_env_gen_state;
-
- unsigned char note;
- char gate;
-
- float buf[5][BUFFER_SIZE];
-};
-typedef struct _bw_example_synth_poly_voice bw_example_synth_poly_voice;
-
-struct _bw_example_synth_poly {
- // Sub-components
- bw_note_queue note_queue;
- bw_osc_saw_coeffs vco_saw_coeffs;
- bw_osc_pulse_coeffs vco1_pulse_coeffs;
- bw_osc_tri_coeffs vco1_tri_coeffs;
- bw_gain_coeffs vco1_gain_coeffs;
- bw_osc_pulse_coeffs vco2_pulse_coeffs;
- bw_osc_tri_coeffs vco2_tri_coeffs;
- bw_gain_coeffs vco2_gain_coeffs;
- bw_osc_pulse_coeffs vco3_pulse_coeffs;
- bw_osc_tri_coeffs vco3_tri_coeffs;
- bw_gain_coeffs vco3_gain_coeffs;
- bw_noise_gen_coeffs noise_gen_coeffs;
- bw_pink_filt_coeffs pink_filt_coeffs;
- bw_gain_coeffs noise_gain_coeffs;
- bw_env_gen_coeffs vcf_env_gen_coeffs;
- bw_env_gen_coeffs vca_env_gen_coeffs;
- bw_phase_gen_coeffs a440_phase_gen_coeffs;
- bw_phase_gen_state a440_phase_gen_state;
- bw_gain_coeffs gain_coeffs;
- bw_ppm_coeffs ppm_coeffs;
- bw_ppm_state ppm_state;
-
- bw_example_synth_poly_voice voices[N_VOICES];
-
- // Parameters
- float params[p_n];
-
- // States
- uint64_t rand_state;
- float pitch_bend;
- float mod_wheel;
-
- // Buffers
- float buf[BUFFER_SIZE];
-};
-typedef struct _bw_example_synth_poly bw_example_synth_poly;
-
-void bw_example_synth_poly_init(bw_example_synth_poly *instance);
-void bw_example_synth_poly_set_sample_rate(bw_example_synth_poly *instance, float sample_rate);
-void bw_example_synth_poly_reset(bw_example_synth_poly *instance);
-void bw_example_synth_poly_process(bw_example_synth_poly *instance, const float** x, float** y, int n_samples);
-void bw_example_synth_poly_set_parameter(bw_example_synth_poly *instance, int index, float value);
-float bw_example_synth_poly_get_parameter(bw_example_synth_poly *instance, int index);
-void bw_example_synth_poly_note_on(bw_example_synth_poly *instance, char note, char velocity);
-void bw_example_synth_poly_note_off(bw_example_synth_poly *instance, char note);
-void bw_example_synth_poly_pitch_bend(bw_example_synth_poly *instance, int value);
-void bw_example_synth_poly_mod_wheel(bw_example_synth_poly *instance, char value);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/examples/synth_poly/old/config.h b/examples/synth_poly/old/config.h
deleted file mode 100644
index 880fb96..0000000
--- a/examples/synth_poly/old/config.h
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Brickworks
- *
- * Copyright (C) 2023 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
- * along with Brickworks. If not, see .
- *
- * File authors: Stefano D'Angelo
- */
-
-#ifndef _CONFIG_H
-#define _CONFIG_H
-
-// Definitions
-
-#define IO_MONO 1
-#define IO_STEREO (1<<1)
-
-struct config_io_bus {
- const char *name;
- char out;
- char aux;
- char cv;
- char configs;
-};
-
-struct config_parameter {
- const char *name;
- const char *shortName;
- const char *units;
- char out;
- char bypass;
- int steps;
- float defaultValueUnmapped;
-};
-
-// Data
-
-#define COMPANY_NAME "Orastron"
-#define COMPANY_WEBSITE "https://www.orastron.com/"
-#define COMPANY_MAILTO "mailto:info@orastron.com"
-
-#define PLUGIN_NAME "bw_example_synth_poly"
-#define PLUGIN_VERSION "1.0.0"
-
-#define NUM_BUSES_IN 0
-#define NUM_BUSES_OUT 1
-#define NUM_CHANNELS_IN 0
-#define NUM_CHANNELS_OUT 1
-
-static struct config_io_bus config_buses_out[NUM_BUSES_OUT] = {
- { "Audio out", 1, 0, 0, IO_MONO }
-};
-
-#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 },
- { "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 level", "VCO1 level", "%", 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 level", "VCO2 level", "%", 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 level", "VCO3 level", "%", 0, 0, 0, 0.f },
- { "Noise color", "Noise color", "", 0, 0, 1, 0.f },
- { "Noise level", "Noise level", "%", 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 }
-};
-
-// Internal API
-
-#include "bw_example_synth_poly.h"
-
-#define P_TYPE bw_example_synth_poly
-#define P_INIT bw_example_synth_poly_init
-#define P_SET_SAMPLE_RATE bw_example_synth_poly_set_sample_rate
-#define P_RESET bw_example_synth_poly_reset
-#define P_PROCESS bw_example_synth_poly_process
-#define P_SET_PARAMETER bw_example_synth_poly_set_parameter
-#define P_GET_PARAMETER bw_example_synth_poly_get_parameter
-#define P_NOTE_ON bw_example_synth_poly_note_on
-#define P_NOTE_OFF bw_example_synth_poly_note_off
-#define P_PITCH_BEND bw_example_synth_poly_pitch_bend
-#define P_MOD_WHEEL bw_example_synth_poly_mod_wheel
-
-#endif
diff --git a/examples/synth_poly/old/config.js b/examples/synth_poly/old/config.js
deleted file mode 100644
index cf29faa..0000000
--- a/examples/synth_poly/old/config.js
+++ /dev/null
@@ -1,234 +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
- * along with Brickworks. If not, see .
- *
- * File author: Stefano D'Angelo
- */
-
-var buses = [
- {
- stereo: false,
- output: true
- }
-];
-
-var parameters = [
- {
- name: "Volume",
- output: false,
- defaultValue: 0.5
- },
- {
- name: "Master tune",
- output: false,
- defaultValue: 0.5
- },
- {
- name: "Portamento",
- output: false,
- defaultValue: 0.0
- },
- {
- 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: "VCO1 waveform",
- output: false,
- defaultValue: 0.5,
- step: 2
- },
- {
- name: "VCO1 pulse width/slope",
- output: false,
- defaultValue: 0.5
- },
- {
- name: "VCO1 level",
- 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 level",
- 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 level",
- output: false,
- defaultValue: 0.0
- },
- {
- name: "Noise color",
- output: false,
- defaultValue: 0.0,
- step: 1
- },
- {
- name: "Noise level",
- 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: "VCF cutoff",
- output: false,
- defaultValue: 1.0
- },
- {
- name: "VCF Q",
- output: false,
- defaultValue: 0.0
- },
- {
- name: "VCF contour",
- output: false,
- defaultValue: 0.0
- },
- {
- name: "VCF attack",
- output: false,
- defaultValue: 0.0
- },
- {
- name: "VCF decay",
- output: false,
- defaultValue: 0.0
- },
- {
- name: "VCF sustain",
- output: false,
- defaultValue: 1.0
- },
- {
- name: "VCF release",
- output: false,
- defaultValue: 0.0
- },
- {
- name: "VCA attack",
- output: false,
- defaultValue: 0.0
- },
- {
- name: "VCA decay",
- output: false,
- defaultValue: 0.0
- },
- {
- name: "VCA sustain",
- output: false,
- defaultValue: 1.0
- },
- {
- name: "VCA release",
- output: false,
- defaultValue: 0.0
- },
- {
- name: "A440",
- output: false,
- defaultValue: 0.0,
- step: 1
- },
- {
- name: "Level",
- output: true,
- defaultValue: 0.0
- }
-];
diff --git a/examples/synthpp_poly/src/android.json b/examples/synthpp_poly/src/android.json
new file mode 100644
index 0000000..18e908b
--- /dev/null
+++ b/examples/synthpp_poly/src/android.json
@@ -0,0 +1,5 @@
+{
+ "android": {
+ "javaPackageName": "com.orastron.bw_example_synthpp_poly"
+ }
+}
diff --git a/examples/synthpp_poly/src/cmd.json b/examples/synthpp_poly/src/cmd.json
new file mode 100644
index 0000000..50893aa
--- /dev/null
+++ b/examples/synthpp_poly/src/cmd.json
@@ -0,0 +1,16 @@
+{
+ "cmd": {
+ "busIds": [ "midi_in", "output" ],
+ "parameterIds": [
+ "volume", "master_tune", "portamento", "mod_mix",
+ "vco1_mod", "vco1_coarse", "vco1_fine", "vco1_wave", "vco1_pw", "vco1_level",
+ "vco2_mod", "vco2_coarse", "vco2_fine", "vco2_wave", "vco2_pw", "vco2_level",
+ "vco3_kbd_ctrl", "vco3_coarse", "vco3_fine", "vco3_wave", "vco3_pw", "vco3_level",
+ "noise_color", "noise_level",
+ "vcf_mod", "vcf_kbd_ctrl", "vcf_cutoff", "vcf_resonance", "vcf_contour",
+ "vcf_attack", "vcf_decay", "vcf_sustain", "vcf_release",
+ "vca_attack", "vca_decay", "vca_sustain", "vca_release",
+ "a440", "level"
+ ]
+ }
+}
diff --git a/examples/synthpp_poly/src/daisy-seed.json b/examples/synthpp_poly/src/daisy-seed.json
new file mode 100644
index 0000000..2fee4f9
--- /dev/null
+++ b/examples/synthpp_poly/src/daisy-seed.json
@@ -0,0 +1,26 @@
+{
+ "daisy_seed": {
+ "parameterPins": [
+ -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+ -1, -1,
+ -1, -1, -1, -1, -1,
+ -1, -1, -1, -1,
+ -1, -1, -1, -1,
+ -1, 22
+ ],
+ "midiCCMaps": [
+ 7, 3, 5, 9,
+ 14, 15, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29,
+ 30, 31, 85, 86, 87, 89,
+ 90, 102,
+ 103, 104, 74, 71, 105,
+ 106, 107, 108, 109,
+ 73, 110, 111, 72,
+ 112, -1
+ ]
+ }
+}
diff --git a/examples/synthpp_poly/src/impl.cpp b/examples/synthpp_poly/src/impl.cpp
new file mode 100644
index 0000000..54c9b1b
--- /dev/null
+++ b/examples/synthpp_poly/src/impl.cpp
@@ -0,0 +1,684 @@
+#include "impl.h"
+
+#include "common.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define BUFFER_SIZE 128
+#define SYNC_RATE 1e-3f // synchronous control rate, seconds
+#define N_VOICES 8
+
+using namespace Brickworks;
+
+class Engine;
+
+class Voice {
+public:
+ PhaseGen<1> vco1PhaseGen;
+ PhaseGen<1> vco2PhaseGen;
+ PhaseGen<1> vco3PhaseGen;
+ SVF<1> vcf;
+
+ unsigned char note;
+ char gate;
+ float modK;
+ float vcfEnvK;
+
+ float buf[5][BUFFER_SIZE];
+
+ Engine * instance;
+ int index;
+};
+
+class Engine {
+public:
+ Engine() : noiseGen(&randState) {}
+
+ NoteQueue noteQueue;
+ OscSaw vco1OscSaw;
+ OscPulse vco1OscPulse;
+ OscTri vco1OscTri;
+ Gain vco1Gain;
+ OscSaw vco2OscSaw;
+ OscPulse vco2OscPulse;
+ OscTri vco2OscTri;
+ Gain vco2Gain;
+ OscSaw vco3OscSaw;
+ OscPulse vco3OscPulse;
+ OscTri vco3OscTri;
+ Gain vco3Gain;
+ OscFilt oscFilt;
+ NoiseGen noiseGen;
+ PinkFilt pinkFilt;
+ Gain noiseGain;
+ EnvGen vcfEnvGen;
+ EnvGen vcaEnvGen;
+ PhaseGen<1> a440PhaseGen;
+ Gain<1> gain;
+ PPM<1> ppm;
+
+ Voice voices[N_VOICES];
+
+ size_t syncCount;
+
+ uint64_t randState;
+ float masterTune;
+ float modulationMix;
+ float vco1Modulation;
+ float vco1Coarse;
+ float vco1Fine;
+ char vco1Waveform;
+ float vco2Modulation;
+ float vco2Coarse;
+ float vco2Fine;
+ char vco2Waveform;
+ char vco3KbdCtrl;
+ float vco3Coarse;
+ float vco3Fine;
+ char vco3Waveform;
+ char noiseColor;
+ float vcfModulation;
+ char vcfKbdCtrl;
+ float vcfCutoff;
+ float vcfContour;
+ char a440;
+
+ float pitchBend;
+ float modWheel;
+ size_t syncLeft;
+ char vco3WaveformCur;
+ char noiseColorCur;
+ char vco1WaveformCur;
+ char vco2WaveformCur;
+
+ float buf[BUFFER_SIZE];
+};
+
+extern "C" {
+
+impl impl_new(void) {
+ Engine *instance = new Engine();
+
+ instance->vco1OscSaw.setAntialiasing(true);
+ instance->vco1OscPulse.setAntialiasing(true);
+ instance->vco1OscTri.setAntialiasing(true);
+ instance->vco2OscSaw.setAntialiasing(true);
+ instance->vco2OscPulse.setAntialiasing(true);
+ instance->vco2OscTri.setAntialiasing(true);
+ instance->vco3OscSaw.setAntialiasing(true);
+ instance->vco3OscPulse.setAntialiasing(true);
+ instance->vco3OscTri.setAntialiasing(true);
+ instance->a440PhaseGen.setFrequency(440.f);
+
+ instance->randState = 0xbaddecaf600dfeed;
+
+ for (int i = 0; i < N_VOICES; i++) {
+ instance->voices[i].instance = instance;
+ instance->voices[i].index = i;
+ }
+
+ return reinterpret_cast(instance);
+}
+
+void impl_free(impl handle) {
+ Engine *instance = reinterpret_cast(handle);
+ delete instance;
+}
+
+void impl_set_sample_rate(impl handle, float sample_rate) {
+ Engine *instance = reinterpret_cast(handle);
+
+ instance->vco1OscSaw.setSampleRate(sample_rate);
+ instance->vco1OscPulse.setSampleRate(sample_rate);
+ instance->vco1OscTri.setSampleRate(sample_rate);
+ instance->vco1Gain.setSampleRate(sample_rate);
+ instance->vco2OscSaw.setSampleRate(sample_rate);
+ instance->vco2OscPulse.setSampleRate(sample_rate);
+ instance->vco2OscTri.setSampleRate(sample_rate);
+ instance->vco2Gain.setSampleRate(sample_rate);
+ instance->vco3OscSaw.setSampleRate(sample_rate);
+ instance->vco3OscPulse.setSampleRate(sample_rate);
+ instance->vco3OscTri.setSampleRate(sample_rate);
+ instance->vco3Gain.setSampleRate(sample_rate);
+ instance->noiseGen.setSampleRate(sample_rate);
+ instance->pinkFilt.setSampleRate(sample_rate);
+ instance->noiseGain.setSampleRate(sample_rate);
+ instance->vcfEnvGen.setSampleRate(sample_rate);
+ instance->vcaEnvGen.setSampleRate(sample_rate);
+ instance->a440PhaseGen.setSampleRate(sample_rate);
+ instance->gain.setSampleRate(sample_rate);
+ instance->ppm.setSampleRate(sample_rate);
+
+ for (int i = 0; i < N_VOICES; i++) {
+ instance->voices[i].vco1PhaseGen.setSampleRate(sample_rate);
+ instance->voices[i].vco2PhaseGen.setSampleRate(sample_rate);
+ instance->voices[i].vco3PhaseGen.setSampleRate(sample_rate);
+ instance->voices[i].vcf.setSampleRate(sample_rate);
+ }
+
+ instance->syncCount = (size_t)bw_roundf(sample_rate * SYNC_RATE);
+}
+
+void impl_reset(impl handle) {
+ Engine *instance = reinterpret_cast(handle);
+
+ for (int i = 0; i < N_VOICES; i++)
+ instance->voices[i].vcf.setCutoff(instance->vcfCutoff);
+
+ instance->noteQueue = NoteQueue();
+ instance->vco1OscSaw.reset();
+ instance->vco1OscPulse.reset();
+ instance->vco1OscTri.reset();
+ instance->vco1Gain.reset();
+ instance->vco2OscSaw.reset();
+ instance->vco2OscPulse.reset();
+ instance->vco2OscTri.reset();
+ instance->vco2Gain.reset();
+ instance->vco3OscSaw.reset();
+ instance->vco3OscPulse.reset();
+ instance->vco3OscTri.reset();
+ instance->vco3Gain.reset();
+ instance->oscFilt.reset();
+ instance->noiseGen.reset();
+ instance->pinkFilt.reset();
+ instance->noiseGain.reset();
+ instance->vcfEnvGen.reset();
+ instance->vcaEnvGen.reset();
+ instance->a440PhaseGen.reset();
+ instance->gain.reset();
+ instance->ppm.reset();
+ for (int i = 0; i < N_VOICES; i++) {
+ instance->voices[i].vco1PhaseGen.reset();
+ instance->voices[i].vco2PhaseGen.reset();
+ instance->voices[i].vco3PhaseGen.reset();
+ instance->voices[i].vcf.reset();
+
+ instance->voices[i].note = 60;
+ instance->voices[i].gate = 0;
+ }
+ instance->pitchBend = 0.f;
+ instance->modWheel = 0.f;
+ instance->syncLeft = instance->syncCount;
+ instance->vco3WaveformCur = instance->vco3Waveform;
+ instance->noiseColorCur = instance->noiseColor;
+ instance->vco1WaveformCur = instance->vco1Waveform;
+ instance->vco2WaveformCur = instance->vco2Waveform;
+}
+
+void impl_set_parameter(impl handle, size_t index, float value) {
+ Engine *instance = reinterpret_cast(handle);
+ switch (index) {
+ case 0:
+ {
+ const float v = 0.01f * value;
+ instance->gain.setGainLin(v * v * v);
+ }
+ break;
+ case 1:
+ instance->masterTune = value;
+ break;
+ case 2:
+ {
+ // using portamento time 0% -> 90%: tau = portamento time / log(10)
+ const float v = (0.001f * 0.4342944819032517f) * value;
+ for (int i = 0; i < N_VOICES; i++) {
+ instance->voices[i].vco1PhaseGen.setPortamentoTau(v);
+ instance->voices[i].vco2PhaseGen.setPortamentoTau(v);
+ instance->voices[i].vco3PhaseGen.setPortamentoTau(v);
+ }
+ break;
+ }
+ case 3:
+ instance->modulationMix = 0.01f * value;
+ break;
+ case 4:
+ instance->vco1Modulation = 0.01f * value;
+ break;
+ case 5:
+ instance->vco1Coarse = value;
+ break;
+ case 6:
+ instance->vco1Fine = value;
+ break;
+ case 7:
+ instance->vco1Waveform = value;
+ break;
+ case 8:
+ {
+ const float v = 0.01f * value;
+ instance->vco1OscPulse.setPulseWidth(v);
+ instance->vco1OscTri.setSlope(bw_clipf(v, 0.001f, 0.999f));
+ break;
+ }
+ case 9:
+ {
+ const float v = 0.01f * value;
+ instance->vco1Gain.setGainLin(v * v * v);
+ break;
+ }
+ case 10:
+ instance->vco2Modulation = 0.01f * value;
+ break;
+ case 11:
+ instance->vco2Coarse = value;
+ break;
+ case 12:
+ instance->vco2Fine = value;
+ break;
+ case 13:
+ instance->vco2Waveform = value;
+ break;
+ case 14:
+ {
+ const float v = 0.01f * value;
+ instance->vco2OscPulse.setPulseWidth(v);
+ instance->vco2OscTri.setSlope(bw_clipf(v, 0.001f, 0.999f));
+ break;
+ }
+ case 15:
+ {
+ const float v = 0.01f * value;
+ instance->vco2Gain.setGainLin(v * v * v);
+ break;
+ }
+ case 16:
+ instance->vco3KbdCtrl = value >= 0.5f;
+ break;
+ case 17:
+ instance->vco3Coarse = value;
+ break;
+ case 18:
+ instance->vco3Fine = value;
+ break;
+ case 19:
+ instance->vco3Waveform = value;
+ break;
+ case 20:
+ {
+ const float v = 0.01f * value;
+ instance->vco3OscPulse.setPulseWidth(v);
+ instance->vco3OscTri.setSlope(bw_clipf(v, 0.001f, 0.999f));
+ break;
+ }
+ case 21:
+ {
+ const float v = 0.01f * value;
+ instance->vco3Gain.setGainLin(v * v * v);
+ break;
+ }
+ case 22:
+ instance->noiseColor = value;
+ break;
+ case 23:
+ {
+ const float v = 0.01f * value;
+ instance->noiseGain.setGainLin(v * v * v);
+ break;
+ }
+ case 24:
+ instance->vcfModulation = 0.01f * value;
+ break;
+ case 25:
+ instance->vcfKbdCtrl = value;
+ break;
+ case 26:
+ instance->vcfCutoff = value;
+ break;
+ case 27:
+ {
+ const float v = 0.5f + (0.01f * 9.5f) * value;
+ for (int i = 0; i < N_VOICES; i++)
+ instance->voices[i].vcf.setQ(v);
+ break;
+ }
+ case 28:
+ instance->vcfContour = 0.01f * value;
+ break;
+ case 29:
+ instance->vcfEnvGen.setAttack(0.001f * value);
+ break;
+ case 30:
+ instance->vcfEnvGen.setDecay(0.001f * value);
+ break;
+ case 31:
+ instance->vcfEnvGen.setSustain(0.01f * value);
+ break;
+ case 32:
+ instance->vcfEnvGen.setRelease(0.001f * value);
+ break;
+ case 33:
+ instance->vcaEnvGen.setAttack(0.001f * value);
+ break;
+ case 34:
+ instance->vcaEnvGen.setDecay(0.001f * value);
+ break;
+ case 35:
+ instance->vcaEnvGen.setSustain(0.01f * value);
+ break;
+ case 36:
+ instance->vcaEnvGen.setRelease(0.001f * value);
+ break;
+ case 37:
+ instance->a440 = value >= 0.5f;
+ break;
+ }
+}
+
+float impl_get_parameter(impl handle, size_t index) {
+ (void)index;
+ Engine *instance = reinterpret_cast(handle);
+ return bw_clipf(instance->ppm.getYZ1(0), -60.f, 0.f);
+}
+
+static void noteOn(void *BW_RESTRICT handle, unsigned char note, float velocity) {
+ (void)velocity;
+ Voice *v = (Voice *)handle;
+ v->note = note;
+ v->gate = 1;
+}
+
+static void noteOff(void *BW_RESTRICT handle, float velocity) {
+ (void)velocity;
+ Voice *v = (Voice *)handle;
+ v->gate = 0;
+}
+
+static unsigned char getNote(const void *BW_RESTRICT handle) {
+ Voice *v = (Voice *)handle;
+ return v->note;
+}
+
+static char isFree(const void *BW_RESTRICT handle) {
+ Voice *v = (Voice *)handle;
+ bw_env_gen_phase phase = v->instance->vcaEnvGen.getPhase(v->index);
+ return !v->gate && phase == bw_env_gen_phase_off;
+}
+
+void impl_process(impl handle, const float **inputs, float **outputs, size_t n_samples) {
+ // here is a WASM-compatible version only as it'd be too cumbersome to maintain two versions
+
+ (void)inputs;
+
+ Engine *instance = reinterpret_cast(handle);
+
+ // voice allocation
+
+ static bw_voice_alloc_opts alloc_opts = { bw_voice_alloc_priority_low, noteOn, noteOff, getNote, isFree };
+ void *voices[N_VOICES];
+ for (int i = 0; i < N_VOICES; i++)
+ voices[i] = (void *)(instance->voices + i);
+ bw_voice_alloc(&alloc_opts, &instance->noteQueue.queue, voices, N_VOICES);
+ instance->noteQueue.clear();
+
+ // asynchronous control-rate operations
+
+ const float df1 = instance->vco1Coarse + instance->pitchBend + (8.333333333333333e-2f * 0.01f) * instance->vco1Fine;
+ const float df2 = instance->vco2Coarse + instance->pitchBend + (8.333333333333333e-2f * 0.01f) * instance->vco2Fine;
+ 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;
+ 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));
+ }
+
+ if (instance->vco3WaveformCur != instance->vco3Waveform) {
+ switch (instance->vco3Waveform) {
+ case 2:
+ instance->vco3OscPulse.reset();
+ break;
+ case 3:
+ instance->vco3OscTri.reset();
+ break;
+ }
+ 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:
+ instance->vco1OscPulse.reset();
+ break;
+ case 3:
+ instance->vco1OscTri.reset();
+ break;
+ }
+ instance->vco1WaveformCur = instance->vco1Waveform;
+ }
+
+ if (instance->vco2WaveformCur != instance->vco2Waveform) {
+ switch (instance->vco2Waveform) {
+ case 2:
+ instance->vco2OscPulse.reset();
+ break;
+ case 3:
+ instance->vco2OscTri.reset();
+ break;
+ }
+ instance->vco2WaveformCur = instance->vco2Waveform;
+ }
+
+ // synchronous control-rate and audio-rate operations
+
+ 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;
+ }
+
+ for (size_t i = 0; i < n_samples; ) {
+ float *out = outputs[0] + i;
+ int n = bw_minf(bw_minf(n_samples - i, BUFFER_SIZE), instance->syncLeft);
+
+ const char sync = instance->syncLeft == instance->syncCount;
+ float *y[1] = {out};
+
+ // vco 3
+
+ for (int j = 0; j < N_VOICES; j++) {
+ float *vb0[1] = {instance->voices[j].buf[0]};
+ float *vb1[1] = {instance->voices[j].buf[1]};
+ instance->voices[j].vco3PhaseGen.process(nullptr, vb0, vb1, n);
+ }
+ switch (instance->vco3WaveformCur) {
+ case 1:
+ instance->vco3OscSaw.process(b0, b1, b0, n);
+ break;
+ case 2:
+ instance->vco3OscPulse.process(b0, b1, b0, n);
+ break;
+ default:
+ instance->vco3OscTri.process(b0, b1, 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);
+
+ // 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]));
+ }
+ if (sync)
+ for (int j = 0; j < N_VOICES; j++)
+ instance->voices[j].modK = b2[j][0];
+
+ // vco 1
+
+ for (int j = 0; j < N_VOICES; j++) {
+ float *vb2[1] = {instance->voices[j].buf[2]};
+ float *vb3[1] = {instance->voices[j].buf[3]};
+ float *vb4[1] = {instance->voices[j].buf[4]};
+ bufScale<1>(vb2, instance->vco1Modulation, vb3, n);
+ instance->voices[j].vco1PhaseGen.process(vb3, vb3, vb4, n);
+ }
+ switch (instance->vco1WaveformCur) {
+ case 1:
+ instance->vco1OscSaw.process(b3, b4, b3, n);
+ break;
+ case 2:
+ instance->vco1OscPulse.process(b3, b4, b3, n);
+ break;
+ default:
+ instance->vco1OscTri.process(b3, b4, b3, n);
+ break;
+ }
+
+ // vco 2
+
+ for (int j = 0; j < N_VOICES; j++) {
+ float *vb2[1] = {instance->voices[j].buf[2]};
+ float *vb4[1] = {instance->voices[j].buf[4]};
+ bufScale<1>(vb2, instance->vco2Modulation, vb2, n);
+ instance->voices[j].vco2PhaseGen.process(vb2, vb2, vb4, n);
+ }
+ switch (instance->vco2WaveformCur) {
+ case 1:
+ instance->vco2OscSaw.process(b2, b4, b2, n);
+ break;
+ case 2:
+ instance->vco2OscPulse.process(b2, b4, b2, n);
+ break;
+ default:
+ instance->vco2OscTri.process(b2, b4, 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->oscFilt.process(b0, 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);
+
+ // vcf
+
+ instance->vcfEnvGen.process(g, nullptr, n);
+ if (sync)
+ 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;
+ }
+ instance->voices[j].vcf.setCutoff(bw_clipf(cutoff, 20.f, 20e3f));
+ 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);
+
+ // mix voices
+
+ bufFill<1>(0.f, y, n);
+ for (int j = 0; j < N_VOICES; j++) {
+ float *vb0[1] = {instance->voices[j].buf[0]};
+ bufMix<1>(y, vb0, y, n);
+ }
+
+ // A 440 Hz osc
+
+ if (instance->a440) { // nobody will notice or care about phase issues here
+ float *b[1] = {instance->buf};
+ instance->a440PhaseGen.process(nullptr, b, nullptr, n);
+ oscSinProcess<1>(b, b, n);
+ bufMix<1>(y, b, y, n);
+ }
+
+ // output
+
+ instance->gain.process(y, y, n);
+ instance->ppm.process(y, nullptr, n);
+
+ instance->syncLeft -= n;
+ if (instance->syncLeft == 0)
+ instance->syncLeft = instance->syncCount;
+
+ i += n;
+ }
+}
+
+void impl_midi_msg_in(impl handle, size_t index, const uint8_t * data) {
+ (void)index;
+ Engine *instance = reinterpret_cast(handle);
+ switch (data[0] & 0xf0) {
+ case 0x90: // note on
+ instance->noteQueue.add(data[1], data[2] != 0, (1.f / 127.f) * data[2], false);
+ break;
+ case 0x80: // note off
+ instance->noteQueue.add(data[1], false, 0, false);
+ break;
+ case 0xe0: // pitch bend
+ {
+ const uint16_t v = (data[2] << 7) | data[1];
+ instance->pitchBend = 2.f * bw_maxf((1.f / 16383.f) * (v - 0x2000), -1.f) - 1.f;
+ break;
+ }
+ case 0xb0: // control change
+ if (data[1] == 1) // mod wheel
+ instance->modWheel = (1.f / 127.f) * data[2];
+ break;
+ }
+}
+
+}
diff --git a/examples/synthpp_poly/src/ios.json b/examples/synthpp_poly/src/ios.json
new file mode 100644
index 0000000..054eaa6
--- /dev/null
+++ b/examples/synthpp_poly/src/ios.json
@@ -0,0 +1,5 @@
+{
+ "ios": {
+ "productBundleIdentifier": "com.orastron.bw_example_synthpp_poly"
+ }
+}
diff --git a/examples/synthpp_poly/src/lv2.json b/examples/synthpp_poly/src/lv2.json
new file mode 100644
index 0000000..a4e9e61
--- /dev/null
+++ b/examples/synthpp_poly/src/lv2.json
@@ -0,0 +1,22 @@
+{
+ "lv2": {
+ "prefixes": {
+ "bw_examples": "https://www.orastron.com/brickworks/examples/"
+ },
+ "uri": "@bw_examples:synthpp_poly",
+ "types": [ "@lv2:InstrumentPlugin" ],
+ "version": "1.0",
+ "busSymbols": [ "midi_in", "output" ],
+ "parameterSymbols": [
+ "volume", "master_tune", "portamento", "mod_mix",
+ "vco1_mod", "vco1_coarse", "vco1_fine", "vco1_wave", "vco1_pw", "vco1_level",
+ "vco2_mod", "vco2_coarse", "vco2_fine", "vco2_wave", "vco2_pw", "vco2_level",
+ "vco3_mod", "vco3_coarse", "vco3_fine", "vco3_wave", "vco3_pw", "vco3_level",
+ "noise_color", "noise_level",
+ "vcf_mod", "vcf_kbd_ctrl", "vcf_cutoff", "vcf_resonance", "vcf_contour",
+ "vcf_attack", "vcf_decay", "vcf_sustain", "vcf_release",
+ "vca_attack", "vca_decay", "vca_sustain", "vca_release",
+ "a440", "level"
+ ]
+ }
+}
diff --git a/examples/synthpp_poly/src/product.json b/examples/synthpp_poly/src/product.json
new file mode 100644
index 0000000..63fc712
--- /dev/null
+++ b/examples/synthpp_poly/src/product.json
@@ -0,0 +1,443 @@
+{
+ "product": {
+ "name": "Brickworks polyphonic synth example (C++)",
+ "version": "1.1.0",
+ "buildVersion": "1",
+ "bundleName": "bw_example_synthpp_poly",
+ "buses": [
+ {
+ "type": "midi",
+ "direction": "input",
+ "name": "MIDI input",
+ "shortName": "MIDI input"
+ },
+ {
+ "type": "audio",
+ "direction": "output",
+ "channels": "mono",
+ "name": "Output",
+ "shortName": "Output"
+ }
+ ],
+ "parameters": [
+ {
+ "name": "Volume",
+ "shortName": "Volume",
+ "direction": "input",
+ "defaultValue": 50.0,
+ "minimum": 0.0,
+ "maximum": 100.0,
+ "unit": "pc",
+ "map": "linear"
+ },
+ {
+ "name": "Master tune",
+ "shortName": "Master tune",
+ "direction": "input",
+ "defaultValue": 440.0,
+ "minimum": 415.304697579945,
+ "maximum": 466.1637615180899,
+ "unit": "hz",
+ "map": "logarithmic"
+ },
+ {
+ "name": "Portamento",
+ "shortName": "Portamento",
+ "direction": "input",
+ "defaultValue": 0.0,
+ "minimum": 0.0,
+ "maximum": 1000.0,
+ "unit": "ms",
+ "map": "linear"
+ },
+ {
+ "name": "Modulation mix",
+ "shortName": "Modulation mix",
+ "direction": "input",
+ "defaultValue": 0.0,
+ "minimum": 0.0,
+ "maximum": 100.0,
+ "unit": "pc",
+ "map": "linear"
+ },
+ {
+ "name": "VCO1 modulation",
+ "shortName": "VCO1 modulation",
+ "direction": "input",
+ "defaultValue": 0.0,
+ "minimum": 0.0,
+ "maximum": 100.0,
+ "unit": "pc",
+ "map": "linear"
+ },
+ {
+ "name": "VCO1 coarse tuning",
+ "shortName": "VCO1 coarse",
+ "direction": "input",
+ "defaultValue": 0.0,
+ "minimum": -3.0,
+ "maximum": 3.0,
+ "unit": "oct",
+ "map": "linear"
+ },
+ {
+ "name": "VCO1 fine tuning",
+ "shortName": "VCO1 fine",
+ "direction": "input",
+ "defaultValue": 0.0,
+ "minimum": -100.0,
+ "maximum": 100.0,
+ "unit": "cent",
+ "map": "linear"
+ },
+ {
+ "name": "VCO1 waveform",
+ "shortName": "VCO1 waveform",
+ "direction": "input",
+ "defaultValue": 1.0,
+ "minimum": 1.0,
+ "maximum": 3.0,
+ "integer": true,
+ "scalePoints": {
+ "Saw": 1.0,
+ "Pulse": 2.0,
+ "Triangle": 3.0
+ },
+ "list": true,
+ "map": "linear"
+ },
+ {
+ "name": "VCO1 pulse width/slope",
+ "shortName": "VCO1 pw/slope",
+ "direction": "input",
+ "defaultValue": 50.0,
+ "minimum": 0.0,
+ "maximum": 100.0,
+ "unit": "pc",
+ "map": "linear"
+ },
+ {
+ "name": "VCO1 level",
+ "shortName": "VCO1 level",
+ "direction": "input",
+ "defaultValue": 100.0,
+ "minimum": 0.0,
+ "maximum": 100.0,
+ "unit": "pc",
+ "map": "linear"
+ },
+ {
+ "name": "VCO2 modulation",
+ "shortName": "VCO2 modulation",
+ "direction": "input",
+ "defaultValue": 0.0,
+ "minimum": 0.0,
+ "maximum": 100.0,
+ "unit": "pc",
+ "map": "linear"
+ },
+ {
+ "name": "VCO2 coarse tuning",
+ "shortName": "VCO2 coarse",
+ "direction": "input",
+ "defaultValue": 0.0,
+ "minimum": -3.0,
+ "maximum": 3.0,
+ "unit": "oct",
+ "map": "linear"
+ },
+ {
+ "name": "VCO2 fine tuning",
+ "shortName": "VCO2 fine",
+ "direction": "input",
+ "defaultValue": 0.0,
+ "minimum": -100.0,
+ "maximum": 100.0,
+ "unit": "cent",
+ "map": "linear"
+ },
+ {
+ "name": "VCO2 waveform",
+ "shortName": "VCO2 waveform",
+ "direction": "input",
+ "defaultValue": 1.0,
+ "minimum": 1.0,
+ "maximum": 3.0,
+ "integer": true,
+ "scalePoints": {
+ "Saw": 1.0,
+ "Pulse": 2.0,
+ "Triangle": 3.0
+ },
+ "list": true,
+ "map": "linear"
+ },
+ {
+ "name": "VCO2 pulse width/slope",
+ "shortName": "VCO2 pw/slope",
+ "direction": "input",
+ "defaultValue": 50.0,
+ "minimum": 0.0,
+ "maximum": 100.0,
+ "unit": "pc",
+ "map": "linear"
+ },
+ {
+ "name": "VCO2 level",
+ "shortName": "VCO2 level",
+ "direction": "input",
+ "defaultValue": 0.0,
+ "minimum": 0.0,
+ "maximum": 100.0,
+ "unit": "pc",
+ "map": "linear"
+ },
+ {
+ "name": "VCO3 keyboard control",
+ "shortName": "VCO3 kbd ctrl",
+ "direction": "input",
+ "defaultValue": 1.0,
+ "minimum": 0.0,
+ "maximum": 1.0,
+ "toggled": true
+ },
+ {
+ "name": "VCO3 coarse tuning",
+ "shortName": "VCO3 coarse",
+ "direction": "input",
+ "defaultValue": 0.0,
+ "minimum": -3.0,
+ "maximum": 3.0,
+ "unit": "oct",
+ "map": "linear"
+ },
+ {
+ "name": "VCO3 fine tuning",
+ "shortName": "VCO3 fine",
+ "direction": "input",
+ "defaultValue": 0.0,
+ "minimum": -100.0,
+ "maximum": 100.0,
+ "unit": "cent",
+ "map": "linear"
+ },
+ {
+ "name": "VCO3 waveform",
+ "shortName": "VCO3 waveform",
+ "direction": "input",
+ "defaultValue": 1.0,
+ "minimum": 1.0,
+ "maximum": 3.0,
+ "integer": true,
+ "scalePoints": {
+ "Saw": 1.0,
+ "Pulse": 2.0,
+ "Triangle": 3.0
+ },
+ "list": true,
+ "map": "linear"
+ },
+ {
+ "name": "VCO3 pulse width/slope",
+ "shortName": "VCO3 pw/slope",
+ "direction": "input",
+ "defaultValue": 50.0,
+ "minimum": 0.0,
+ "maximum": 100.0,
+ "unit": "pc",
+ "map": "linear"
+ },
+ {
+ "name": "VCO3 level",
+ "shortName": "VCO3 level",
+ "direction": "input",
+ "defaultValue": 0.0,
+ "minimum": 0.0,
+ "maximum": 100.0,
+ "unit": "pc",
+ "map": "linear"
+ },
+ {
+ "name": "Noise color",
+ "shortName": "Noise color",
+ "direction": "input",
+ "defaultValue": 1.0,
+ "minimum": 1.0,
+ "maximum": 2.0,
+ "integer": true,
+ "scalePoints": {
+ "White": 1.0,
+ "Pink": 2.0
+ },
+ "list": true,
+ "map": "linear"
+ },
+ {
+ "name": "Noise level",
+ "shortName": "Noise level",
+ "direction": "input",
+ "defaultValue": 0.0,
+ "minimum": 0.0,
+ "maximum": 100.0,
+ "unit": "pc",
+ "map": "linear"
+ },
+ {
+ "name": "VCF modulation",
+ "shortName": "VCF modulation",
+ "direction": "input",
+ "defaultValue": 0.0,
+ "minimum": 0.0,
+ "maximum": 100.0,
+ "unit": "pc",
+ "map": "linear"
+ },
+ {
+ "name": "VCF keyboard control",
+ "shortName": "VCF kbd ctrl",
+ "direction": "input",
+ "defaultValue": 1.0,
+ "minimum": 1.0,
+ "maximum": 4.0,
+ "integer": true,
+ "scalePoints": {
+ "Off": 1.0,
+ "1/3": 2.0,
+ "2/3": 3.0,
+ "Full": 4.0
+ },
+ "list": true,
+ "map": "linear"
+ },
+ {
+ "name": "VCF cutoff",
+ "shortName": "VCF cutoff",
+ "direction": "input",
+ "defaultValue": 20e3,
+ "minimum": 20.0,
+ "maximum": 20e3,
+ "unit": "hz",
+ "map": "logarithmic"
+ },
+ {
+ "name": "VCF resonance",
+ "shortName": "VCF resonance",
+ "direction": "input",
+ "defaultValue": 0.0,
+ "minimum": 0.0,
+ "maximum": 100.0,
+ "unit": "pc",
+ "map": "linear"
+ },
+ {
+ "name": "VCF contour",
+ "shortName": "VCF contour",
+ "direction": "input",
+ "defaultValue": 0.0,
+ "minimum": 0.0,
+ "maximum": 100.0,
+ "unit": "pc",
+ "map": "linear"
+ },
+ {
+ "name": "VCF attack",
+ "shortName": "VCF attack",
+ "direction": "input",
+ "defaultValue": 0.0,
+ "minimum": 0.0,
+ "maximum": 1000.0,
+ "unit": "ms",
+ "map": "linear"
+ },
+ {
+ "name": "VCF decay",
+ "shortName": "VCF decay",
+ "direction": "input",
+ "defaultValue": 0.0,
+ "minimum": 0.0,
+ "maximum": 1000.0,
+ "unit": "ms",
+ "map": "linear"
+ },
+ {
+ "name": "VCF sustain",
+ "shortName": "VCF sustain",
+ "direction": "input",
+ "defaultValue": 100.0,
+ "minimum": 0.0,
+ "maximum": 100.0,
+ "unit": "pc",
+ "map": "linear"
+ },
+ {
+ "name": "VCF release",
+ "shortName": "VCF release",
+ "direction": "input",
+ "defaultValue": 0.0,
+ "minimum": 0.0,
+ "maximum": 1000.0,
+ "unit": "ms",
+ "map": "linear"
+ },
+ {
+ "name": "VCA attack",
+ "shortName": "VCA attack",
+ "direction": "input",
+ "defaultValue": 2.0,
+ "minimum": 2.0,
+ "maximum": 1000.0,
+ "unit": "ms",
+ "map": "linear"
+ },
+ {
+ "name": "VCA decay",
+ "shortName": "VCA decay",
+ "direction": "input",
+ "defaultValue": 2.0,
+ "minimum": 2.0,
+ "maximum": 1000.0,
+ "unit": "ms",
+ "map": "linear"
+ },
+ {
+ "name": "VCA sustain",
+ "shortName": "VCA sustain",
+ "direction": "input",
+ "defaultValue": 100.0,
+ "minimum": 0.0,
+ "maximum": 100.0,
+ "unit": "pc",
+ "map": "linear"
+ },
+ {
+ "name": "VCA release",
+ "shortName": "VCA release",
+ "direction": "input",
+ "defaultValue": 2.0,
+ "minimum": 2.0,
+ "maximum": 1000.0,
+ "unit": "ms",
+ "map": "linear"
+ },
+ {
+ "name": "A440",
+ "shortName": "A440",
+ "direction": "input",
+ "defaultValue": 0.0,
+ "minimum": 0.0,
+ "maximum": 1.0,
+ "toggled": true
+ },
+ {
+ "name": "Level",
+ "shortName": "Level",
+ "direction": "output",
+ "defaultValue": -60.0,
+ "minimum": -60.0,
+ "maximum": 0.0,
+ "unit": "db",
+ "map": "linear"
+ }
+ ]
+ }
+}
diff --git a/examples/synthpp_poly/src/vst3.json b/examples/synthpp_poly/src/vst3.json
new file mode 100644
index 0000000..06e4f1e
--- /dev/null
+++ b/examples/synthpp_poly/src/vst3.json
@@ -0,0 +1,11 @@
+{
+ "vst3": {
+ "plugin": {
+ "cid": "7f75d73bf5aa4d9dba2b1d5483d45cfe"
+ },
+ "controller": {
+ "cid": "183b9664aca74eafad09b16b5d702155"
+ },
+ "subCategory": "Instrument|Synth"
+ }
+}