28
28
29
29
#include "shared-bindings/audiocore/RawSample.h"
30
30
#include "shared-bindings/audiocore/WaveFile.h"
31
+ #include "shared-bindings/microcontroller/__init__.h"
31
32
#include "supervisor/background_callback.h"
32
33
33
34
#include "py/mpstate.h"
@@ -47,71 +48,78 @@ void audio_dma_reset(void) {
47
48
}
48
49
}
49
50
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 ) {
52
51
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 ) {
61
57
62
58
#pragma GCC diagnostic push
63
59
#pragma GCC diagnostic ignored "-Wcast-align"
60
+
61
+ // Check whether a conversion is necessary
64
62
if (dma -> signed_to_unsigned ||
65
63
dma -> unsigned_to_signed ||
66
64
dma -> sample_spacing > 1 ||
67
65
(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
+
69
76
uint32_t out_i = 0 ;
70
77
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 ) {
74
82
mp_raise_RuntimeError (translate ("Internal audio buffer too small" ));
75
83
}
76
84
77
85
size_t shift = dma -> output_resolution - dma -> sample_resolution ;
78
86
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 ) {
80
88
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 ;
82
90
} 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 ;
84
92
} 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 ;
86
94
}
87
95
out_i += 1 ;
88
96
}
89
97
} 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 ) {
91
99
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 ;
93
101
} 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 ;
95
103
} else {
96
- ((uint8_t * )* output_buffer )[out_i ] = ((uint8_t * )buffer )[i ];
104
+ ((uint8_t * )* output )[out_i ] = ((uint8_t * )input )[i ];
97
105
}
98
106
out_i += 1 ;
99
107
}
100
108
} else if (dma -> sample_resolution > 8 && dma -> output_resolution > 8 ) {
101
109
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 ) {
103
111
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 ;
105
113
} 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 ;
107
115
} else {
108
- ((uint16_t * )* output_buffer )[out_i ] = ((uint16_t * )buffer )[i ];
116
+ ((uint16_t * )* output )[out_i ] = ((uint16_t * )input )[i ];
109
117
}
110
118
if (dma -> output_resolution < 16 ) {
111
119
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 ;
113
121
} 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 ;
115
123
}
116
124
}
117
125
out_i += 1 ;
@@ -122,65 +130,70 @@ void audio_dma_convert_signed(audio_dma_t *dma, uint8_t *buffer, uint32_t buffer
122
130
mp_raise_RuntimeError (translate ("Audio conversion not implemented" ));
123
131
}
124
132
} 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 ;
127
136
}
128
137
#pragma GCC diagnostic pop
129
- dma -> first_buffer_free = !dma -> first_buffer_free ;
130
138
}
131
139
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 ];
138
143
139
- uint8_t * output_buffer ;
140
- uint32_t output_buffer_length ;
141
144
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 ;
144
147
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 );
146
149
147
150
if (get_buffer_result == GET_BUFFER_ERROR ) {
148
151
audio_dma_stop (dma );
149
152
return ;
150
153
}
151
154
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).
153
159
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 );
159
169
160
- dma_channel_set_trans_count (dma_channel , output_buffer_length / dma -> output_size , false /* trigger */ );
161
170
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 */ );
162
172
163
173
if (get_buffer_result == GET_BUFFER_DONE ) {
164
174
if (dma -> loop ) {
165
175
audiosample_reset_buffer (dma -> sample , dma -> single_channel_output , dma -> audio_channel );
166
176
} 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
+
167
183
if (output_buffer_length == 0 &&
168
184
!dma_channel_is_busy (dma -> channel [0 ]) &&
169
185
!dma_channel_is_busy (dma -> channel [1 ])) {
170
186
// No data has been read, and both DMA channels have now finished, so it's safe to stop.
171
187
audio_dma_stop (dma );
172
188
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 );
177
189
}
178
190
}
179
191
}
180
192
}
181
193
182
194
// 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 ,
184
197
mp_obj_t sample ,
185
198
bool loop ,
186
199
bool single_channel_output ,
@@ -189,6 +202,7 @@ audio_dma_result audio_dma_setup_playback(audio_dma_t *dma,
189
202
uint8_t output_resolution ,
190
203
uint32_t output_register_address ,
191
204
uint8_t dma_trigger_source ) {
205
+
192
206
// Use two DMA channels to play because the DMA can't wrap to itself without the
193
207
// buffer being power of two aligned.
194
208
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,
213
227
dma -> unsigned_to_signed = false;
214
228
dma -> output_signed = output_signed ;
215
229
dma -> sample_spacing = 1 ;
216
- dma -> first_channel_free = true;
217
230
dma -> output_resolution = output_resolution ;
218
231
dma -> sample_resolution = audiosample_bits_per_sample (sample );
219
232
dma -> output_register_address = output_register_address ;
220
233
221
234
audiosample_reset_buffer (sample , single_channel_output , audio_channel );
222
235
223
- bool single_buffer ;
236
+
237
+ bool single_buffer ; // True if data fits in one single buffer.
238
+
224
239
bool samples_signed ;
225
240
uint32_t max_buffer_length ;
226
241
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,
236
251
max_buffer_length /= dma -> sample_spacing ;
237
252
}
238
253
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 ) {
242
257
return AUDIO_DMA_MEMORY_ERROR ;
243
258
}
244
259
245
- dma -> first_buffer_free = true;
246
260
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 ) {
250
264
return AUDIO_DMA_MEMORY_ERROR ;
251
265
}
252
266
}
@@ -276,9 +290,11 @@ audio_dma_result audio_dma_setup_playback(audio_dma_t *dma,
276
290
channel_config_set_dreq (& c , dma_trigger_source );
277
291
channel_config_set_read_increment (& c , true);
278
292
channel_config_set_write_increment (& c , false);
293
+
279
294
// Chain to the other channel by default.
280
295
channel_config_set_chain_to (& c , dma -> channel [(i + 1 ) % 2 ]);
281
296
dma_channel_set_config (dma -> channel [i ], & c , false /* trigger */ );
297
+
282
298
dma_channel_set_write_addr (dma -> channel [i ], (void * )output_register_address , false /* trigger */ );
283
299
}
284
300
@@ -288,9 +304,9 @@ audio_dma_result audio_dma_setup_playback(audio_dma_t *dma,
288
304
MP_STATE_PORT (playing_audio )[dma -> channel [1 ]] = dma ;
289
305
290
306
// Load the first two blocks up front.
291
- audio_dma_load_next_block (dma );
307
+ audio_dma_load_next_block (dma , 0 );
292
308
if (!single_buffer ) {
293
- audio_dma_load_next_block (dma );
309
+ audio_dma_load_next_block (dma , 1 );
294
310
}
295
311
296
312
// 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,
307
323
channel_config_set_chain_to (& c , dma -> channel [1 ]); // Chain to ourselves so we stop.
308
324
dma_channel_configure (dma -> channel [1 ], & c ,
309
325
& dma_hw -> ch [dma -> channel [0 ]].al3_read_addr_trig , // write address
310
- & dma -> first_buffer , // read address
326
+ & dma -> buffer [ 0 ] , // read address
311
327
1 , // transaction count
312
328
false); // trigger
313
329
} 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
315
331
// we're WFI.
316
332
dma_hw -> inte0 |= (1 << dma -> channel [0 ]) | (1 << dma -> channel [1 ]);
317
333
irq_set_mask_enabled (1 << DMA_IRQ_0 , true);
@@ -402,18 +418,19 @@ bool audio_dma_get_paused(audio_dma_t *dma) {
402
418
}
403
419
404
420
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
+
407
424
dma -> channel [0 ] = NUM_DMA_CHANNELS ;
408
425
dma -> channel [1 ] = NUM_DMA_CHANNELS ;
409
426
}
410
427
411
428
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 ;
414
431
415
- m_free (dma -> second_buffer );
416
- dma -> second_buffer = NULL ;
432
+ m_free (dma -> buffer [ 1 ] );
433
+ dma -> buffer [ 1 ] = NULL ;
417
434
}
418
435
419
436
bool audio_dma_get_playing (audio_dma_t * dma ) {
@@ -433,14 +450,34 @@ STATIC void dma_callback_fun(void *arg) {
433
450
return ;
434
451
}
435
452
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
+ }
437
472
}
438
473
439
474
void isr_dma_0 (void ) {
440
475
for (size_t i = 0 ; i < NUM_DMA_CHANNELS ; i ++ ) {
441
476
uint32_t mask = 1 << i ;
442
477
if ((dma_hw -> intr & mask ) != 0 && MP_STATE_PORT (playing_audio )[i ] != NULL ) {
443
478
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 ;
444
481
background_callback_add (& dma -> callback , dma_callback_fun , (void * )dma );
445
482
dma_hw -> ints0 = mask ;
446
483
}
0 commit comments