fix bw_{note_queue,voice_alloc}, synth_poly begins to work

This commit is contained in:
Stefano D'Angelo 2023-06-08 09:35:36 +02:00
parent b2169b6792
commit 6cecf0d654
9 changed files with 96 additions and 56 deletions

1
TODO
View File

@ -49,6 +49,7 @@ code:
* revise typedef style (see https://stackoverflow.com/questions/54752861/using-an-anonymous-struct-vs-a-named-struct-with-typedef) * revise typedef style (see https://stackoverflow.com/questions/54752861/using-an-anonymous-struct-vs-a-named-struct-with-typedef)
* sr-dependent vs cr-dependent coeffs? see synth poly example * sr-dependent vs cr-dependent coeffs? see synth poly example
* bw_buf_copy (to be used in bw_slew_lim)? * bw_buf_copy (to be used in bw_slew_lim)?
* C++ compound literals...
build system: build system:
* make makefiles handle paths with spaces etc * make makefiles handle paths with spaces etc

View File

@ -1,8 +1,8 @@
ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
TARGET = bw_example_synth_mono TARGET = bw_example_synth_poly
C_SOURCES += ${ROOT_DIR}/../src/bw_example_synth_mono.c C_SOURCES += ${ROOT_DIR}/../src/bw_example_synth_poly.c
SYNTH := yes SYNTH := yes

View File

@ -90,33 +90,28 @@ void bw_example_synth_poly_set_sample_rate(bw_example_synth_poly *instance, floa
bw_phase_gen_set_sample_rate(&instance->voices[i].vco2_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_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); bw_svf_set_sample_rate(&instance->voices[i].vcf_coeffs, sample_rate);
voices[i].gate = 0;
} }
} }
void bw_example_synth_poly_reset(bw_example_synth_poly *instance) { void bw_example_synth_poly_reset(bw_example_synth_poly *instance) {
const float v = instance->params[p_vcf_cutoff]; const float v = instance->params[p_vcf_cutoff];
const float cutoff = 20.f + (20e3f - 20.f) * v * v * v; const float cutoff = 20.f + (20e3f - 20.f) * v * v * v;
bw_svf_set_cutoff(&instance->vcf_coeffs, bw_clipf(cutoff, 20.f, 20e3f)); 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_note_queue_reset(&instance->note_queue);
bw_osc_pulse_reset_coeffs(&instance->vco1_pulse_coeffs); bw_osc_pulse_reset_coeffs(&instance->vco1_pulse_coeffs);
bw_osc_tri_reset_coeffs(&instance->vco1_tri_coeffs); bw_osc_tri_reset_coeffs(&instance->vco1_tri_coeffs);
bw_gain_reset_coeffs(&instance->vco1_gain_coeffs); bw_gain_reset_coeffs(&instance->vco1_gain_coeffs);
bw_phase_gen_reset_coeffs(&instance->vco2_phase_gen_coeffs);
bw_osc_pulse_reset_coeffs(&instance->vco2_pulse_coeffs); bw_osc_pulse_reset_coeffs(&instance->vco2_pulse_coeffs);
bw_osc_tri_reset_coeffs(&instance->vco2_tri_coeffs); bw_osc_tri_reset_coeffs(&instance->vco2_tri_coeffs);
bw_gain_reset_coeffs(&instance->vco2_gain_coeffs); bw_gain_reset_coeffs(&instance->vco2_gain_coeffs);
bw_phase_gen_reset_coeffs(&instance->vco3_phase_gen_coeffs);
bw_osc_pulse_reset_coeffs(&instance->vco3_pulse_coeffs); bw_osc_pulse_reset_coeffs(&instance->vco3_pulse_coeffs);
bw_osc_tri_reset_coeffs(&instance->vco3_tri_coeffs); bw_osc_tri_reset_coeffs(&instance->vco3_tri_coeffs);
bw_gain_reset_coeffs(&instance->vco3_gain_coeffs); bw_gain_reset_coeffs(&instance->vco3_gain_coeffs);
bw_gain_reset_coeffs(&instance->noise_gain_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->vcf_env_gen_coeffs);
bw_env_gen_reset_state(&instance->vcf_env_gen_coeffs, &instance->vcf_env_gen_state);
bw_svf_reset_coeffs(&instance->vcf_coeffs);
bw_env_gen_reset_coeffs(&instance->vca_env_gen_coeffs); 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_coeffs(&instance->a440_phase_gen_coeffs);
bw_phase_gen_reset_state(&instance->a440_phase_gen_coeffs, &instance->a440_phase_gen_state, 0.f); bw_phase_gen_reset_state(&instance->a440_phase_gen_coeffs, &instance->a440_phase_gen_state, 0.f);
bw_gain_reset_coeffs(&instance->gain_coeffs); bw_gain_reset_coeffs(&instance->gain_coeffs);
@ -124,12 +119,20 @@ void bw_example_synth_poly_reset(bw_example_synth_poly *instance) {
bw_ppm_reset_state(&instance->ppm_coeffs, &instance->ppm_state); bw_ppm_reset_state(&instance->ppm_coeffs, &instance->ppm_state);
for (int i = 0; i < N_VOICES; i++) { for (int i = 0; i < N_VOICES; i++) {
bw_phase_gen_reset_state(&instance->vco1_phase_gen_coeffs, &instance->voices[i].vco1_phase_gen_state, 0.f); bw_phase_gen_reset_coeffs(&instance->voices[i].vco2_phase_gen_coeffs);
bw_phase_gen_reset_state(&instance->vco2_phase_gen_coeffs, &instance->voices[i].vco2_phase_gen_state, 0.f); bw_phase_gen_reset_coeffs(&instance->voices[i].vco3_phase_gen_coeffs);
bw_phase_gen_reset_state(&instance->vco3_phase_gen_coeffs, &instance->voices[i].vco3_phase_gen_state, 0.f); 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);
bw_phase_gen_reset_state(&instance->voices[i].vco2_phase_gen_coeffs, &instance->voices[i].vco2_phase_gen_state, 0.f);
bw_phase_gen_reset_state(&instance->voices[i].vco3_phase_gen_coeffs, &instance->voices[i].vco3_phase_gen_state, 0.f);
bw_osc_filt_reset_state(&instance->voices[i].osc_filt_state); bw_osc_filt_reset_state(&instance->voices[i].osc_filt_state);
bw_pink_filt_reset_state(&instance->pink_filt_coeffs, &instance->voices[i].pink_filt_state); bw_pink_filt_reset_state(&instance->pink_filt_coeffs, &instance->voices[i].pink_filt_state);
bw_svf_reset_state(&instance->vcf_coeffs, &instance->voices[i].vcf_state, 0.f); bw_svf_reset_state(&instance->voices[i].vcf_coeffs, &instance->voices[i].vcf_state, 0.f);
bw_env_gen_reset_state(&instance->vcf_env_gen_coeffs, &instance->voices[i].vcf_env_gen_state);
bw_env_gen_reset_state(&instance->vca_env_gen_coeffs, &instance->voices[i].vca_env_gen_state);
instance->voices[i].gate = 0;
} }
instance->pitch_bend = 0.f; instance->pitch_bend = 0.f;
@ -137,12 +140,14 @@ void bw_example_synth_poly_reset(bw_example_synth_poly *instance) {
} }
static void note_on(void *BW_RESTRICT voice, unsigned char note, float velocity) { 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; bw_example_synth_poly_voice *v = (bw_example_synth_poly_voice *)voice;
v->note = note; v->note = note;
v->gate = 1; v->gate = 1;
} }
static void note_off(void *BW_RESTRICT voice, float velocity) { static void note_off(void *BW_RESTRICT voice, float velocity) {
(void)velocity;
bw_example_synth_poly_voice *v = (bw_example_synth_poly_voice *)voice; bw_example_synth_poly_voice *v = (bw_example_synth_poly_voice *)voice;
v->gate = 0; v->gate = 0;
} }
@ -158,58 +163,87 @@ static char is_free(void *BW_RESTRICT voice) {
return !v->gate && phase == bw_env_gen_phase_off; 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) { 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... // 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, // 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" // otherwise it's probably still ok but a bit "swingy"
(void)x; (void)x;
static bw_voice_alloc_opts alloc_opts = { bw_voice_alloc_mode_low, note_on, note_off, get_note, is_free }; static bw_voice_alloc_opts alloc_opts = { bw_voice_alloc_mode_low, note_on, note_off, get_note, is_free };
bw_voice_alloc(&alloc_opts, &instance->note_queue, &instance->voices, N_VOICES); 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); bw_note_queue_clear(&instance->note_queue);
const float f1 = const float df1 =
6.f * instance->params[p_vco1_coarse] - 3.f 6.f * instance->params[p_vco1_coarse] - 3.f
+ 2.f * instance->pitch_bend - 1.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); + 8.333333333333333e-2f * (2.f * (instance->params[p_master_tune] + instance->params[p_vco1_fine]) - 71.f);
const float f2 = const float df2 =
6.f * instance->params[p_vco2_coarse] - 3.f 6.f * instance->params[p_vco2_coarse] - 3.f
+ 2.f * instance->pitch_bend - 1.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); + 8.333333333333333e-2f * (2.f * (instance->params[p_master_tune] + instance->params[p_vco2_fine]) - 71.f);
const float f3 = const float df3 =
6.f * instance->params[p_vco3_coarse] - 3.f 6.f * instance->params[p_vco3_coarse] - 3.f
+ 2.f * instance->pitch_bend - 1.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); + 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++) { for (int i = 0; i < N_VOICES; i++) {
int n = instance->params[p_vco3_kbd] >= 0.5f ? instance->voices[i].note : 0; 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_3(f1 + 8.333333333333333e-2f * instance->voices[i].note)); bw_phase_gen_set_frequency(&instance->voices[i].vco1_phase_gen_coeffs, 440.f * bw_pow2f_3(df1 + 8.333333333333333e-2f * instance->voices[i].note));
bw_phase_gen_set_frequency(&instance->voices[i].vco2_phase_gen_coeffs, 440.f * bw_pow2f_3(f2 + 8.333333333333333e-2f * instance->voices[i].note)); bw_phase_gen_set_frequency(&instance->voices[i].vco2_phase_gen_coeffs, 440.f * bw_pow2f_3(df2 + 8.333333333333333e-2f * instance->voices[i].note));
bw_phase_gen_set_frequency(&instance->voices[i].vco3_phase_gen_coeffs, 440.f * bw_pow2f_3(f3 + 8.333333333333333e-2f * n)); bw_phase_gen_set_frequency(&instance->voices[i].vco3_phase_gen_coeffs, 440.f * bw_pow2f_3(df3 + 8.333333333333333e-2f * n3));
} }
float *b0[N_VOICES], *b1[N_VOICES], *b2[N_VOICES], *b3[N_VOICES];
for (int j = 0; j < N_VOICES; j++) { for (int j = 0; j < N_VOICES; j++) {
bw_phase_gen_update_coeffs_ctrl(&instance->voices[i].vco1_phase_gen_coeffs); b0[j] = instance->voices[j].buf[0];
bw_phase_gen_update_coeffs_ctrl(&instance->voices[i].vco2_phase_gen_coeffs); b1[j] = instance->voices[j].buf[1];
bw_phase_gen_update_coeffs_ctrl(&instance->voices[i].vco3_phase_gen_coeffs); b2[j] = instance->voices[j].buf[2];
b3[j] = instance->voices[j].buf[3];
} }
for (int i = 0; i < n_samples; i++) { for (int i = 0; i < n_samples; i += BUFFER_SIZE) {
for (int j = 0; j < N_VOICES; j++) { float *out = y[0] + i;
bw_phase_gen_update_coeffs_audio(&instance->voices[i].vco1_phase_gen_coeffs); int n = bw_minf(n_samples - i, BUFFER_SIZE);
bw_phase_gen_update_coeffs_audio(&instance->voices[i].vco2_phase_gen_coeffs);
bw_phase_gen_update_coeffs_audio(&instance->voices[i].vco3_phase_gen_coeffs); 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 {
for (int j = 0; j < N_VOICES; j++)
bw_osc_saw_process(&instance->vco_saw_coeffs, b0[j], b1[j], b0[j], n);
bw_osc_pulse_reset_coeffs(&instance->vco3_pulse_coeffs);
bw_osc_tri_reset_coeffs(&instance->vco3_tri_coeffs);
} }
//...
bw_buf_fill(out, 0.f, n);
for (int j = 0; j < N_VOICES; j++) { for (int j = 0; j < N_VOICES; j++) {
float p1, p1inc; bw_example_synth_poly_voice *v = instance->voices + j;
bw_phase_gen_process1(&instance->voices[i].vco1_phase_gen_coeffs, &instance->voices[i].vco1_phase_gen_state, &p1, &p1inc; if (v->gate)
bw_buf_mix(out, out, b0[j], 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, out, instance->buf, n);
bw_gain_process(&instance->gain_coeffs, out, out, n);
bw_ppm_process(&instance->ppm_coeffs, &instance->ppm_state, out, NULL, n);
} }
//... //...
/*
for (int i = 0; i < n_samples; i += BUFFER_SIZE) { for (int i = 0; i < n_samples; i += BUFFER_SIZE) {
float *out = y[0] + i; float *out = y[0] + i;
int n = bw_minf(n_samples - i, BUFFER_SIZE); int n = bw_minf(n_samples - i, BUFFER_SIZE);
@ -305,6 +339,7 @@ void bw_example_synth_poly_process(bw_example_synth_poly *instance, const float*
bw_gain_process(&instance->gain_coeffs, out, out, n); bw_gain_process(&instance->gain_coeffs, out, out, n);
bw_ppm_process(&instance->ppm_coeffs, &instance->ppm_state, out, NULL, 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) { void bw_example_synth_poly_set_parameter(bw_example_synth_poly *instance, int index, float value) {

View File

@ -25,6 +25,7 @@
extern "C" { extern "C" {
#endif #endif
#include <bw_note_queue.h>
#include <bw_phase_gen.h> #include <bw_phase_gen.h>
#include <bw_osc_saw.h> #include <bw_osc_saw.h>
#include <bw_osc_pulse.h> #include <bw_osc_pulse.h>
@ -100,6 +101,8 @@ struct _bw_example_synth_poly_voice {
unsigned char note; unsigned char note;
char gate; char gate;
float buf[4][BUFFER_SIZE];
}; };
typedef struct _bw_example_synth_poly_voice bw_example_synth_poly_voice; typedef struct _bw_example_synth_poly_voice bw_example_synth_poly_voice;
@ -127,7 +130,7 @@ struct _bw_example_synth_poly {
bw_ppm_coeffs ppm_coeffs; bw_ppm_coeffs ppm_coeffs;
bw_ppm_state ppm_state; bw_ppm_state ppm_state;
bw_example_poly_synth_voice voices[N_VOICES]; bw_example_synth_poly_voice voices[N_VOICES];
// Parameters // Parameters
float params[p_n]; float params[p_n];
@ -138,7 +141,7 @@ struct _bw_example_synth_poly {
float mod_wheel; float mod_wheel;
// Buffers // Buffers
float buf[4][BUFFER_SIZE]; float buf[BUFFER_SIZE];
}; };
typedef struct _bw_example_synth_poly bw_example_synth_poly; typedef struct _bw_example_synth_poly bw_example_synth_poly;

View File

@ -1,6 +1,6 @@
ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
NAME := bw_example_synth_mono NAME := bw_example_synth_poly
SOURCES = ${SOURCES_COMMON} ${ROOT_DIR}/../src/bw_example_synth_mono.c SOURCES = ${SOURCES_COMMON} ${ROOT_DIR}/../src/bw_example_synth_poly.c
include ${ROOT_DIR}/../../common/vst3/vst3.mk include ${ROOT_DIR}/../../common/vst3/vst3.mk

View File

@ -1,7 +1,7 @@
/* /*
* Brickworks * Brickworks
* *
* Copyright (C) 2022 Orastron Srl unipersonale * Copyright (C) 2023 Orastron Srl unipersonale
* *
* Brickworks is free software: you can redistribute it and/or modify * Brickworks is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -23,14 +23,14 @@
#define PLUGIN_SUBCATEGORY "Instrument|Synth" #define PLUGIN_SUBCATEGORY "Instrument|Synth"
#define PLUGIN_GUID_1 0x5af9b172 #define PLUGIN_GUID_1 0x14f4e502
#define PLUGIN_GUID_2 0x95ef439c #define PLUGIN_GUID_2 0xf2314c26
#define PLUGIN_GUID_3 0xb10ed6f0 #define PLUGIN_GUID_3 0xa89226a1
#define PLUGIN_GUID_4 0xb962eef1 #define PLUGIN_GUID_4 0xd539f201
#define CTRL_GUID_1 0xed4990b0 #define CTRL_GUID_1 0xd7917a95
#define CTRL_GUID_2 0x89894215 #define CTRL_GUID_2 0xb3e14394
#define CTRL_GUID_3 0x96fc7cda #define CTRL_GUID_3 0xa6c5bcb7
#define CTRL_GUID_4 0x5a56cec9 #define CTRL_GUID_4 0x852d78bb
#endif #endif

View File

@ -1,5 +1,5 @@
ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
SOURCES = ${SOURCES_COMMON} ${ROOT_DIR}/../src/bw_example_synth_mono.c SOURCES = ${SOURCES_COMMON} ${ROOT_DIR}/../src/bw_example_synth_poly.c
SYNTH := yes SYNTH := yes
NEEDS_MEMSET := yes NEEDS_MEMSET := yes

View File

@ -92,9 +92,10 @@ static inline void bw_note_queue_add(bw_note_queue *BW_RESTRICT queue, unsigned
* change at any time in future versions. Please, do not use it directly. */ * change at any time in future versions. Please, do not use it directly. */
static inline void bw_note_queue_reset(bw_note_queue *BW_RESTRICT queue) { static inline void bw_note_queue_reset(bw_note_queue *BW_RESTRICT queue) {
for (char i = 0; i < 128; i++) for (int i = 0; i < 128; i++)
queue->status[i] = { 0, 0.f }; queue->status[i] = (bw_note_queue_status){ .pressed = 0, .velocity = 0.f };
queue->n_pressed = 0; queue->n_pressed = 0;
queue->n_events = 0;
} }
static inline void bw_note_queue_clear(bw_note_queue *BW_RESTRICT queue) { static inline void bw_note_queue_clear(bw_note_queue *BW_RESTRICT queue) {
@ -113,14 +114,14 @@ static inline void bw_note_queue_add(bw_note_queue *BW_RESTRICT queue, unsigned
if (i == queue->n_events) if (i == queue->n_events)
queue->n_events++; queue->n_events++;
else else
went_off = queue->events[i].went_off || queue->events[i].velocity <= 0.f; went_off = queue->events[i].went_off || !queue->events[i].status.pressed;
queue->events[i] = { note, { pressed, velocity }, went_off || force_went_off }; queue->events[i] = (bw_note_queue_event){ .note = note, .status = { pressed, velocity }, .went_off = went_off || force_went_off };
if (pressed && !queue->status[note].pressed) if (pressed && !queue->status[note].pressed)
queue->n_pressed++; queue->n_pressed++;
else if (!pressed && queue->status[note].pressed) else if (!pressed && queue->status[note].pressed)
queue->n_pressed--; queue->n_pressed--;
queue->status[note] = { pressed, velocity }; queue->status[note] = (bw_note_queue_status){ .pressed = pressed, .velocity = velocity };
} }
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -51,7 +51,7 @@ extern "C" {
/*! api {{{ /*! api {{{
* #### bw_voice_alloc_mode * #### bw_voice_alloc_mode
* ```>>> */ * ```>>> */
typedef enum typedef enum {
bw_voice_alloc_mode_low, bw_voice_alloc_mode_low,
bw_voice_alloc_mode_high bw_voice_alloc_mode_high
} bw_voice_alloc_mode; } bw_voice_alloc_mode;
@ -92,7 +92,7 @@ void bw_voice_alloc(const bw_voice_alloc_opts *BW_RESTRICT opts, bw_note_queue *
if (ev->status.pressed) { if (ev->status.pressed) {
for (int j = 0; j < n_voices; j++) for (int j = 0; j < n_voices; j++)
if (opt->is_free(voices[j])) { if (opts->is_free(voices[j])) {
opts->note_on(voices[j], ev->note, ev->status.velocity); opts->note_on(voices[j], ev->note, ev->status.velocity);
goto next_event; goto next_event;
} }
@ -111,7 +111,7 @@ void bw_voice_alloc(const bw_voice_alloc_opts *BW_RESTRICT opts, bw_note_queue *
} }
next_event:; next_event:;
} }
} }
#ifdef __cplusplus #ifdef __cplusplus