Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion shared-module/audiodelays/Chorus.c
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ audioio_get_buffer_result_t audiodelays_chorus_get_buffer(audiodelays_chorus_obj
int32_t voices = (int32_t)MAX(synthio_block_slot_get(&self->voices), 1.0);
int32_t mix_down_scale = SYNTHIO_MIX_DOWN_SCALE(voices);
mp_float_t mix = synthio_block_slot_get_limited(&self->mix, MICROPY_FLOAT_CONST(0.0), MICROPY_FLOAT_CONST(1.0));
int32_t mix_scaled = (int32_t)(mix * MICROPY_FLOAT_CONST(32768.0));

mp_float_t f_delay_ms = synthio_block_slot_get(&self->delay_ms);
if (MICROPY_FLOAT_C_FUN(fabs)(self->current_delay_ms - f_delay_ms) >= self->sample_ms) {
Expand Down Expand Up @@ -311,7 +312,7 @@ audioio_get_buffer_result_t audiodelays_chorus_get_buffer(audiodelays_chorus_obj
}

// Add original sample + effect
word = sample_word + (int32_t)(word * mix);
word = sample_word + ((word * mix_scaled) >> 15);
word = synthio_mix_down_sample(word, 2);

if (MP_LIKELY(self->base.bits_per_sample == 16)) {
Expand Down
20 changes: 12 additions & 8 deletions shared-module/audiodelays/Echo.c
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,11 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t *
mp_float_t mix = synthio_block_slot_get_limited(&self->mix, MICROPY_FLOAT_CONST(0.0), MICROPY_FLOAT_CONST(1.0)) * MICROPY_FLOAT_CONST(2.0);
mp_float_t decay = synthio_block_slot_get_limited(&self->decay, MICROPY_FLOAT_CONST(0.0), MICROPY_FLOAT_CONST(1.0));

int32_t decay_scaled = (int32_t)(decay * MICROPY_FLOAT_CONST(32768.0));
int32_t echo_scaled = (int32_t)(MIN(mix, MICROPY_FLOAT_CONST(1.0)) * MICROPY_FLOAT_CONST(32768.0));
int32_t sample_scaled = (int32_t)(MIN(MICROPY_FLOAT_CONST(2.0) - mix,
MICROPY_FLOAT_CONST(1.0)) * MICROPY_FLOAT_CONST(32768.0));

mp_float_t f_delay_ms = synthio_block_slot_get(&self->delay_ms);
if (MICROPY_FLOAT_C_FUN(fabs)(self->current_delay_ms - f_delay_ms) >= self->sample_ms) {
recalculate_delay(self, f_delay_ms);
Expand Down Expand Up @@ -340,16 +345,16 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t *
next_buffer_pos = echo_buffer_pos + self->echo_buffer_rate;

for (uint32_t j = echo_buffer_pos >> 8; j < next_buffer_pos >> 8; j++) {
word = (int16_t)(echo_buffer[(j % echo_buf_len) + echo_buffer_offset] * decay);
word = (int16_t)((echo_buffer[(j % echo_buf_len) + echo_buffer_offset] * decay_scaled) >> 15);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
word = (int16_t)((echo_buffer[(j % echo_buf_len) + echo_buffer_offset] * decay_scaled) >> 15);
word = synthio_sat16(echo_buffer[(j % echo_buf_len) + echo_buffer_offset] * decay_scaled, 15);

echo_buffer[(j % echo_buf_len) + echo_buffer_offset] = (int16_t)audiofilters_process_filter_chain(&self->filter, self->base.channel_count, !!echo_buffer_offset, word);
}
} else {
echo = echo_buffer[echo_buffer_pos + echo_buffer_offset];
word = (int16_t)(echo * decay);
word = (int16_t)((echo * decay_scaled) >> 15);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
word = (int16_t)((echo * decay_scaled) >> 15);
word = synthio_sat16(echo * decay_scaled, 15);

echo_buffer[echo_buffer_pos++ + echo_buffer_offset] = (int16_t)audiofilters_process_filter_chain(&self->filter, self->base.channel_count, !!echo_buffer_offset, word);
}

word = (int16_t)(echo * MIN(mix, MICROPY_FLOAT_CONST(1.0)));
word = (int16_t)((echo * echo_scaled) >> 15);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
word = (int16_t)((echo * echo_scaled) >> 15);
word = synthio_sat16(echo * echo_scaled, 15);


if (MP_LIKELY(self->base.bits_per_sample == 16)) {
word_buffer[i] = word;
Expand Down Expand Up @@ -418,13 +423,13 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t *
next_buffer_pos = echo_buffer_pos + self->echo_buffer_rate;
} else {
echo = echo_buffer[echo_buffer_pos + echo_buffer_offset];
word = (int32_t)(echo * decay + sample_word);
word = ((echo * decay_scaled) >> 15) + sample_word;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
word = ((echo * decay_scaled) >> 15) + sample_word;
synthio_sat16(echo * decay_scaled, 15) + sample_word;

}

if (MP_LIKELY(self->base.bits_per_sample == 16)) {
if (self->freq_shift) {
for (uint32_t j = echo_buffer_pos >> 8; j < next_buffer_pos >> 8; j++) {
word = (int32_t)(echo_buffer[(j % echo_buf_len) + echo_buffer_offset] * decay + sample_word);
word = ((echo_buffer[(j % echo_buf_len) + echo_buffer_offset] * decay_scaled) >> 15) + sample_word;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
word = ((echo_buffer[(j % echo_buf_len) + echo_buffer_offset] * decay_scaled) >> 15) + sample_word;
word = synthio_sat16(echo_buffer[(j % echo_buf_len) + echo_buffer_offset] * decay_scaled, 15) + sample_word;

word = synthio_mix_down_sample(word, SYNTHIO_MIX_DOWN_SCALE(2));
echo_buffer[(j % echo_buf_len) + echo_buffer_offset] = (int16_t)audiofilters_process_filter_chain(&self->filter, self->base.channel_count, !!echo_buffer_offset, word);
}
Expand All @@ -435,7 +440,7 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t *
} else {
if (self->freq_shift) {
for (uint32_t j = echo_buffer_pos >> 8; j < next_buffer_pos >> 8; j++) {
word = (int32_t)(echo_buffer[(j % echo_buf_len) + echo_buffer_offset] * decay + sample_word);
word = ((echo_buffer[(j % echo_buf_len) + echo_buffer_offset] * decay_scaled) >> 15) + sample_word;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
word = ((echo_buffer[(j % echo_buf_len) + echo_buffer_offset] * decay_scaled) >> 15) + sample_word;
word = synthio_sat16(echo_buffer[(j % echo_buf_len) + echo_buffer_offset] * decay_scaled, 15) + sample_word;

// Do not have mix_down for 8 bit so just hard cap samples into 1 byte
word = MIN(MAX(word, -128), 127);
echo_buffer[(j % echo_buf_len) + echo_buffer_offset] = (int8_t)audiofilters_process_filter_chain(&self->filter, self->base.channel_count, !!echo_buffer_offset, word);
Expand All @@ -447,8 +452,7 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t *
}
}

word = (int32_t)((sample_word * MIN(MICROPY_FLOAT_CONST(2.0) - mix, MICROPY_FLOAT_CONST(1.0)))
+ (echo * MIN(mix, MICROPY_FLOAT_CONST(1.0))));
word = ((sample_word * sample_scaled) >> 15) + ((echo * echo_scaled) >> 15);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This version shifts and truncates each separately before adding. It could instead sum first and then shift to stay more precise.

Suggested change
word = ((sample_word * sample_scaled) >> 15) + ((echo * echo_scaled) >> 15);
word = synthio_shift_round_to_zero(sample_word * sample_scaled + echo * echo_scaled, 15);

This is the helper function that could be added to synthio/init.h so it can be used here and a few other places.

static inline int32_t synthio_shift_round_to_zero(int32_t n, int rshift) {
    if (n < 0) {
        n += (1 << rshift) - 1;
    }
    return n >> rshift;
}

word = synthio_mix_down_sample(word, SYNTHIO_MIX_DOWN_SCALE(2));

if (MP_LIKELY(self->base.bits_per_sample == 16)) {
Expand Down
10 changes: 7 additions & 3 deletions shared-module/audiodelays/MultiTapDelay.c
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,11 @@ audioio_get_buffer_result_t audiodelays_multi_tap_delay_get_buffer(audiodelays_m
mp_float_t mix = synthio_block_slot_get_limited(&self->mix, MICROPY_FLOAT_CONST(0.0), MICROPY_FLOAT_CONST(1.0)) * MICROPY_FLOAT_CONST(2.0);
mp_float_t decay = synthio_block_slot_get_limited(&self->decay, MICROPY_FLOAT_CONST(0.0), MICROPY_FLOAT_CONST(1.0));

int32_t decay_scaled = (int32_t)(decay * MICROPY_FLOAT_CONST(32768.0));
int32_t tap_scaled = (int32_t)(MIN(mix, MICROPY_FLOAT_CONST(1.0)) * MICROPY_FLOAT_CONST(32768.0));
int32_t sample_scaled = (int32_t)(MIN(MICROPY_FLOAT_CONST(2.0) - mix,
MICROPY_FLOAT_CONST(1.0)) * MICROPY_FLOAT_CONST(32768.0));

int16_t *sample_src = NULL;
int8_t *sample_hsrc = NULL;
if (self->sample != NULL) {
Expand Down Expand Up @@ -424,7 +429,7 @@ audioio_get_buffer_result_t audiodelays_multi_tap_delay_get_buffer(audiodelays_m
}

// Apply decay and add sample
delay_word = (int32_t)(delay_word * decay) + sample_word;
delay_word = ((delay_word * decay_scaled) >> 15) + sample_word;

@FoamyGuy FoamyGuy Sep 21, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should use synthio_sat16(). The old code rounds toward zero, but the new code always goes toward -infinity. The helper should resolve it.

delay_word = synthio_sat16(delay_word * decay_scaled, 15) + sample_word;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
delay_word = ((delay_word * decay_scaled) >> 15) + sample_word;
delay_word = synthio_sat16(delay_word * decay_scaled, 15) + sample_word;


if (MP_LIKELY(self->base.bits_per_sample == 16)) {
delay_word = synthio_mix_down_sample(delay_word, SYNTHIO_MIX_DOWN_SCALE(2));
Expand All @@ -436,8 +441,7 @@ audioio_get_buffer_result_t audiodelays_multi_tap_delay_get_buffer(audiodelays_m
}

// Mix sample with tap output
word = (int32_t)((sample_word * MIN(MICROPY_FLOAT_CONST(2.0) - mix, MICROPY_FLOAT_CONST(1.0)))
+ (word * MIN(mix, MICROPY_FLOAT_CONST(1.0))));
word = ((sample_word * sample_scaled) >> 15) + ((word * tap_scaled) >> 15);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
word = ((sample_word * sample_scaled) >> 15) + ((word * tap_scaled) >> 15);
word = synthio_shift_round_to_zero(sample_word * sample_scaled + word * tap_scaled, 15);

word = synthio_mix_down_sample(word, SYNTHIO_MIX_DOWN_SCALE(2));

if (MP_LIKELY(self->base.bits_per_sample == 16)) {
Expand Down
9 changes: 6 additions & 3 deletions shared-module/audiofilters/Filter.c
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,9 @@ audioio_get_buffer_result_t audiofilters_filter_get_buffer(audiofilters_filter_o
shared_bindings_synthio_lfo_tick(self->base.sample_rate, n / self->base.channel_count);
mp_float_t mix = synthio_block_slot_get_limited(&self->mix, MICROPY_FLOAT_CONST(0.0), MICROPY_FLOAT_CONST(1.0));

int32_t wet_scaled = (int32_t)(mix * MICROPY_FLOAT_CONST(32768.0));
int32_t dry_scaled = (int32_t)((MICROPY_FLOAT_CONST(1.0) - mix) * MICROPY_FLOAT_CONST(32768.0));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
int32_t dry_scaled = (int32_t)((MICROPY_FLOAT_CONST(1.0) - mix) * MICROPY_FLOAT_CONST(32768.0));
int32_t dry_scaled = 32768 - wet_scaled;

Both wet and dry truncate toward zero. Could cause off by 1 error when they're summed.


if (mix <= MICROPY_FLOAT_CONST(0.01) || !self->filter.states) { // if mix is zero pure sample only or no biquad filter objects are provided
for (uint32_t i = 0; i < n; i++) {
if (MP_LIKELY(self->base.bits_per_sample == 16)) {
Expand Down Expand Up @@ -238,15 +241,15 @@ audioio_get_buffer_result_t audiofilters_filter_get_buffer(audiofilters_filter_o
bool buf_offset = (j % self->base.channel_count) == 1;
uint32_t k = j / self->base.channel_count;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section has different behavior rounding always down instead of toward zero. It should use the new helper function.

Add here:

int32_t filtered_word = self->filter_buffer[k + SYNTHIO_MAX_DUR * buf_offset];

if (MP_LIKELY(self->base.bits_per_sample == 16)) {
word_buffer[i + j] = synthio_mix_down_sample((int32_t)((sample_src[i + j] * (MICROPY_FLOAT_CONST(1.0) - mix)) + (self->filter_buffer[k + SYNTHIO_MAX_DUR * buf_offset] * mix)), SYNTHIO_MIX_DOWN_SCALE(2));
word_buffer[i + j] = synthio_mix_down_sample(((sample_src[i + j] * dry_scaled) >> 15) + ((self->filter_buffer[k + SYNTHIO_MAX_DUR * buf_offset] * wet_scaled) >> 15), SYNTHIO_MIX_DOWN_SCALE(2));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
word_buffer[i + j] = synthio_mix_down_sample(((sample_src[i + j] * dry_scaled) >> 15) + ((self->filter_buffer[k + SYNTHIO_MAX_DUR * buf_offset] * wet_scaled) >> 15), SYNTHIO_MIX_DOWN_SCALE(2));
word_buffer[i + j] = synthio_mix_down_sample(synthio_shift_round_to_zero(sample_src[i + j] * dry_scaled + filtered_word * wet_scaled, 15), SYNTHIO_MIX_DOWN_SCALE(2));

if (!self->base.samples_signed) {
word_buffer[i + j] ^= 0x8000;
}
} else {
if (self->base.samples_signed) {
hword_buffer[i + j] = (int8_t)((sample_hsrc[i + j] * (MICROPY_FLOAT_CONST(1.0) - mix)) + (self->filter_buffer[k + SYNTHIO_MAX_DUR * buf_offset] * mix));
hword_buffer[i + j] = (int8_t)(((sample_hsrc[i + j] * dry_scaled) >> 15) + ((self->filter_buffer[k + SYNTHIO_MAX_DUR * buf_offset] * wet_scaled) >> 15));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
hword_buffer[i + j] = (int8_t)(((sample_hsrc[i + j] * dry_scaled) >> 15) + ((self->filter_buffer[k + SYNTHIO_MAX_DUR * buf_offset] * wet_scaled) >> 15));
hword_buffer[i + j] = (int8_t)synthio_shift_round_to_zero(sample_hsrc[i + j] * dry_scaled + filtered_word * wet_scaled, 15);

} else {
hword_buffer[i + j] = (uint8_t)(((int8_t)(((uint8_t)sample_hsrc[i + j]) ^ 0x80) * (MICROPY_FLOAT_CONST(1.0) - mix)) + (self->filter_buffer[k + SYNTHIO_MAX_DUR * buf_offset] * mix)) ^ 0x80;
hword_buffer[i + j] = (uint8_t)((((int8_t)(((uint8_t)sample_hsrc[i + j]) ^ 0x80) * dry_scaled) >> 15) + ((self->filter_buffer[k + SYNTHIO_MAX_DUR * buf_offset] * wet_scaled) >> 15)) ^ 0x80;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
hword_buffer[i + j] = (uint8_t)((((int8_t)(((uint8_t)sample_hsrc[i + j]) ^ 0x80) * dry_scaled) >> 15) + ((self->filter_buffer[k + SYNTHIO_MAX_DUR * buf_offset] * wet_scaled) >> 15)) ^ 0x80;
hword_buffer[i + j] = (uint8_t)synthio_shift_round_to_zero((int8_t)(((uint8_t)sample_hsrc[i + j]) ^ 0x80) * dry_scaled + filtered_word * wet_scaled, 15) ^ 0x80;

}
}
}
Expand Down
25 changes: 12 additions & 13 deletions shared-module/audiomixer/Mixer.c
Original file line number Diff line number Diff line change
Expand Up @@ -106,20 +106,19 @@ static inline uint32_t mult16signed(uint32_t val, int32_t lomul, int32_t himul)
__asm__ volatile ("pkhbt %0, %1, %2, lsl #16" : "=r" (val) : "r" (lo), "r" (hi)); // pack
return val;
#else
uint32_t result = 0;
for (int8_t i = 0; i < 2; i++) {
float mod_mul = (float)(i ? himul : lomul) / (float)((1 << 15) - 1);
int16_t ai = (val >> (sizeof(uint16_t) * 8 * i));
int32_t intermediate = (int32_t)(ai * mod_mul);
if (intermediate > SHRT_MAX) {
intermediate = SHRT_MAX;
} else if (intermediate < SHRT_MIN) {
intermediate = SHRT_MIN;
}
intermediate &= 0x0000FFFF;
result |= (((uint32_t)intermediate)) << (sizeof(int16_t) * 8 * i);
int32_t lo = ((int32_t)(int16_t)val * lomul) >> 15;
int32_t hi = ((int32_t)(int16_t)(val >> 16) * himul) >> 15;
if (lo > SHRT_MAX) {
lo = SHRT_MAX;
} else if (lo < SHRT_MIN) {
lo = SHRT_MIN;
}
return result;
if (hi > SHRT_MAX) {
hi = SHRT_MAX;
} else if (hi < SHRT_MIN) {
hi = SHRT_MIN;
}
return ((uint32_t)hi << 16) | ((uint32_t)lo & 0xFFFF);
#endif
}

Expand Down
Loading