Skip to content

Commit 59b89fd

Browse files
committed
Fix various audio DMA issues:
RP2040 and SAMD51: - Detect when DMA has finished, and stop DMA audio explicitly. - Do not accidentally reuse `first_buffer` supplied by WaveFile or RawSample. Always realloc `first_buffer` and `second_buffer` RP2040: - When audio playing is stopped, write a final zero to the output register. This prevents residual PWM tones. - Handle buffer size for 8-bit samples properly for 16-bit output. - Fail on some edge cases (which may not be possible at the moment).
1 parent 2cd80d1 commit 59b89fd

File tree

4 files changed

+32
-18
lines changed

4 files changed

+32
-18
lines changed

ports/atmel-samd/audio_dma.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,13 @@ void audio_dma_load_next_block(audio_dma_t *dma) {
157157
if (dma->loop) {
158158
audiosample_reset_buffer(dma->sample, dma->single_channel_output, dma->audio_channel);
159159
} else {
160-
descriptor->DESCADDR.reg = 0;
160+
if ((output_buffer_length == 0) && dma_transfer_status(SHARED_RX_CHANNEL) & 0x3) {
161+
// Nothing further to read and previous buffer is finished.
162+
audio_dma_stop(dma);
163+
} else {
164+
// Break descriptor chain.
165+
descriptor->DESCADDR.reg = 0;
166+
}
161167
}
162168
}
163169
descriptor->BTCTRL.bit.VALID = true;

ports/atmel-samd/common-hal/audiobusio/I2SOut.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444

4545
#include "atmel_start_pins.h"
4646
#include "hal/include/hal_gpio.h"
47-
#include "hpl/gclk/hpl_gclk_base.h"
47+
#include "hpl//hpl_gclk_base.h"
4848
#include "peripheral_clk_config.h"
4949

5050
#ifdef SAMD21
@@ -379,7 +379,7 @@ void common_hal_audiobusio_i2sout_stop(audiobusio_i2sout_obj_t *self) {
379379
}
380380
#endif
381381
disconnect_gclk_from_peripheral(self->gclk, I2S_GCLK_ID_0 + self->clock_unit);
382-
disable_gclk(self->gclk);
382+
disable_clock_generator(self->gclk);
383383

384384
#ifdef SAM_D5X_E5X
385385
connect_gclk_to_peripheral(5, I2S_GCLK_ID_0 + self->clock_unit);

ports/raspberrypi/audio_dma.c

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -150,15 +150,7 @@ void audio_dma_load_next_block(audio_dma_t *dma) {
150150
audio_dma_stop(dma);
151151
return;
152152
}
153-
bool busy0 = dma_channel_is_busy(dma->channel[0]);
154-
bool busy1 = dma_channel_is_busy(dma->channel[1]);
155-
if (busy0 == busy1) {
156-
mp_printf(&mp_plat_print, "busy: %d %d\n", busy0, busy1);
157-
}
158153

159-
if (buffer_length < 256) {
160-
mp_printf(&mp_plat_print, "%d length: %d\n", dma->first_channel_free, buffer_length);
161-
}
162154
audio_dma_convert_signed(dma, buffer, buffer_length, &output_buffer, &output_buffer_length);
163155

164156
// If we don't have an output buffer, save the pointer to first_buffer for use in the single
@@ -169,13 +161,21 @@ void audio_dma_load_next_block(audio_dma_t *dma) {
169161

170162
dma_channel_set_trans_count(dma_channel, output_buffer_length / dma->output_size, false /* trigger */);
171163
dma_channel_set_read_addr(dma_channel, output_buffer, false /* trigger */);
164+
172165
if (get_buffer_result == GET_BUFFER_DONE) {
173166
if (dma->loop) {
174167
audiosample_reset_buffer(dma->sample, dma->single_channel_output, dma->audio_channel);
175168
} else {
176-
// Set channel trigger to ourselves so we don't keep going.
177-
dma_channel_hw_t *c = &dma_hw->ch[dma_channel];
178-
c->al1_ctrl = (c->al1_ctrl & ~DMA_CH0_CTRL_TRIG_CHAIN_TO_BITS) | (dma_channel << DMA_CH0_CTRL_TRIG_CHAIN_TO_LSB);
169+
if (output_buffer_length == 0 &&
170+
!dma_channel_is_busy(dma->channel[0]) &&
171+
!dma_channel_is_busy(dma->channel[1])) {
172+
// No data has been read, and both DMA channels have now finished, so it's safe to stop.
173+
audio_dma_stop(dma);
174+
} else {
175+
// Set channel trigger to ourselves so we don't keep going.
176+
dma_channel_hw_t *c = &dma_hw->ch[dma_channel];
177+
c->al1_ctrl = (c->al1_ctrl & ~DMA_CH0_CTRL_TRIG_CHAIN_TO_BITS) | (dma_channel << DMA_CH0_CTRL_TRIG_CHAIN_TO_LSB);
178+
}
179179
}
180180
}
181181
}
@@ -217,18 +217,17 @@ audio_dma_result audio_dma_setup_playback(audio_dma_t *dma,
217217
dma->first_channel_free = true;
218218
dma->output_resolution = output_resolution;
219219
dma->sample_resolution = audiosample_bits_per_sample(sample);
220+
dma->output_register_address = output_register_address;
221+
220222
audiosample_reset_buffer(sample, single_channel_output, audio_channel);
221223

222224
bool single_buffer;
223225
bool samples_signed;
224226
uint32_t max_buffer_length;
225227
audiosample_get_buffer_structure(sample, single_channel_output, &single_buffer, &samples_signed,
226228
&max_buffer_length, &dma->sample_spacing);
227-
mp_printf(&mp_plat_print, "single_buffer: %d, samples_signed: %d, max_buffer_length: %d, dma->sample_spacing: %d\n",
228-
single_buffer, samples_signed, max_buffer_length, dma->sample_spacing); ////
229+
229230
// Check to see if we have to scale the resolution up.
230-
mp_printf(&mp_plat_print, "dma->sample_resolution: %d, dma->output_resolution: %d, output_signed: %d, single_channel_output: %d\n",
231-
dma->sample_resolution, dma->output_resolution, output_signed, single_channel_output);
232231
if (dma->sample_resolution <= 8 && dma->output_resolution > 8) {
233232
max_buffer_length *= 2;
234233
}
@@ -343,6 +342,14 @@ void audio_dma_stop(audio_dma_t *dma) {
343342
if (dma_channel_is_busy(channel)) {
344343
dma_channel_abort(channel);
345344
}
345+
346+
// Write a zero as the last sample. This stops any PWM output.
347+
if (dma->output_resolution <= 8) {
348+
*((uint8_t *)dma->output_register_address) = 0;
349+
} else {
350+
*((uint16_t *)dma->output_register_address) = 0;
351+
}
352+
346353
dma_channel_set_read_addr(channel, NULL, false /* trigger */);
347354
dma_channel_set_write_addr(channel, NULL, false /* trigger */);
348355
dma_channel_set_trans_count(channel, 0, false /* trigger */);

ports/raspberrypi/audio_dma.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ typedef struct {
5151
size_t first_buffer_length;
5252
uint8_t *second_buffer;
5353
size_t second_buffer_length;
54+
uint32_t output_register_address;
5455
background_callback_t callback;
5556
} audio_dma_t;
5657

0 commit comments

Comments
 (0)