Skip to content

Commit 4b82933

Browse files
committed
Double-buffering and improved buffer testing.
1 parent 851765e commit 4b82933

File tree

2 files changed

+48
-21
lines changed

2 files changed

+48
-21
lines changed

ports/raspberrypi/common-hal/audiobusio/I2S.c

Lines changed: 46 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,24 @@ void common_hal_audiobusio_i2s_construct(audiobusio_i2s_obj_t *self,
104104

105105
self->playing = false;
106106
audio_dma_init(&self->dma);
107+
108+
if (self->state_machine.in) {
109+
self->buffer[0] = m_malloc(self->buffer_size);
110+
if (self->buffer[0] == NULL) {
111+
common_hal_audiobusio_i2s_deinit(self);
112+
m_malloc_fail(self->buffer_size);
113+
}
114+
memset(self->buffer[0], 0, self->buffer_size);
115+
116+
self->buffer[1] = m_malloc(self->buffer_size);
117+
if (self->buffer[1] == NULL) {
118+
common_hal_audiobusio_i2s_deinit(self);
119+
m_malloc_fail(self->buffer_size);
120+
}
121+
memset(self->buffer[1], 0, self->buffer_size);
122+
123+
self->last_buf_idx = 1; // Which buffer to use first, toggle between 0 and 1
124+
}
107125
}
108126

109127
void i2s_configure_audio_dma(audiobusio_i2s_obj_t *self, mp_obj_t sample, bool loop, uint32_t sample_rate, uint8_t bits_per_sample, bool force) {
@@ -159,6 +177,9 @@ void common_hal_audiobusio_i2s_deinit(audiobusio_i2s_obj_t *self) {
159177
common_hal_rp2pio_statemachine_deinit(&self->state_machine);
160178

161179
audio_dma_deinit(&self->dma);
180+
181+
self->buffer[0] = NULL;
182+
self->buffer[1] = NULL;
162183
}
163184

164185
// output_buffer may be a byte buffer or a halfword buffer.
@@ -173,21 +194,18 @@ uint32_t common_hal_audiobusio_i2s_record_to_buffer(audiobusio_i2s_obj_t *self,
173194
i2s_configure_audio_dma(self, self, true, self->sample_rate, self->bits_per_sample, true);
174195

175196
size_t output_count = 0;
176-
int16_t *buffer;
177-
size_t buffer_length;
197+
int16_t *buffer[2];
198+
int8_t buffer_idx = 1;
199+
size_t buffer_length = MIN((output_buffer_length - output_count), self->buffer_size / sizeof(int16_t));
178200

179201
while (output_count < output_buffer_length) {
180-
// Do other things while we wait for the buffer to fill.
181-
while (self->last_record_index == self->dma.input_index) {
182-
RUN_BACKGROUND_TASKS;
183-
}
184-
self->last_record_index = self->dma.input_index;
185-
186-
buffer = (int16_t *)audio_dma_get_buffer(&self->dma);
187-
buffer_length = MIN((output_buffer_length - output_count), self->buffer_size / sizeof(int16_t));
202+
do {
203+
buffer_idx = !buffer_idx;
204+
buffer[buffer_idx] = (int16_t *)audio_dma_get_buffer(&self->dma);
205+
} while (buffer[buffer_idx] == NULL || buffer[0] == buffer[1]);
188206

189207
for (size_t i = 0; i < buffer_length; i++) {
190-
output_buffer[i + output_count] = buffer[i];
208+
output_buffer[i + output_count] = buffer[buffer_idx][i];
191209
}
192210

193211
output_count += buffer_length;
@@ -304,9 +322,10 @@ void audiobusio_i2s_reset_buffer(audiobusio_i2s_obj_t *self,
304322
mp_raise_NotImplementedError(MP_ERROR_TEXT("Single channel output not supported."));
305323
}
306324

325+
memset(self->buffer[0], 0, self->buffer_size);
326+
memset(self->buffer[1], 0, self->buffer_size);
327+
307328
i2s_configure_audio_dma(self, self, true, self->sample_rate, self->bits_per_sample, false);
308-
self->last_record_index = -1;
309-
self->last_sample_index = -1;
310329
}
311330

312331
audioio_get_buffer_result_t audiobusio_i2s_get_buffer(audiobusio_i2s_obj_t *self,
@@ -322,14 +341,22 @@ audioio_get_buffer_result_t audiobusio_i2s_get_buffer(audiobusio_i2s_obj_t *self
322341
mp_raise_NotImplementedError(MP_ERROR_TEXT("Single channel output not supported."));
323342
}
324343

325-
// Do other things while we wait for the buffer to fill.
326-
while (self->last_sample_index == self->dma.input_index) {
327-
RUN_BACKGROUND_TASKS;
328-
}
329-
self->last_sample_index = self->dma.input_index;
344+
// Switch our buffers to the other buffer
345+
self->last_buf_idx = !self->last_buf_idx;
330346

347+
uint8_t *dma_buffer;
348+
do {
349+
dma_buffer = audio_dma_get_buffer(&self->dma);
350+
} while (dma_buffer == NULL);
351+
352+
// Copy dma buffer to output buffer
353+
memcpy(self->buffer[self->last_buf_idx], dma_buffer, self->buffer_size);
354+
355+
// Finally pass our buffer and length to the calling audio function
356+
*buffer = (uint8_t *)self->buffer[self->last_buf_idx];
331357
*buffer_length = self->buffer_size;
332-
*buffer = audio_dma_get_buffer(&self->dma);
358+
359+
// I2S always returns more data unless an error occured (see audiocore/__init__.h)
333360
return GET_BUFFER_MORE_DATA;
334361
}
335362

ports/raspberrypi/common-hal/audiobusio/I2S.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ typedef struct {
2626
uint32_t sample_rate;
2727
uint8_t bits_per_sample;
2828
bool samples_signed;
29-
uint8_t last_sample_index;
30-
uint8_t last_record_index;
29+
uint8_t *buffer[2];
30+
uint8_t last_buf_idx;
3131
} audiobusio_i2s_obj_t;
3232

3333
// These are not available from Python because it may be called in an interrupt.

0 commit comments

Comments
 (0)