diff --git a/shared-module/audiodelays/Echo.c b/shared-module/audiodelays/Echo.c index c1a21d8981171..126b8b5a1c416 100644 --- a/shared-module/audiodelays/Echo.c +++ b/shared-module/audiodelays/Echo.c @@ -95,11 +95,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_read_pos = self->buffer_len / sizeof(uint16_t); - self->echo_buffer_write_pos = 0; - - // where we read the previous echo from delay_ms ago to play back now (for freq shift) - self->echo_buffer_left_pos = self->echo_buffer_right_pos = 0; + self->echo_buffer_pos = 0; + // use a separate buffer position for the right channel when using freq_shift + self->echo_buffer_right_pos = 0; } bool common_hal_audiodelays_echo_deinited(audiodelays_echo_obj_t *self) { @@ -137,24 +135,11 @@ void recalculate_delay(audiodelays_echo_obj_t *self, mp_float_t f_delay_ms) { 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)); - self->echo_buffer_len = self->max_echo_buffer_len; + // Only use half of the buffer per channel if stereo + self->echo_buffer_len = self->max_echo_buffer_len >> (self->channel_count - 1); } else { - // Calculate the current echo buffer length in bytes - uint32_t new_echo_buffer_len = (uint32_t)(self->sample_rate / MICROPY_FLOAT_CONST(1000.0) * f_delay_ms) * (self->channel_count * sizeof(uint16_t)); - - // Check if our new echo is too long for our maximum buffer - if (new_echo_buffer_len > self->max_echo_buffer_len) { - return; - } else if (new_echo_buffer_len < 0.0) { // or too short! - return; - } - - // If the echo buffer is larger then our audio buffer weird things happen - if (new_echo_buffer_len < self->buffer_len) { - return; - } - - self->echo_buffer_len = new_echo_buffer_len; + // Calculate the current echo buffer length in bytes and limit to valid range + self->echo_buffer_len = MIN(MAX((uint32_t)(self->sample_rate / MICROPY_FLOAT_CONST(1000.0) * f_delay_ms) * (self->channel_count * sizeof(uint16_t)), self->channel_count * sizeof(uint16_t)), self->max_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); @@ -184,6 +169,12 @@ bool common_hal_audiodelays_echo_get_freq_shift(audiodelays_echo_obj_t *self) { } void common_hal_audiodelays_echo_set_freq_shift(audiodelays_echo_obj_t *self, bool freq_shift) { + // 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_right_pos = 0; + } self->freq_shift = freq_shift; uint32_t delay_ms = (uint32_t)synthio_block_slot_get(&self->delay_ms); recalculate_delay(self, delay_ms); @@ -316,13 +307,11 @@ 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 = 0; - if (self->freq_shift) { - echo_buffer_pos = self->echo_buffer_left_pos; - if (channel == 1) { - echo_buffer_pos = self->echo_buffer_right_pos; - } + // Set our echo buffer position + uint32_t echo_buf_pos = self->echo_buffer_pos; + // Use a separate buffer position if using single channel output + if (self->freq_shift && channel == 1) { + echo_buf_pos = self->echo_buffer_right_pos; } // If we have no sample keep the echo echoing @@ -346,19 +335,20 @@ 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; + bool echo_buf_offset = self->freq_shift && (channel == 1 || i % self->channel_count == 1); if (self->freq_shift) { - echo = echo_buffer[echo_buffer_pos >> 8]; - next_buffer_pos = echo_buffer_pos + self->echo_buffer_rate; + echo = echo_buffer[(echo_buf_pos >> 8) + echo_buf_len * echo_buf_offset]; + next_buffer_pos = echo_buf_pos + self->echo_buffer_rate; word = (int16_t)(echo * decay); - for (uint32_t j = echo_buffer_pos >> 8; j < next_buffer_pos >> 8; j++) { - echo_buffer[j % echo_buf_len] = word; + for (uint32_t j = echo_buf_pos >> 8; j < next_buffer_pos >> 8; j++) { + echo_buffer[(j % echo_buf_len) + echo_buf_len * echo_buf_offset] = word; } } else { - echo = echo_buffer[self->echo_buffer_read_pos++]; + echo = echo_buffer[echo_buf_pos]; word = (int16_t)(echo * decay); - echo_buffer[self->echo_buffer_write_pos++] = word; + echo_buffer[echo_buf_pos++] = word; } word = (int16_t)(echo * mix); @@ -375,15 +365,10 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t * } } - if (self->freq_shift) { - echo_buffer_pos = next_buffer_pos % (echo_buf_len << 8); - } else { - if (self->echo_buffer_read_pos >= echo_buf_len) { - self->echo_buffer_read_pos = 0; - } - if (self->echo_buffer_write_pos >= echo_buf_len) { - self->echo_buffer_write_pos = 0; - } + if (self->freq_shift && (single_channel_output || echo_buf_offset)) { + echo_buf_pos = next_buffer_pos % (echo_buf_len << 8); + } else if (!self->freq_shift && echo_buf_pos >= echo_buf_len) { + echo_buf_pos = 0; } } } @@ -418,23 +403,23 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t * int32_t echo, word = 0; uint32_t next_buffer_pos = 0; + bool echo_buf_offset = self->freq_shift && (channel == 1 || i % self->channel_count == 1); if (self->freq_shift) { - echo = echo_buffer[echo_buffer_pos >> 8]; - next_buffer_pos = echo_buffer_pos + self->echo_buffer_rate; - word = (int32_t)(echo * decay + sample_word); + echo = echo_buffer[(echo_buf_pos >> 8) + echo_buf_len * echo_buf_offset]; + next_buffer_pos = echo_buf_pos + self->echo_buffer_rate; } else { - echo = echo_buffer[self->echo_buffer_read_pos++]; - word = (int32_t)(echo * decay + sample_word); + echo = echo_buffer[echo_buf_pos]; } + word = (int32_t)(echo * decay + sample_word); if (MP_LIKELY(self->bits_per_sample == 16)) { word = synthio_mix_down_sample(word, SYNTHIO_MIX_DOWN_SCALE(2)); if (self->freq_shift) { - for (uint32_t j = echo_buffer_pos >> 8; j < next_buffer_pos >> 8; j++) { - echo_buffer[j % echo_buf_len] = (int16_t)word; + for (uint32_t j = echo_buf_pos >> 8; j < next_buffer_pos >> 8; j++) { + echo_buffer[(j % echo_buf_len) + echo_buf_len * echo_buf_offset] = (int16_t)word; } } else { - echo_buffer[self->echo_buffer_write_pos++] = (int16_t)word; + echo_buffer[echo_buf_pos++] = (int16_t)word; } } else { // Do not have mix_down for 8 bit so just hard cap samples into 1 byte @@ -444,11 +429,11 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t * word = -128; } if (self->freq_shift) { - for (uint32_t j = echo_buffer_pos >> 8; j < next_buffer_pos >> 8; j++) { - echo_buffer[j % echo_buf_len] = (int8_t)word; + for (uint32_t j = echo_buf_pos >> 8; j < next_buffer_pos >> 8; j++) { + echo_buffer[(j % echo_buf_len) + echo_buf_offset] = (int8_t)word; } } else { - echo_buffer[self->echo_buffer_write_pos++] = (int8_t)word; + echo_buffer[echo_buf_pos++] = (int8_t)word; } } @@ -469,15 +454,10 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t * } } - if (self->freq_shift) { - echo_buffer_pos = next_buffer_pos % (echo_buf_len << 8); - } else { - if (self->echo_buffer_read_pos >= echo_buf_len) { - self->echo_buffer_read_pos = 0; - } - if (self->echo_buffer_write_pos >= echo_buf_len) { - self->echo_buffer_write_pos = 0; - } + if (self->freq_shift && (single_channel_output || echo_buf_offset)) { + echo_buf_pos = next_buffer_pos % (echo_buf_len << 8); + } else if (!self->freq_shift && echo_buf_pos >= echo_buf_len) { + echo_buf_pos = 0; } } } @@ -490,12 +470,11 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t * self->sample_buffer_length -= n; } - if (self->freq_shift) { - if (channel == 0) { - self->echo_buffer_left_pos = echo_buffer_pos; - } else if (channel == 1) { - self->echo_buffer_right_pos = echo_buffer_pos; - } + // Update buffer position + if (self->freq_shift && channel == 1) { + self->echo_buffer_right_pos = echo_buf_pos; + } else { + self->echo_buffer_pos = echo_buf_pos; } } diff --git a/shared-module/audiodelays/Echo.h b/shared-module/audiodelays/Echo.h index dd05318186240..e1f9ed77b2a61 100644 --- a/shared-module/audiodelays/Echo.h +++ b/shared-module/audiodelays/Echo.h @@ -42,11 +42,8 @@ typedef struct { uint32_t echo_buffer_len; // bytes uint32_t max_echo_buffer_len; // bytes - uint32_t echo_buffer_read_pos; // words - uint32_t echo_buffer_write_pos; // words - + uint32_t echo_buffer_pos; // words (<< 8 when freq_shift=True) uint32_t echo_buffer_rate; // words << 8 - uint32_t echo_buffer_left_pos; // words << 8 uint32_t echo_buffer_right_pos; // words << 8 mp_obj_t sample;