Unify echo buffer channel splitting.

This commit is contained in:
Cooper Dalrymple 2025-04-21 17:42:39 -05:00
parent feee74a5e2
commit 7d0e72e5ca
2 changed files with 45 additions and 32 deletions

View file

@ -98,9 +98,9 @@ void common_hal_audiodelays_echo_construct(audiodelays_echo_obj_t *self, uint32_
// read is where we read previous echo from delay_ms ago to play back now
// write is where the store the latest playing sample to echo back later
self->echo_buffer_pos = 0;
self->echo_buffer_left_pos = 0;
// use a separate buffer position for the right channel when using freq_shift
// use a separate buffer position for the right channel
self->echo_buffer_right_pos = 0;
}
@ -127,18 +127,21 @@ void recalculate_delay(audiodelays_echo_obj_t *self, mp_float_t f_delay_ms) {
// Require that delay is at least 1 sample long
f_delay_ms = MAX(f_delay_ms, self->sample_ms);
// Calculate the maximum buffer size per channel in bytes
uint32_t max_echo_buffer_len = self->max_echo_buffer_len >> (self->base.channel_count - 1);
if (self->freq_shift) {
// Calculate the rate of iteration over the echo buffer with 8 sub-bits
self->echo_buffer_rate = (uint32_t)MAX(self->max_delay_ms / f_delay_ms * MICROPY_FLOAT_CONST(256.0), MICROPY_FLOAT_CONST(1.0));
// Only use half of the buffer per channel if stereo
self->echo_buffer_len = self->max_echo_buffer_len >> (self->base.channel_count - 1);
self->echo_buffer_len = max_echo_buffer_len;
} else {
// Calculate the current echo buffer length in bytes
uint32_t new_echo_buffer_len = (uint32_t)(self->base.sample_rate / MICROPY_FLOAT_CONST(1000.0) * f_delay_ms) * (self->base.channel_count * sizeof(uint16_t));
uint32_t new_echo_buffer_len = (uint32_t)(self->base.sample_rate / MICROPY_FLOAT_CONST(1000.0) * f_delay_ms) * sizeof(uint16_t);
// Limit to valid range
if (new_echo_buffer_len > self->max_echo_buffer_len) {
new_echo_buffer_len = self->max_echo_buffer_len;
if (new_echo_buffer_len > max_echo_buffer_len) {
new_echo_buffer_len = max_echo_buffer_len;
} else if (new_echo_buffer_len < self->buffer_len) {
// If the echo buffer is smaller than our audio buffer, weird things happen
new_echo_buffer_len = self->buffer_len;
@ -147,7 +150,9 @@ void recalculate_delay(audiodelays_echo_obj_t *self, mp_float_t f_delay_ms) {
self->echo_buffer_len = new_echo_buffer_len;
// Clear the now unused part of the buffer or some weird artifacts appear
memset(self->echo_buffer + self->echo_buffer_len, 0, self->max_echo_buffer_len - self->echo_buffer_len);
for (uint32_t i = 0; i < self->base.channel_count; i++) {
memset(self->echo_buffer + (i * max_echo_buffer_len) + self->echo_buffer_len, 0, max_echo_buffer_len - self->echo_buffer_len);
}
}
self->current_delay_ms = f_delay_ms;
@ -177,7 +182,7 @@ void common_hal_audiodelays_echo_set_freq_shift(audiodelays_echo_obj_t *self, bo
// Clear the echo buffer and reset buffer position if changing freq_shift modes
if (self->freq_shift != freq_shift) {
memset(self->echo_buffer, 0, self->max_echo_buffer_len);
self->echo_buffer_pos = 0;
self->echo_buffer_left_pos = 0;
self->echo_buffer_right_pos = 0;
}
self->freq_shift = freq_shift;
@ -279,12 +284,7 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t *
}
uint32_t echo_buf_len = self->echo_buffer_len / sizeof(uint16_t);
// Set our echo buffer position accounting for stereo
uint32_t echo_buffer_pos = self->echo_buffer_pos;
if (self->freq_shift && channel == 1) {
echo_buffer_pos = self->echo_buffer_right_pos;
}
uint32_t max_echo_buf_len = (self->max_echo_buffer_len >> (self->base.channel_count - 1)) / sizeof(uint16_t);
// If we have no sample keep the echo echoing
if (self->sample == NULL) {
@ -307,7 +307,10 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t *
for (uint32_t i = 0; i < length; i++) {
int16_t echo, word = 0;
uint32_t next_buffer_pos = 0;
uint32_t echo_buffer_offset = echo_buf_len * (self->freq_shift && ((single_channel_output && channel == 1) || (!single_channel_output && (i % self->base.channel_count) == 1)));
// Get our echo buffer position and offset depending on current channel
uint32_t echo_buffer_offset = max_echo_buf_len * ((single_channel_output && channel == 1) || (!single_channel_output && (i % self->base.channel_count) == 1));
uint32_t echo_buffer_pos = echo_buffer_offset ? self->echo_buffer_right_pos : self->echo_buffer_left_pos;
if (self->freq_shift) {
echo = echo_buffer[(echo_buffer_pos >> 8) + echo_buffer_offset];
@ -318,9 +321,9 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t *
echo_buffer[(j % echo_buf_len) + echo_buffer_offset] = word;
}
} else {
echo = echo_buffer[echo_buffer_pos];
echo = echo_buffer[echo_buffer_pos + echo_buffer_offset];
word = (int16_t)(echo * decay);
echo_buffer[echo_buffer_pos++] = word;
echo_buffer[echo_buffer_pos++ + echo_buffer_offset] = word;
}
word = (int16_t)(echo * mix);
@ -337,11 +340,18 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t *
}
}
if (self->freq_shift && (self->base.channel_count == 1 || single_channel_output || (!single_channel_output && (i % self->base.channel_count) == 1))) {
if (self->freq_shift) {
echo_buffer_pos = next_buffer_pos % (echo_buf_len << 8);
} else if (!self->freq_shift && echo_buffer_pos >= echo_buf_len) {
echo_buffer_pos = 0;
}
// Update buffer position
if (echo_buffer_offset) {
self->echo_buffer_right_pos = echo_buffer_pos;
} else {
self->echo_buffer_left_pos = echo_buffer_pos;
}
}
}
@ -375,13 +385,16 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t *
int32_t echo, word = 0;
uint32_t next_buffer_pos = 0;
uint32_t echo_buffer_offset = echo_buf_len * (self->freq_shift && ((single_channel_output && channel == 1) || (!single_channel_output && (i % self->base.channel_count) == 1)));
// Get our echo buffer position and offset depending on current channel
uint32_t echo_buffer_offset = max_echo_buf_len * ((single_channel_output && channel == 1) || (!single_channel_output && (i % self->base.channel_count) == 1));
uint32_t echo_buffer_pos = echo_buffer_offset ? self->echo_buffer_right_pos : self->echo_buffer_left_pos;
if (self->freq_shift) {
echo = echo_buffer[(echo_buffer_pos >> 8) + echo_buffer_offset];
next_buffer_pos = echo_buffer_pos + self->echo_buffer_rate;
} else {
echo = echo_buffer[echo_buffer_pos];
echo = echo_buffer[echo_buffer_pos + echo_buffer_offset];
word = (int32_t)(echo * decay + sample_word);
}
@ -394,7 +407,7 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t *
}
} else {
word = synthio_mix_down_sample(word, SYNTHIO_MIX_DOWN_SCALE(2));
echo_buffer[echo_buffer_pos++] = (int16_t)word;
echo_buffer[echo_buffer_pos++ + echo_buffer_offset] = (int16_t)word;
}
} else {
if (self->freq_shift) {
@ -407,7 +420,7 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t *
} else {
// Do not have mix_down for 8 bit so just hard cap samples into 1 byte
word = MIN(MAX(word, -128), 127);
echo_buffer[echo_buffer_pos++] = (int8_t)word;
echo_buffer[echo_buffer_pos++ + echo_buffer_offset] = (int8_t)word;
}
}
@ -428,11 +441,18 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t *
}
}
if (self->freq_shift && (self->base.channel_count == 1 || single_channel_output || (!single_channel_output && (i % self->base.channel_count) == 1))) {
if (self->freq_shift) {
echo_buffer_pos = next_buffer_pos % (echo_buf_len << 8);
} else if (!self->freq_shift && echo_buffer_pos >= echo_buf_len) {
echo_buffer_pos = 0;
}
// Update buffer position
if (echo_buffer_offset) {
self->echo_buffer_right_pos = echo_buffer_pos;
} else {
self->echo_buffer_left_pos = echo_buffer_pos;
}
}
}
@ -443,13 +463,6 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t *
self->sample_remaining_buffer += (n * (self->base.bits_per_sample / 8));
self->sample_buffer_length -= n;
}
// Update buffer position
if (self->freq_shift && channel == 1) {
self->echo_buffer_right_pos = echo_buffer_pos;
} else {
self->echo_buffer_pos = echo_buffer_pos;
}
}
// Finally pass our buffer and length to the calling audio function

View file

@ -37,8 +37,8 @@ typedef struct {
uint32_t echo_buffer_len; // bytes
uint32_t max_echo_buffer_len; // bytes
uint32_t echo_buffer_pos; // words (<< 8 when freq_shift=True)
uint32_t echo_buffer_right_pos; // words << 8
uint32_t echo_buffer_left_pos; // words (<< 8 when freq_shift=True)
uint32_t echo_buffer_right_pos; // words (<< 8 when freq_shift=True)
uint32_t echo_buffer_rate; // words << 8
mp_obj_t sample;