Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix handling of stereo audio within audiodelays when freq_shift=True #9876

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
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
118 changes: 47 additions & 71 deletions shared-module/audiodelays/Echo.c
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,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) {
Expand Down Expand Up @@ -130,24 +128,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);
Expand Down Expand Up @@ -177,6 +162,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);
Expand Down Expand Up @@ -304,14 +295,9 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t *
// The echo buffer is always stored as a 16-bit value internally
int16_t *echo_buffer = (int16_t *)self->echo_buffer;
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;
}
uint32_t echo_buf_pos = self->echo_buffer_pos;
if (self->freq_shift && channel == 1) {
echo_buf_pos = self->echo_buffer_right_pos;
}

// Loop over the entire length of our buffer to fill it, this may require several calls to get data from the sample
Expand Down Expand Up @@ -355,19 +341,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);
Expand All @@ -384,15 +371,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;
}
}
}
Expand Down Expand Up @@ -430,23 +412,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 = mix_down_sample(word);
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
Expand All @@ -456,11 +438,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;
}
}

Expand All @@ -480,15 +462,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;
}
}
}
Expand All @@ -502,12 +479,11 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t *
}
}

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;
}

// Finally pass our buffer and length to the calling audio function
Expand Down
5 changes: 1 addition & 4 deletions shared-module/audiodelays/Echo.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,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;
Expand Down