use NDEBUG in examples + fix velocity in synth(pp)_poly + really remove bw_omega* + fix bw_math + fix high cutoff in bw_one_pole + debugging in bw_{note_queue,rand,voice_alloc}

This commit is contained in:
Stefano D'Angelo 2023-07-25 08:52:01 +02:00
parent 681008abdb
commit 8299cb7eae
10 changed files with 147 additions and 101 deletions

1
TODO
View File

@ -84,6 +84,7 @@ code:
* web effect mono->stereo and stereo->mono inputs? or display "input needs to be n channels"?
* fix vst3 mapped values (visible in Ableton Live) and short names
* polish examples (ranges, etc.)
* dump data structures (see note queue) and indicate error precisely
build system:
* make makefiles handle paths with spaces etc

View File

@ -12,11 +12,13 @@ SYSTEM_FILES_DIR = ${LIBDAISY_DIR}/core
include ${SYSTEM_FILES_DIR}/Makefile
CPPFLAGS += \
-DNDEBUG \
-I${ROOT_DIR} \
-I${ROOT_DIR}/../src \
-I${ROOT_DIR}/../../../include
CFLAGS += \
-DNDEBUG \
-I${ROOT_DIR} \
-I${ROOT_DIR}/../src \
-I${ROOT_DIR}/../../../include \

View File

@ -7,6 +7,7 @@ BUILD_PLUGIN_DIR := build/${NAME}.vst3
CXXFLAGS := \
-DRELEASE=1 \
-DNDEBUG \
-I${ROOT_DIR} \
-I${ROOT_DIR}/../src \
-I${COMMON_DIR} \

View File

@ -404,7 +404,7 @@ float bw_example_synth_poly_get_parameter(bw_example_synth_poly *instance, int i
}
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, velocity, 0);
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) {

View File

@ -375,7 +375,7 @@ float bw_example_synthpp_poly_get_parameter(bw_example_synthpp_poly *instance, i
}
void bw_example_synthpp_poly_note_on(bw_example_synthpp_poly *instance, char note, char velocity) {
instance->noteQueue.add(note, velocity != 0, velocity, false);
instance->noteQueue.add(note, velocity != 0, (1.f / 127.f) * velocity, false);
}
void bw_example_synthpp_poly_note_off(bw_example_synthpp_poly *instance, char note) {

View File

@ -371,28 +371,6 @@ static inline float bw_lin2dBf_3(float x);
*
* Absolute error < 0.032, relative error < 1.5%.
*
* #### bw_omega_3log()
* ```>>> */
static inline float bw_omega_3log(float x);
/*! <<<```
* Returns an approximation of `omega(x)`, where `omega` is the <a
* href="https://en.wikipedia.org/wiki/Wright_omega_function"
* target="_blank">Wright omega function</a>.
*
* Absolute error < 0.27, relative error < 14.6% for non-negative input and
* decreasing with increasing input, goes rapidly to 100% for progressively
* more negative input.
*
* #### bw_omega_3lognr()
* ```>>> */
static inline float bw_omega_3lognr(float x);
/*! <<<```
* Returns an approximation of `omega(x)`, where `omega` is the <a
* href="https://en.wikipedia.org/wiki/Wright_omega_function"
* target="_blank">Wright omega function</a>.
*
* Absolute error < 0.045, relative error < 3.7%.
*
* #### bw_sqrtf_2()
* ```>>> */
static inline float bw_sqrtf_2(float x);
@ -508,57 +486,57 @@ static inline float bw_absf(float x) {
}
static inline float bw_min0xf(float x) {
BW_ASSERT(bw_isfinite(x));
BW_ASSERT(bw_is_finite(x));
const float y = 0.5f * (x - bw_absf(x));
BW_ASSERT(bw_isfinite(y));
BW_ASSERT(bw_is_finite(y));
return y;
}
static inline float bw_max0xf(float x) {
BW_ASSERT(bw_isfinite(x));
BW_ASSERT(bw_is_finite(x));
const float y = 0.5f * (x + bw_absf(x));
BW_ASSERT(bw_isfinite(y));
BW_ASSERT(bw_is_finite(y));
return y;
}
static inline float bw_minf(float a, float b) {
BW_ASSERT(bw_isfinite(a));
BW_ASSERT(bw_isfinite(b));
BW_ASSERT(bw_is_finite(a));
BW_ASSERT(bw_is_finite(b));
const float y = a + bw_min0xf(b - a);
BW_ASSERT(bw_isfinite(y));
BW_ASSERT(bw_is_finite(y));
return y;
}
static inline float bw_maxf(float a, float b) {
BW_ASSERT(bw_isfinite(a));
BW_ASSERT(bw_isfinite(b));
BW_ASSERT(bw_is_finite(a));
BW_ASSERT(bw_is_finite(b));
const float y = a + bw_max0xf(b - a);
BW_ASSERT(bw_isfinite(y));
BW_ASSERT(bw_is_finite(y));
return y;
}
static inline float bw_clipf(float x, float m, float M) {
BW_ASSERT(bw_isfinite(x));
BW_ASSERT(bw_isfinite(m));
BW_ASSERT(bw_isfinite(M));
BW_ASSERT(bw_is_finite(x));
BW_ASSERT(bw_is_finite(m));
BW_ASSERT(bw_is_finite(M));
const float y = bw_minf(bw_maxf(x, m), M);
BW_ASSERT(bw_isfinite(y));
BW_ASSERT(bw_is_finite(y));
return y;
}
static inline float bw_truncf(float x) {
BW_ASSERT(bw_isfinite(x));
BW_ASSERT(bw_is_finite(x));
_bw_floatint v = {.f = x};
int32_t ex = (v.i & 0x7f800000) >> 23;
int32_t m = (~0u) << bw_clipi32(150 - ex, 0, 23);
m &= bw_signfilli32(126 - ex) | 0x80000000;
v.i &= m;
BW_ASSERT(bw_isfinite(v.f));
BW_ASSERT(bw_is_finite(v.f));
return v.f;
}
static inline float bw_roundf(float x) {
BW_ASSERT(bw_isfinite(x));
BW_ASSERT(bw_is_finite(x));
_bw_floatint v = {.f = x};
int32_t ex = (v.i & 0x7f800000) >> 23;
int32_t sh = bw_clipi32(150 - ex, 0, 23);
@ -571,38 +549,38 @@ static inline float bw_roundf(float x) {
v.i &= mt;
s.i &= ms;
const float y = v.f + s.f;
BW_ASSERT(bw_isfinite(y));
BW_ASSERT(bw_is_finite(y));
return y;
}
static inline float bw_floorf(float x) {
BW_ASSERT(bw_isfinite(x));
BW_ASSERT(bw_is_finite(x));
_bw_floatint t = {.f = bw_truncf(x)}; // first bit set when t < 0
_bw_floatint y = {.f = x - t.f}; // first bit set when t > x
_bw_floatint s = {.f = 1.f};
s.i &= bw_signfilli32(t.i & y.i);
const float y = t.f - s.f;
BW_ASSERT(bw_isfinite(y));
return y;
const float r = t.f - s.f;
BW_ASSERT(bw_is_finite(r));
return r;
}
static inline float bw_ceilf(float x) {
BW_ASSERT(bw_isfinite(x));
BW_ASSERT(bw_is_finite(x));
_bw_floatint t = {.f = bw_truncf(x)}; // first bit set when t < 0
_bw_floatint y = {.f = x - t.f}; // first bit set when t > x
_bw_floatint s = {.f = 1.f};
s.i &= bw_signfilli32(~t.i & y.i);
const float y = t.f + s.f;
BW_ASSERT(bw_isfinite(y));
return y;
const float r = t.f + s.f;
BW_ASSERT(bw_is_finite(r));
return r;
}
static inline void bw_intfracf(float x, float *i, float *f) {
BW_ASSERT(bw_isfinite(x));
BW_ASSERT(bw_is_finite(x));
*i = bw_floorf(x);
*f = x - *i;
BW_ASSERT(bw_isfinite(*i));
BW_ASSERT(bw_isfinite(*f));
BW_ASSERT(bw_is_finite(*i));
BW_ASSERT(bw_is_finite(*f));
}
static inline float bw_rcpf_2(float x) {
@ -614,85 +592,85 @@ static inline float bw_rcpf_2(float x) {
}
static inline float bw_sin2pif_3(float x) {
BW_ASSERT(bw_isfinite(x));
BW_ASSERT(bw_is_finite(x));
x = x - bw_floorf(x);
float xp1 = x + x - 1.f;
float xp2 = bw_absf(xp1);
float xp = 1.570796326794897f - 1.570796326794897f * bw_absf(xp2 + xp2 - 1.f);
const float y = -bw_copysignf(1.f, xp1) * (xp + xp * xp * (-0.05738534102710938f - 0.1107398163618408f * xp));
BW_ASSERT(bw_isfinite(y));
BW_ASSERT(bw_is_finite(y));
return y;
}
static inline float bw_sinf_3(float x) {
BW_ASSERT(bw_isfinite(x));
BW_ASSERT(bw_is_finite(x));
const float y = bw_sin2pif_3(0.1591549430918953f * x);
BW_ASSERT(bw_isfinite(y));
BW_ASSERT(bw_is_finite(y));
return y;
}
static inline float bw_cos2pif_3(float x) {
BW_ASSERT(bw_isfinite(x));
return bw_sin2pif_3(x + 0.25f);
BW_ASSERT(bw_isfinite(y));
BW_ASSERT(bw_is_finite(x));
const float y = bw_sin2pif_3(x + 0.25f);
BW_ASSERT(bw_is_finite(y));
return y;
}
static inline float bw_cosf_3(float x) {
BW_ASSERT(bw_isfinite(x));
return bw_cos2pif_3(0.1591549430918953f * x);
BW_ASSERT(bw_isfinite(y));
BW_ASSERT(bw_is_finite(x));
const float y = bw_cos2pif_3(0.1591549430918953f * x);
BW_ASSERT(bw_is_finite(y));
return y;
}
static inline float bw_tan2pif_3(float x) {
BW_ASSERT(bw_isfinite(x));
BW_ASSERT(bw_is_finite(x));
BW_ASSERT((x - 0.5f * bw_floorf(x + x) <= 0.249840845056908f)
|| (x - 0.5f * bw_floorf(x + x) >= 0.250159154943092f));
return bw_sin2pif_3(x) * bw_rcpf_2(bw_cos2pif_3(x));
BW_ASSERT(bw_isfinite(y));
const float y = bw_sin2pif_3(x) * bw_rcpf_2(bw_cos2pif_3(x));
BW_ASSERT(bw_is_finite(y));
return y;
}
static inline float bw_tanf_3(float x) {
BW_ASSERT(bw_isfinite(x));
BW_ASSERT(bw_is_finite(x));
BW_ASSERT((x - 3.141592653589793f * bw_floorf(0.318309886183791f * x) <= 1.569796326794897f)
|| (x - 3.141592653589793f * bw_floorf(0.318309886183791f * x) >= 1.571796326794896f));
x = 0.1591549430918953f * x;
const float y = bw_sin2pif_3(x) * bw_rcpf_2(bw_cos2pif_3(x));
BW_ASSERT(bw_isfinite(y));
BW_ASSERT(bw_is_finite(y));
return y;
}
static inline float bw_log2f_3(float x) {
BW_ASSERT(bw_isfinite(x));
BW_ASSERT(bw_is_finite(x));
BW_ASSERT(x >= 1.175494350822287e-38f);
_bw_floatint v = {.f = x};
int e = v.i >> 23;
v.i = (v.i & 0x007fffff) | 0x3f800000;
const float y = (float)e - 129.213475204444817f + v.f * (3.148297929334117f + v.f * (-1.098865286222744f + v.f * 0.1640425613334452f));
BW_ASSERT(bw_isfinite(y));
BW_ASSERT(bw_is_finite(y));
return y;
}
static inline float bw_logf_3(float x) {
BW_ASSERT(bw_isfinite(x));
BW_ASSERT(bw_is_finite(x));
BW_ASSERT(x >= 1.175494350822287e-38f);
const float y = 0.693147180559945f * bw_log2f_3(x);
BW_ASSERT(bw_isfinite(y));
BW_ASSERT(bw_is_finite(y));
return y;
}
static inline float bw_log10f_3(float x) {
BW_ASSERT(bw_isfinite(x));
BW_ASSERT(bw_is_finite(x));
BW_ASSERT(x >= 1.175494350822287e-38f);
const float y = 0.3010299956639811f * bw_log2f_3(x);
BW_ASSERT(bw_isfinite(y));
BW_ASSERT(bw_is_finite(y));
return y;
}
static inline float bw_pow2f_3(float x) {
BW_ASSERT(bw_isfinite(x));
BW_ASSERT(bw_is_finite(x));
BW_ASSERT(x <= 127.999f);
if (x < -126.f)
return 0.f;
@ -702,48 +680,48 @@ static inline float bw_pow2f_3(float x) {
float f = x - (float)l;
v.i = (l + 127) << 23;
const float y = v.f + v.f * f * (0.6931471805599453f + f * (0.2274112777602189f + f * 0.07944154167983575f));
BW_ASSERT(bw_isfinite(y));
BW_ASSERT(bw_is_finite(y));
return y;
}
static inline float bw_expf_3(float x) {
BW_ASSERT(bw_isfinite(x));
BW_ASSERT(bw_is_finite(x));
BW_ASSERT(x <= 88.722f);
const float y = bw_pow2f_3(1.442695040888963f * x);
BW_ASSERT(bw_isfinite(y));
BW_ASSERT(bw_is_finite(y));
return y;
}
static inline float bw_pow10f_3(float x) {
BW_ASSERT(bw_isfinite(x));
BW_ASSERT(bw_is_finite(x));
BW_ASSERT(x <= 38.531f);
const float y = bw_pow2f_3(3.321928094887363f * x);
BW_ASSERT(bw_isfinite(y));
BW_ASSERT(bw_is_finite(y));
return y;
}
static inline float bw_dB2linf_3(float x) {
BW_ASSERT(bw_isfinite(x));
BW_ASSERT(bw_is_finite(x));
BW_ASSERT(x <= 770.630f);
const float y = bw_pow2f_3(0.1660964047443682f * x);
BW_ASSERT(bw_isfinite(y));
BW_ASSERT(bw_is_finite(y));
return y;
}
static inline float bw_lin2dBf_3(float x) {
BW_ASSERT(bw_isfinite(x));
BW_ASSERT(bw_is_finite(x));
BW_ASSERT(x >= 1.175494350822287e-38f);
const float y = 20.f * bw_log10f_3(x);
BW_ASSERT(bw_isfinite(y));
BW_ASSERT(bw_is_finite(y));
return y;
}
static inline float bw_sqrtf_2(float x) {
BW_ASSERT(bw_isfinite(x));
BW_ASSERT(bw_is_finite(x));
BW_ASSERT(x >= 0.f);
if (x < 8.077935669463161e-28f) {
const float y = 3.518437208883201e13f * x;
BW_ASSERT(bw_isfinite(y));
BW_ASSERT(bw_is_finite(y));
return y;
}
_bw_floatint v = {.f = x};
@ -751,49 +729,49 @@ static inline float bw_sqrtf_2(float x) {
float r = bw_rcpf_2(x);
v.f = v.f + v.f * (0.5f - 0.5f * r * v.f * v.f);
v.f = v.f + v.f * (0.5f - 0.5f * r * v.f * v.f);
BW_ASSERT(bw_isfinite(v.f));
BW_ASSERT(bw_is_finite(v.f));
return v.f;
}
static inline float bw_tanhf_3(float x) {
BW_ASSERT(bw_isfinite(x));
BW_ASSERT(bw_is_finite(x));
const float xm = bw_clipf(x, -2.115287308554551f, 2.115287308554551f);
const float axm = bw_absf(xm);
const float y = xm * axm * (0.01218073260037716f * axm - 0.2750231331124371f) + xm;
BW_ASSERT(bw_isfinite(y));
BW_ASSERT(bw_is_finite(y));
return y;
}
static inline float bw_sinhf_3(float x) {
BW_ASSERT(bw_isfinite(x));
BW_ASSERT(bw_is_finite(x));
BW_ASSERT(x >= -88.722f && x <= 88.722f);
const float y = 0.5f * (bw_expf_3(x) - bw_expf_3(-x));
BW_ASSERT(bw_isfinite(y));
BW_ASSERT(bw_is_finite(y));
return y;
}
static inline float bw_coshf_3(float x) {
BW_ASSERT(bw_isfinite(x));
BW_ASSERT(bw_is_finite(x));
BW_ASSERT(x >= -88.722f && x <= 88.722f);
const float y = 0.5f * (bw_expf_3(x) + bw_expf_3(-x));
BW_ASSERT(bw_isfinite(y));
BW_ASSERT(bw_is_finite(y));
return y;
}
static inline float bw_asinhf_3(float x) {
BW_ASSERT(bw_isfinite(x));
BW_ASSERT(bw_is_finite(x));
BW_ASSERT(x >= -1.7e38f && x <= 1.7e38f);
float a = bw_absf(x);
const float y = bw_copysignf(bw_logf_3((a >= 4096.f ? a : bw_sqrtf_2(a * a + 1.f)) + a), x);
BW_ASSERT(bw_isfinite(y));
BW_ASSERT(bw_is_finite(y));
return y;
}
static inline float bw_acoshf_3(float x) {
BW_ASSERT(bw_isfinite(x));
BW_ASSERT(bw_is_finite(x));
BW_ASSERT(x >= 1.f);
const float y = bw_logf_3((x >= 8192.f ? x : bw_sqrtf_2(x * x - 1.f)) + x);
BW_ASSERT(bw_isfinite(y));
BW_ASSERT(bw_is_finite(y));
return y;
}

View File

@ -32,6 +32,7 @@
* <ul>
* <li>Version <strong>0.6.0</strong>:
* <ul>
* <li>Added debugging code.</li>
* <li>Removed dependency on bw_config.</li>
* </ul>
* </li>
@ -121,6 +122,12 @@ static inline void bw_note_queue_add(bw_note_queue *BW_RESTRICT queue, unsigned
*
* If `force_went_off` is set to non-`0`, `went_off` is always set to
* non-`0`.
*
* #### bw_note_queue_add()
* ```>>> */
static inline char bw_note_queue_is_valid(bw_note_queue *BW_RESTRICT queue);
/*! <<<```
* WRITEME
* }}} */
/*** Implementation ***/
@ -129,17 +136,26 @@ 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. */
static inline void bw_note_queue_reset(bw_note_queue *BW_RESTRICT queue) {
BW_ASSERT(queue != NULL);
for (int i = 0; i < 128; i++)
queue->status[i] = (bw_note_queue_status){ .pressed = 0, .velocity = 0.f };
queue->n_pressed = 0;
queue->n_events = 0;
BW_ASSERT_DEEP(bw_note_queue_is_valid(queue));
}
static inline void bw_note_queue_clear(bw_note_queue *BW_RESTRICT queue) {
BW_ASSERT(queue != NULL);
BW_ASSERT_DEEP(bw_note_queue_is_valid(queue));
queue->n_events = 0;
}
static inline void bw_note_queue_add(bw_note_queue *BW_RESTRICT queue, unsigned char note, char pressed, float velocity, char force_went_off) {
BW_ASSERT(queue != NULL);
BW_ASSERT_DEEP(bw_note_queue_is_valid(queue));
BW_ASSERT(note < 128);
BW_ASSERT(bw_is_finite(velocity) && velocity <= 1.f);
if (!pressed && !queue->status[note].pressed)
return;
@ -159,6 +175,37 @@ static inline void bw_note_queue_add(bw_note_queue *BW_RESTRICT queue, unsigned
else if (!pressed && queue->status[note].pressed)
queue->n_pressed--;
queue->status[note] = (bw_note_queue_status){ .pressed = pressed, .velocity = velocity };
BW_ASSERT_DEEP(bw_note_queue_is_valid(queue));
}
static inline char bw_note_queue_is_valid(bw_note_queue *BW_RESTRICT queue) {
BW_ASSERT(queue != NULL);
if (queue->n_events >= 128 || queue->n_pressed >= 128)
return 0;
for (int i = 0; i < (int)queue->n_events; i++) {
bw_note_queue_event *ev = queue->events + i;
if (ev->note >= 128 || !bw_is_finite(ev->status.velocity) || ev->status.velocity > 1.f)
return 0;
for (int j = 0; j < i; j++) {
bw_note_queue_event *ev2 = queue->events + j;
if (ev2->note == ev->note)
return 0;
}
}
int cnt = 0;
for (int i = 0; i < 128; i++) {
bw_note_queue_status *st = queue->status + i;
if (st->pressed)
cnt++;
if (!bw_is_finite(st->velocity) || st->velocity > 1.f)
return 0;
}
return cnt == queue->n_pressed;
}
#ifdef __cplusplus

View File

@ -33,6 +33,7 @@
* <li>Version <strong>0.6.0</strong>:
* <ul>
* <li>Removed dependency on bw_config.</li>
* <li>Fixed bug when setting very high cutoff values.</li>
* </ul>
* </li>
* <li>Version <strong>0.5.0</strong>:
@ -346,9 +347,11 @@ static inline void bw_one_pole_reset_state(const bw_one_pole_coeffs *BW_RESTRICT
static inline void bw_one_pole_update_coeffs_ctrl(bw_one_pole_coeffs *BW_RESTRICT coeffs) {
if (coeffs->param_changed) {
if (coeffs->param_changed & _BW_ONE_POLE_PARAM_CUTOFF_UP)
coeffs->mA1u = bw_expf_3(coeffs->Ttm2pi * coeffs->cutoff_up);
coeffs->mA1u = coeffs->cutoff_up > 1.591549430918953e8f ? 0.f : bw_expf_3(coeffs->Ttm2pi * coeffs->cutoff_up);
// tau < 1 ns is instantaneous for any practical purpose
if (coeffs->param_changed & _BW_ONE_POLE_PARAM_CUTOFF_DOWN)
coeffs->mA1d = bw_expf_3(coeffs->Ttm2pi * coeffs->cutoff_down);
coeffs->mA1d = coeffs->cutoff_down > 1.591549430918953e8f ? 0.f : bw_expf_3(coeffs->Ttm2pi * coeffs->cutoff_down);
// as before
if (coeffs->param_changed & _BW_ONE_POLE_PARAM_STICKY_THRESH)
coeffs->st2 = coeffs->sticky_thresh * coeffs->sticky_thresh;
coeffs->param_changed = 0;

View File

@ -36,6 +36,7 @@
* <ul>
* <li>Version <strong>0.6.0</strong>:
* <ul>
* <li>Added debugging code.</li>
* <li>Removed dependency on bw_config.</li>
* </ul>
* </li>
@ -90,6 +91,7 @@ static inline float bw_randf(uint64_t *BW_RESTRICT state);
* change at any time in future versions. Please, do not use it directly. */
static inline uint32_t bw_randu32(uint64_t *BW_RESTRICT state) {
BW_ASSERT(state != NULL);
// Permuted Congruential Generator,
// taken from https://nullprogram.com/blog/2017/09/21/
*state = *state * 0x9b60933458e17d7d + 0xd737232eeccdf7ed;
@ -97,6 +99,7 @@ static inline uint32_t bw_randu32(uint64_t *BW_RESTRICT state) {
}
static inline float bw_randf(uint64_t *BW_RESTRICT state) {
BW_ASSERT(state != NULL);
return (2.f / (float)UINT32_MAX) * (float)bw_randu32(state) - 1.f;
}

View File

@ -29,6 +29,7 @@
* <ul>
* <li>Version <strong>0.6.0</strong>:
* <ul>
* <li>Added debugging code.</li>
* <li>Removed dependency on bw_config.</li>
* </ul>
* </li>
@ -88,7 +89,7 @@ typedef struct {
*
* #### bw_voice_alloc()
* ```>>> */
void bw_voice_alloc(const bw_voice_alloc_opts *BW_RESTRICT opts, bw_note_queue *BW_RESTRICT queue, void **BW_RESTRICT voices, int n_voices);
void bw_voice_alloc(const bw_voice_alloc_opts *BW_RESTRICT opts, bw_note_queue *BW_RESTRICT queue, void **BW_RESTRICT voices, BW_SIZE_T n_voices);
/*! <<<```
* It performs voice allocation according to `opts` and using the events in
* `queue`.
@ -102,7 +103,17 @@ void bw_voice_alloc(const bw_voice_alloc_opts *BW_RESTRICT opts, bw_note_queue *
/* WARNING: This part of the file is not part of the public API. Its content may
* change at any time in future versions. Please, do not use it directly. */
void bw_voice_alloc(const bw_voice_alloc_opts *BW_RESTRICT opts, bw_note_queue *BW_RESTRICT queue, void **BW_RESTRICT voices, int n_voices) {
void bw_voice_alloc(const bw_voice_alloc_opts *BW_RESTRICT opts, bw_note_queue *BW_RESTRICT queue, void **BW_RESTRICT voices, BW_SIZE_T n_voices) {
BW_ASSERT(opts != NULL);
BW_ASSERT(opts->priority == bw_voice_alloc_priority_low || opts->priority == bw_voice_alloc_priority_high);
BW_ASSERT(n_voices == 0 || opts->note_on != NULL);
BW_ASSERT(n_voices == 0 || opts->note_off != NULL);
BW_ASSERT(n_voices == 0 || opts->get_note != NULL);
BW_ASSERT(n_voices == 0 || opts->is_free != NULL);
BW_ASSERT(queue != NULL);
BW_ASSERT_DEEP(bw_note_queue_is_valid(queue));
BW_ASSERT(n_voices == 0 || voices != NULL);
for (unsigned char i = 0; i < queue->n_events; i++) {
bw_note_queue_event *ev = queue->events + i;
for (int j = 0; j < n_voices; j++)