Skip to content

Commit 24e61a7

Browse files
committed
Track more carefully which audio buffers to fill, based on interrupt channels
1 parent 2451c78 commit 24e61a7

File tree

4 files changed

+136
-102
lines changed

4 files changed

+136
-102
lines changed

ports/raspberrypi/audio_dma.c

Lines changed: 111 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
#include "shared-bindings/audiocore/RawSample.h"
3030
#include "shared-bindings/audiocore/WaveFile.h"
31+
#include "shared-bindings/microcontroller/__init__.h"
3132
#include "supervisor/background_callback.h"
3233

3334
#include "py/mpstate.h"
@@ -47,71 +48,78 @@ void audio_dma_reset(void) {
4748
}
4849
}
4950

50-
void audio_dma_convert_signed(audio_dma_t *dma, uint8_t *buffer, uint32_t buffer_length,
51-
uint8_t **output_buffer, uint32_t *output_buffer_length) {
5251

53-
size_t output_buffer_max_length;
54-
if (dma->first_buffer_free) {
55-
*output_buffer = dma->first_buffer;
56-
output_buffer_max_length = dma->first_buffer_length;
57-
} else {
58-
*output_buffer = dma->second_buffer;
59-
output_buffer_max_length = dma->second_buffer_length;
60-
}
52+
STATIC void audio_dma_convert_samples(
53+
audio_dma_t *dma,
54+
uint8_t *input, uint32_t input_length,
55+
uint8_t *available_output_buffer, uint32_t available_output_buffer_length,
56+
uint8_t **output, uint32_t *output_length) {
6157

6258
#pragma GCC diagnostic push
6359
#pragma GCC diagnostic ignored "-Wcast-align"
60+
61+
// Check whether a conversion is necessary
6462
if (dma->signed_to_unsigned ||
6563
dma->unsigned_to_signed ||
6664
dma->sample_spacing > 1 ||
6765
(dma->sample_resolution != dma->output_resolution)) {
68-
*output_buffer_length = buffer_length / dma->sample_spacing;
66+
67+
// Must convert.
68+
// Write the conversion into the passed-in output buffer
69+
*output = available_output_buffer;
70+
*output_length = input_length / dma->sample_spacing;
71+
72+
if (*output_length > available_output_buffer_length) {
73+
mp_raise_RuntimeError(translate("Internal audio buffer too small"));
74+
}
75+
6976
uint32_t out_i = 0;
7077
if (dma->sample_resolution <= 8 && dma->output_resolution > 8) {
71-
// reading bytes, writing 16-bit samples
72-
*output_buffer_length = *output_buffer_length * 2;
73-
if (*output_buffer_length > output_buffer_max_length) {
78+
// reading bytes, writing 16-bit words, so output buffer will be bigger.
79+
80+
*output_length = *output_length * 2;
81+
if (*output_length > available_output_buffer_length) {
7482
mp_raise_RuntimeError(translate("Internal audio buffer too small"));
7583
}
7684

7785
size_t shift = dma->output_resolution - dma->sample_resolution;
7886

79-
for (uint32_t i = 0; i < buffer_length; i += dma->sample_spacing) {
87+
for (uint32_t i = 0; i < input_length; i += dma->sample_spacing) {
8088
if (dma->signed_to_unsigned) {
81-
((uint16_t *)*output_buffer)[out_i] = ((uint16_t)((int8_t *)buffer)[i] + 0x80) << shift;
89+
((uint16_t *)*output)[out_i] = ((uint16_t)((int8_t *)input)[i] + 0x80) << shift;
8290
} else if (dma->unsigned_to_signed) {
83-
((int16_t *)*output_buffer)[out_i] = ((int16_t)((uint8_t *)buffer)[i] - 0x80) << shift;
91+
((int16_t *)*output)[out_i] = ((int16_t)((uint8_t *)input)[i] - 0x80) << shift;
8492
} else {
85-
((uint16_t *)*output_buffer)[out_i] = ((uint16_t)((uint8_t *)buffer)[i]) << shift;
93+
((uint16_t *)*output)[out_i] = ((uint16_t)((uint8_t *)input)[i]) << shift;
8694
}
8795
out_i += 1;
8896
}
8997
} else if (dma->sample_resolution <= 8 && dma->output_resolution <= 8) {
90-
for (uint32_t i = 0; i < buffer_length; i += dma->sample_spacing) {
98+
for (uint32_t i = 0; i < input_length; i += dma->sample_spacing) {
9199
if (dma->signed_to_unsigned) {
92-
((uint8_t *)*output_buffer)[out_i] = ((int8_t *)buffer)[i] + 0x80;
100+
((uint8_t *)*output)[out_i] = ((int8_t *)input)[i] + 0x80;
93101
} else if (dma->unsigned_to_signed) {
94-
((int8_t *)*output_buffer)[out_i] = ((uint8_t *)buffer)[i] - 0x80;
102+
((int8_t *)*output)[out_i] = ((uint8_t *)input)[i] - 0x80;
95103
} else {
96-
((uint8_t *)*output_buffer)[out_i] = ((uint8_t *)buffer)[i];
104+
((uint8_t *)*output)[out_i] = ((uint8_t *)input)[i];
97105
}
98106
out_i += 1;
99107
}
100108
} else if (dma->sample_resolution > 8 && dma->output_resolution > 8) {
101109
size_t shift = 16 - dma->output_resolution;
102-
for (uint32_t i = 0; i < buffer_length / 2; i += dma->sample_spacing) {
110+
for (uint32_t i = 0; i < input_length / 2; i += dma->sample_spacing) {
103111
if (dma->signed_to_unsigned) {
104-
((uint16_t *)*output_buffer)[out_i] = ((int16_t *)buffer)[i] + 0x8000;
112+
((uint16_t *)*output)[out_i] = ((int16_t *)input)[i] + 0x8000;
105113
} else if (dma->unsigned_to_signed) {
106-
((int16_t *)*output_buffer)[out_i] = ((uint16_t *)buffer)[i] - 0x8000;
114+
((int16_t *)*output)[out_i] = ((uint16_t *)input)[i] - 0x8000;
107115
} else {
108-
((uint16_t *)*output_buffer)[out_i] = ((uint16_t *)buffer)[i];
116+
((uint16_t *)*output)[out_i] = ((uint16_t *)input)[i];
109117
}
110118
if (dma->output_resolution < 16) {
111119
if (dma->output_signed) {
112-
((int16_t *)*output_buffer)[out_i] = ((int16_t *)*output_buffer)[out_i] >> shift;
120+
((int16_t *)*output)[out_i] = ((int16_t *)*output)[out_i] >> shift;
113121
} else {
114-
((uint16_t *)*output_buffer)[out_i] = ((uint16_t *)*output_buffer)[out_i] >> shift;
122+
((uint16_t *)*output)[out_i] = ((uint16_t *)*output)[out_i] >> shift;
115123
}
116124
}
117125
out_i += 1;
@@ -122,65 +130,70 @@ void audio_dma_convert_signed(audio_dma_t *dma, uint8_t *buffer, uint32_t buffer
122130
mp_raise_RuntimeError(translate("Audio conversion not implemented"));
123131
}
124132
} else {
125-
*output_buffer = buffer;
126-
*output_buffer_length = buffer_length;
133+
// No conversion necessary. Designate the input buffer as the output buffer.
134+
*output = input;
135+
*output_length = input_length;
127136
}
128137
#pragma GCC diagnostic pop
129-
dma->first_buffer_free = !dma->first_buffer_free;
130138
}
131139

132-
void audio_dma_load_next_block(audio_dma_t *dma) {
133-
uint8_t dma_channel = dma->channel[1];
134-
if (dma->first_channel_free) {
135-
dma_channel = dma->channel[0];
136-
}
137-
dma->first_channel_free = !dma->first_channel_free;
140+
// channel_idx is 0 or 1.
141+
STATIC void audio_dma_load_next_block(audio_dma_t *dma, size_t buffer_idx) {
142+
size_t dma_channel = dma->channel[buffer_idx];
138143

139-
uint8_t *output_buffer;
140-
uint32_t output_buffer_length;
141144
audioio_get_buffer_result_t get_buffer_result;
142-
uint8_t *buffer;
143-
uint32_t buffer_length;
145+
uint8_t *sample_buffer;
146+
uint32_t sample_buffer_length;
144147
get_buffer_result = audiosample_get_buffer(dma->sample,
145-
dma->single_channel_output, dma->audio_channel, &buffer, &buffer_length);
148+
dma->single_channel_output, dma->audio_channel, &sample_buffer, &sample_buffer_length);
146149

147150
if (get_buffer_result == GET_BUFFER_ERROR) {
148151
audio_dma_stop(dma);
149152
return;
150153
}
151154

152-
audio_dma_convert_signed(dma, buffer, buffer_length, &output_buffer, &output_buffer_length);
155+
// Convert the sample format resolution and signedness, as necessary.
156+
// The input sample buffer is what was read from a file or a raw sample buffer.
157+
// The output buffer is one of the DMA buffers (passed in), or if no conversion was done,
158+
// the original sample buffer (to save copying).
153159

154-
// If we don't have an output buffer, save the pointer to first_buffer for use in the single
155-
// buffer special case.
156-
if (dma->first_buffer == NULL) {
157-
dma->first_buffer = output_buffer;
158-
}
160+
// audio_dma_convert_samples() will write the converted samples into the given output
161+
// buffer if necessary. If no conversion was needed, it will return the sample buffer
162+
// as the output buffer.
163+
uint8_t *output_buffer;
164+
uint32_t output_buffer_length;
165+
166+
audio_dma_convert_samples(dma, sample_buffer, sample_buffer_length,
167+
dma->buffer[buffer_idx], dma->buffer_length[buffer_idx],
168+
&output_buffer, &output_buffer_length);
159169

160-
dma_channel_set_trans_count(dma_channel, output_buffer_length / dma->output_size, false /* trigger */);
161170
dma_channel_set_read_addr(dma_channel, output_buffer, false /* trigger */);
171+
dma_channel_set_trans_count(dma_channel, output_buffer_length / dma->output_size, false /* trigger */);
162172

163173
if (get_buffer_result == GET_BUFFER_DONE) {
164174
if (dma->loop) {
165175
audiosample_reset_buffer(dma->sample, dma->single_channel_output, dma->audio_channel);
166176
} else {
177+
// Set channel trigger to ourselves so we don't keep going.
178+
dma_channel_hw_t *c = &dma_hw->ch[dma_channel];
179+
c->al1_ctrl =
180+
(c->al1_ctrl & ~DMA_CH0_CTRL_TRIG_CHAIN_TO_BITS) |
181+
(dma_channel << DMA_CH0_CTRL_TRIG_CHAIN_TO_LSB);
182+
167183
if (output_buffer_length == 0 &&
168184
!dma_channel_is_busy(dma->channel[0]) &&
169185
!dma_channel_is_busy(dma->channel[1])) {
170186
// No data has been read, and both DMA channels have now finished, so it's safe to stop.
171187
audio_dma_stop(dma);
172188
dma->playing_in_progress = false;
173-
} else {
174-
// Set channel trigger to ourselves so we don't keep going.
175-
dma_channel_hw_t *c = &dma_hw->ch[dma_channel];
176-
c->al1_ctrl = (c->al1_ctrl & ~DMA_CH0_CTRL_TRIG_CHAIN_TO_BITS) | (dma_channel << DMA_CH0_CTRL_TRIG_CHAIN_TO_LSB);
177189
}
178190
}
179191
}
180192
}
181193

182194
// Playback should be shutdown before calling this.
183-
audio_dma_result audio_dma_setup_playback(audio_dma_t *dma,
195+
audio_dma_result audio_dma_setup_playback(
196+
audio_dma_t *dma,
184197
mp_obj_t sample,
185198
bool loop,
186199
bool single_channel_output,
@@ -189,6 +202,7 @@ audio_dma_result audio_dma_setup_playback(audio_dma_t *dma,
189202
uint8_t output_resolution,
190203
uint32_t output_register_address,
191204
uint8_t dma_trigger_source) {
205+
192206
// Use two DMA channels to play because the DMA can't wrap to itself without the
193207
// buffer being power of two aligned.
194208
int dma_channel_0_maybe = dma_claim_unused_channel(false);
@@ -213,14 +227,15 @@ audio_dma_result audio_dma_setup_playback(audio_dma_t *dma,
213227
dma->unsigned_to_signed = false;
214228
dma->output_signed = output_signed;
215229
dma->sample_spacing = 1;
216-
dma->first_channel_free = true;
217230
dma->output_resolution = output_resolution;
218231
dma->sample_resolution = audiosample_bits_per_sample(sample);
219232
dma->output_register_address = output_register_address;
220233

221234
audiosample_reset_buffer(sample, single_channel_output, audio_channel);
222235

223-
bool single_buffer;
236+
237+
bool single_buffer; // True if data fits in one single buffer.
238+
224239
bool samples_signed;
225240
uint32_t max_buffer_length;
226241
audiosample_get_buffer_structure(sample, single_channel_output, &single_buffer, &samples_signed,
@@ -236,17 +251,16 @@ audio_dma_result audio_dma_setup_playback(audio_dma_t *dma,
236251
max_buffer_length /= dma->sample_spacing;
237252
}
238253

239-
dma->first_buffer = (uint8_t *)m_realloc(dma->first_buffer, max_buffer_length);
240-
dma->first_buffer_length = max_buffer_length;
241-
if (dma->first_buffer == NULL) {
254+
dma->buffer[0] = (uint8_t *)m_realloc(dma->buffer[0], max_buffer_length);
255+
dma->buffer_length[0] = max_buffer_length;
256+
if (dma->buffer[0] == NULL) {
242257
return AUDIO_DMA_MEMORY_ERROR;
243258
}
244259

245-
dma->first_buffer_free = true;
246260
if (!single_buffer) {
247-
dma->second_buffer = (uint8_t *)m_realloc(dma->second_buffer, max_buffer_length);
248-
dma->second_buffer_length = max_buffer_length;
249-
if (dma->second_buffer == NULL) {
261+
dma->buffer[1] = (uint8_t *)m_realloc(dma->buffer[1], max_buffer_length);
262+
dma->buffer_length[1] = max_buffer_length;
263+
if (dma->buffer[1] == NULL) {
250264
return AUDIO_DMA_MEMORY_ERROR;
251265
}
252266
}
@@ -276,9 +290,11 @@ audio_dma_result audio_dma_setup_playback(audio_dma_t *dma,
276290
channel_config_set_dreq(&c, dma_trigger_source);
277291
channel_config_set_read_increment(&c, true);
278292
channel_config_set_write_increment(&c, false);
293+
279294
// Chain to the other channel by default.
280295
channel_config_set_chain_to(&c, dma->channel[(i + 1) % 2]);
281296
dma_channel_set_config(dma->channel[i], &c, false /* trigger */);
297+
282298
dma_channel_set_write_addr(dma->channel[i], (void *)output_register_address, false /* trigger */);
283299
}
284300

@@ -288,9 +304,9 @@ audio_dma_result audio_dma_setup_playback(audio_dma_t *dma,
288304
MP_STATE_PORT(playing_audio)[dma->channel[1]] = dma;
289305

290306
// Load the first two blocks up front.
291-
audio_dma_load_next_block(dma);
307+
audio_dma_load_next_block(dma, 0);
292308
if (!single_buffer) {
293-
audio_dma_load_next_block(dma);
309+
audio_dma_load_next_block(dma, 1);
294310
}
295311

296312
// Special case the DMA for a single buffer. It's commonly used for a single wave length of sound
@@ -307,11 +323,11 @@ audio_dma_result audio_dma_setup_playback(audio_dma_t *dma,
307323
channel_config_set_chain_to(&c, dma->channel[1]); // Chain to ourselves so we stop.
308324
dma_channel_configure(dma->channel[1], &c,
309325
&dma_hw->ch[dma->channel[0]].al3_read_addr_trig, // write address
310-
&dma->first_buffer, // read address
326+
&dma->buffer[0], // read address
311327
1, // transaction count
312328
false); // trigger
313329
} else {
314-
// Enable our DMA channels on DMA0 to the CPU. This will wake us up when
330+
// Enable our DMA channels on DMA_IRQ_0 to the CPU. This will wake us up when
315331
// we're WFI.
316332
dma_hw->inte0 |= (1 << dma->channel[0]) | (1 << dma->channel[1]);
317333
irq_set_mask_enabled(1 << DMA_IRQ_0, true);
@@ -402,18 +418,19 @@ bool audio_dma_get_paused(audio_dma_t *dma) {
402418
}
403419

404420
void audio_dma_init(audio_dma_t *dma) {
405-
dma->first_buffer = NULL;
406-
dma->second_buffer = NULL;
421+
dma->buffer[0] = NULL;
422+
dma->buffer[1] = NULL;
423+
407424
dma->channel[0] = NUM_DMA_CHANNELS;
408425
dma->channel[1] = NUM_DMA_CHANNELS;
409426
}
410427

411428
void audio_dma_deinit(audio_dma_t *dma) {
412-
m_free(dma->first_buffer);
413-
dma->first_buffer = NULL;
429+
m_free(dma->buffer[0]);
430+
dma->buffer[0] = NULL;
414431

415-
m_free(dma->second_buffer);
416-
dma->second_buffer = NULL;
432+
m_free(dma->buffer[1]);
433+
dma->buffer[1] = NULL;
417434
}
418435

419436
bool audio_dma_get_playing(audio_dma_t *dma) {
@@ -433,14 +450,34 @@ STATIC void dma_callback_fun(void *arg) {
433450
return;
434451
}
435452

436-
audio_dma_load_next_block(dma);
453+
common_hal_mcu_disable_interrupts();
454+
uint32_t channels_to_load_mask = dma->channels_to_load_mask;
455+
dma->channels_to_load_mask = 0;
456+
common_hal_mcu_enable_interrupts();
457+
458+
// Load the blocks for the requested channels.
459+
uint32_t channel = 0;
460+
while (channels_to_load_mask) {
461+
if (channels_to_load_mask & 1) {
462+
if (dma->channel[0] == channel) {
463+
audio_dma_load_next_block(dma, 0);
464+
}
465+
if (dma->channel[1] == channel) {
466+
audio_dma_load_next_block(dma, 1);
467+
}
468+
}
469+
channels_to_load_mask >>= 1;
470+
channel++;
471+
}
437472
}
438473

439474
void isr_dma_0(void) {
440475
for (size_t i = 0; i < NUM_DMA_CHANNELS; i++) {
441476
uint32_t mask = 1 << i;
442477
if ((dma_hw->intr & mask) != 0 && MP_STATE_PORT(playing_audio)[i] != NULL) {
443478
audio_dma_t *dma = MP_STATE_PORT(playing_audio)[i];
479+
// Record all channels whose DMA has completed; they need loading.
480+
dma->channels_to_load_mask |= mask;
444481
background_callback_add(&dma->callback, dma_callback_fun, (void *)dma);
445482
dma_hw->ints0 = mask;
446483
}

ports/raspberrypi/audio_dma.h

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,12 @@ typedef struct {
4343
bool signed_to_unsigned;
4444
bool unsigned_to_signed;
4545
bool output_signed;
46-
bool first_channel_free;
47-
bool first_buffer_free;
4846
bool playing_in_progress;
4947
uint8_t output_resolution; // in bits
5048
uint8_t sample_resolution; // in bits
51-
uint8_t *first_buffer;
52-
size_t first_buffer_length;
53-
uint8_t *second_buffer;
54-
size_t second_buffer_length;
49+
uint8_t *buffer[2];
50+
size_t buffer_length[2];
51+
uint32_t channels_to_load_mask;
5552
uint32_t output_register_address;
5653
background_callback_t callback;
5754
} audio_dma_t;

0 commit comments

Comments
 (0)