Skip to content

Commit 191b143

Browse files
committed
Add PWM based audio playback
See https://learn.adafruit.com/circuitpython-essentials/circuitpython-audio-out to get started. Fixes #4037
1 parent b19c700 commit 191b143

34 files changed

+929
-8
lines changed

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
*.deb binary
1919
*.zip binary
2020
*.pdf binary
21+
*.wav binary
2122

2223
# These should also not be modified by git.
2324
tests/basics/string_cr_conversion.py -text

ports/raspberrypi/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ SRC_C += \
196196
bindings/rp2pio/__init__.c \
197197
common-hal/rp2pio/StateMachine.c \
198198
common-hal/rp2pio/__init__.c \
199+
audio_dma.c \
199200
background.c \
200201
peripherals/pins.c \
201202
fatfs_port.c \

ports/raspberrypi/audio_dma.c

Lines changed: 383 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,383 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2021 Scott Shawcroft for Adafruit Industries
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*/
26+
27+
#include "audio_dma.h"
28+
29+
#include "shared-bindings/audiocore/RawSample.h"
30+
#include "shared-bindings/audiocore/WaveFile.h"
31+
#include "supervisor/background_callback.h"
32+
33+
#include "py/mpstate.h"
34+
#include "py/runtime.h"
35+
36+
#include "src/rp2_common/hardware_irq/include/hardware/irq.h"
37+
38+
#if CIRCUITPY_AUDIOPWMIO || CIRCUITPY_AUDIOBUSIO
39+
40+
#define AUDIO_DMA_CHANNEL_COUNT NUM_DMA_CHANNELS
41+
42+
void audio_dma_convert_signed(audio_dma_t* dma, uint8_t* buffer, uint32_t buffer_length,
43+
uint8_t** output_buffer, uint32_t* output_buffer_length) {
44+
if (dma->first_buffer_free) {
45+
*output_buffer = dma->first_buffer;
46+
} else {
47+
*output_buffer = dma->second_buffer;
48+
}
49+
#pragma GCC diagnostic push
50+
#pragma GCC diagnostic ignored "-Wcast-align"
51+
if (dma->signed_to_unsigned ||
52+
dma->unsigned_to_signed ||
53+
dma->sample_spacing > 1 ||
54+
(dma->sample_resolution != dma->output_resolution)) {
55+
*output_buffer_length = buffer_length / dma->sample_spacing;
56+
uint32_t out_i = 0;
57+
if (dma->sample_resolution <= 8 && dma->output_resolution > 8) {
58+
size_t shift = dma->output_resolution - dma->sample_resolution;
59+
60+
for (uint32_t i = 0; i < buffer_length; i += dma->sample_spacing) {
61+
if (dma->signed_to_unsigned) {
62+
((uint16_t*) *output_buffer)[out_i] = ((uint16_t) ((int8_t*) buffer)[i] + 0x80) << shift;
63+
} else if (dma->unsigned_to_signed) {
64+
((int16_t*) *output_buffer)[out_i] = ((int16_t) ((uint8_t*) buffer)[i] - 0x80) << shift;
65+
} else {
66+
((uint16_t*) *output_buffer)[out_i] = ((uint16_t) ((uint8_t*) buffer)[i]) << shift;
67+
}
68+
out_i += 1;
69+
}
70+
} else if (dma->sample_resolution <= 8 && dma->output_resolution <= 8) {
71+
for (uint32_t i = 0; i < buffer_length; i += dma->sample_spacing) {
72+
if (dma->signed_to_unsigned) {
73+
((uint8_t*) *output_buffer)[out_i] = ((int8_t*) buffer)[i] + 0x80;
74+
} else if (dma->unsigned_to_signed) {
75+
((int8_t*) *output_buffer)[out_i] = ((uint8_t*) buffer)[i] - 0x80;
76+
} else {
77+
((uint8_t*) *output_buffer)[out_i] = ((uint8_t*) buffer)[i];
78+
}
79+
out_i += 1;
80+
}
81+
} else if (dma->sample_resolution > 8 && dma->output_resolution > 8) {
82+
size_t shift = 16 - dma->output_resolution;
83+
for (uint32_t i = 0; i < buffer_length / 2; i += dma->sample_spacing) {
84+
if (dma->signed_to_unsigned) {
85+
((uint16_t*) *output_buffer)[out_i] = ((int16_t*) buffer)[i] + 0x8000;
86+
} else if (dma->unsigned_to_signed) {
87+
((int16_t*) *output_buffer)[out_i] = ((uint16_t*) buffer)[i] - 0x8000;
88+
} else {
89+
((uint16_t*) *output_buffer)[out_i] = ((uint16_t*) buffer)[i];
90+
}
91+
if (dma->output_resolution < 16) {
92+
if (dma->output_signed) {
93+
((int16_t*) *output_buffer)[out_i] = ((int16_t*) *output_buffer)[out_i] >> shift;
94+
} else {
95+
((uint16_t*) *output_buffer)[out_i] = ((uint16_t*) *output_buffer)[out_i] >> shift;
96+
}
97+
}
98+
out_i += 1;
99+
}
100+
}
101+
} else {
102+
*output_buffer = buffer;
103+
*output_buffer_length = buffer_length;
104+
}
105+
#pragma GCC diagnostic pop
106+
dma->first_buffer_free = !dma->first_buffer_free;
107+
}
108+
109+
void audio_dma_load_next_block(audio_dma_t* dma) {
110+
uint8_t dma_channel = dma->channel[1];
111+
if (dma->first_channel_free) {
112+
dma_channel = dma->channel[0];
113+
}
114+
dma->first_channel_free = !dma->first_channel_free;
115+
116+
uint8_t* output_buffer;
117+
uint32_t output_buffer_length;
118+
audioio_get_buffer_result_t get_buffer_result;
119+
uint8_t* buffer;
120+
uint32_t buffer_length;
121+
get_buffer_result = audiosample_get_buffer(dma->sample,
122+
dma->single_channel, dma->audio_channel, &buffer, &buffer_length);
123+
124+
if (get_buffer_result == GET_BUFFER_ERROR) {
125+
audio_dma_stop(dma);
126+
return;
127+
}
128+
129+
audio_dma_convert_signed(dma, buffer, buffer_length, &output_buffer, &output_buffer_length);
130+
131+
// If we don't have an output buffer, save the pointer to first_buffer for use in the single
132+
// buffer special case.
133+
if (dma->first_buffer == NULL) {
134+
dma->first_buffer = output_buffer;
135+
}
136+
137+
dma_channel_set_trans_count(dma_channel, output_buffer_length / dma->output_size, false /* trigger */);
138+
dma_channel_set_read_addr(dma_channel, output_buffer, false /* trigger */);
139+
if (get_buffer_result == GET_BUFFER_DONE) {
140+
if (dma->loop) {
141+
audiosample_reset_buffer(dma->sample, dma->single_channel, dma->audio_channel);
142+
} else {
143+
// Set channel trigger to ourselves so we don't keep going.
144+
dma_channel_hw_t* c = &dma_hw->ch[dma_channel];
145+
c->al1_ctrl = (c->al1_ctrl & ~DMA_CH0_CTRL_TRIG_CHAIN_TO_BITS) | (dma_channel << DMA_CH0_CTRL_TRIG_CHAIN_TO_LSB);
146+
}
147+
}
148+
}
149+
150+
// Playback should be shutdown before calling this.
151+
audio_dma_result audio_dma_setup_playback(audio_dma_t* dma,
152+
mp_obj_t sample,
153+
bool loop,
154+
bool single_channel,
155+
uint8_t audio_channel,
156+
bool output_signed,
157+
uint8_t output_resolution,
158+
uint32_t output_register_address,
159+
uint8_t dma_trigger_source) {
160+
// Use two DMA channels to because the DMA can't wrap to itself without the
161+
// buffer being power of two aligned.
162+
dma->channel[0] = dma_claim_unused_channel(false);
163+
dma->channel[1] = dma_claim_unused_channel(false);
164+
if (dma->channel[0] == NUM_DMA_CHANNELS || dma->channel[1] == NUM_DMA_CHANNELS) {
165+
if (dma->channel[0] < NUM_DMA_CHANNELS) {
166+
dma_channel_unclaim(dma->channel[0]);
167+
}
168+
return AUDIO_DMA_DMA_BUSY;
169+
}
170+
171+
dma->sample = sample;
172+
dma->loop = loop;
173+
dma->single_channel = single_channel;
174+
dma->audio_channel = audio_channel;
175+
dma->signed_to_unsigned = false;
176+
dma->unsigned_to_signed = false;
177+
dma->output_signed = output_signed;
178+
dma->sample_spacing = 1;
179+
dma->first_channel_free = true;
180+
dma->output_resolution = output_resolution;
181+
dma->sample_resolution = audiosample_bits_per_sample(sample);
182+
audiosample_reset_buffer(sample, single_channel, audio_channel);
183+
184+
bool single_buffer;
185+
bool samples_signed;
186+
uint32_t max_buffer_length;
187+
audiosample_get_buffer_structure(sample, single_channel, &single_buffer, &samples_signed,
188+
&max_buffer_length, &dma->sample_spacing);
189+
190+
// Check to see if we have to scale the resolution up.
191+
if (dma->sample_resolution <= 8 && dma->output_resolution > 8) {
192+
max_buffer_length *= 2;
193+
}
194+
if (output_signed != samples_signed ||
195+
dma->sample_spacing > 1 ||
196+
(dma->sample_resolution != dma->output_resolution)) {
197+
max_buffer_length /= dma->sample_spacing;
198+
dma->first_buffer = (uint8_t*) m_realloc(dma->first_buffer, max_buffer_length);
199+
if (dma->first_buffer == NULL) {
200+
return AUDIO_DMA_MEMORY_ERROR;
201+
}
202+
203+
dma->first_buffer_free = true;
204+
if (!single_buffer) {
205+
dma->second_buffer = (uint8_t*) m_realloc(dma->second_buffer, max_buffer_length);
206+
if (dma->second_buffer == NULL) {
207+
return AUDIO_DMA_MEMORY_ERROR;
208+
}
209+
}
210+
dma->signed_to_unsigned = !output_signed && samples_signed;
211+
dma->unsigned_to_signed = output_signed && !samples_signed;
212+
}
213+
214+
if (output_resolution > 8) {
215+
dma->output_size = 2;
216+
} else {
217+
dma->output_size = 1;
218+
}
219+
// Transfer both channels at once.
220+
if (!single_channel && audiosample_channel_count(sample) == 2) {
221+
dma->output_size *= 2;
222+
}
223+
224+
enum dma_channel_transfer_size dma_size = DMA_SIZE_8;
225+
if (dma->output_size == 2) {
226+
dma_size = DMA_SIZE_16;
227+
} else if (dma->output_size == 4) {
228+
dma_size = DMA_SIZE_32;
229+
}
230+
231+
for (size_t i = 0; i < 2; i++) {
232+
dma_channel_config c = dma_channel_get_default_config(dma->channel[i]);
233+
channel_config_set_transfer_data_size(&c, dma_size);
234+
channel_config_set_dreq(&c, dma_trigger_source);
235+
channel_config_set_read_increment(&c, true);
236+
channel_config_set_write_increment(&c, false);
237+
// Chain to the other channel by default.
238+
channel_config_set_chain_to(&c, dma->channel[(i + 1) % 2]);
239+
dma_channel_set_config(dma->channel[i], &c, false /* trigger */);
240+
dma_channel_set_write_addr(dma->channel[i], (void*) output_register_address, false /* trigger */);
241+
}
242+
243+
// We keep the audio_dma_t for internal use and the sample as a root pointer because it
244+
// contains the audiodma structure.
245+
MP_STATE_PORT(playing_audio)[dma->channel[0]] = dma;
246+
MP_STATE_PORT(playing_audio)[dma->channel[1]] = dma;
247+
248+
// Load the first two blocks up front.
249+
audio_dma_load_next_block(dma);
250+
if (!single_buffer) {
251+
audio_dma_load_next_block(dma);
252+
}
253+
254+
// Special case the DMA for a single buffer. It's commonly used for a single wave length of sound
255+
// and may be short. Therefore, we use DMA chaining to loop quickly without involving interrupts.
256+
// On the RP2040 we chain by having a second DMA writing to the config registers of the first.
257+
// Read and write addresses change with DMA so we need to reset the read address back to the
258+
// start of the sample.
259+
if (single_buffer) {
260+
dma_channel_config c = dma_channel_get_default_config(dma->channel[1]);
261+
channel_config_set_transfer_data_size(&c, DMA_SIZE_32);
262+
channel_config_set_dreq(&c, 0x3f); // dma as fast as possible
263+
channel_config_set_read_increment(&c, false);
264+
channel_config_set_write_increment(&c, false);
265+
channel_config_set_chain_to(&c, dma->channel[1]); // Chain to ourselves so we stop.
266+
dma_channel_configure(dma->channel[1], &c,
267+
&dma_hw->ch[dma->channel[0]].al3_read_addr_trig, // write address
268+
&dma->first_buffer, // read address
269+
1, // transaction count
270+
false); // trigger
271+
} else {
272+
// Enable our DMA channels on DMA0 to the CPU. This will wake us up when
273+
// we're WFI.
274+
dma_hw->inte0 |= (1 << dma->channel[0]) | (1 << dma->channel[1]);
275+
irq_set_mask_enabled(1 << DMA_IRQ_0, true);
276+
}
277+
278+
dma_channel_start(dma->channel[0]);
279+
280+
return AUDIO_DMA_OK;
281+
}
282+
283+
void audio_dma_stop(audio_dma_t* dma) {
284+
// Disable our interrupts.
285+
dma_hw->inte0 &= ~((1 << dma->channel[0]) | (1 << dma->channel[1]));
286+
irq_set_mask_enabled(1 << DMA_IRQ_0, false);
287+
288+
// Run any remaining audio tasks because we remove ourselves from
289+
// playing_audio.
290+
RUN_BACKGROUND_TASKS;
291+
292+
for (size_t i = 0; i < 2; i++) {
293+
size_t channel = dma->channel[i];
294+
295+
if (dma_channel_is_busy(channel)) {
296+
dma_channel_abort(channel);
297+
}
298+
dma_channel_unclaim(channel);
299+
MP_STATE_PORT(playing_audio)[channel] = NULL;
300+
dma->channel[i] = NUM_DMA_CHANNELS;
301+
}
302+
303+
// Hold onto our buffers.
304+
}
305+
306+
// To pause we simply stop the DMA. It is the responsibility of the output peripheral
307+
// to hold the previous value.
308+
void audio_dma_pause(audio_dma_t* dma) {
309+
dma_hw->ch[dma->channel[0]].al1_ctrl &= ~DMA_CH0_CTRL_TRIG_EN_BITS;
310+
dma_hw->ch[dma->channel[1]].al1_ctrl &= ~DMA_CH0_CTRL_TRIG_EN_BITS;
311+
}
312+
313+
void audio_dma_resume(audio_dma_t* dma) {
314+
// Always re-enable the non-busy channel first so it's ready to continue when the busy channel
315+
// finishes and chains to it. (An interrupt could make the time between enables long.)
316+
size_t first = 0;
317+
size_t second = 1;
318+
if (dma_channel_is_busy(dma->channel[0])) {
319+
first = 1;
320+
second = 0;
321+
}
322+
dma_hw->ch[dma->channel[first]].al1_ctrl |= DMA_CH0_CTRL_TRIG_EN_BITS;
323+
dma_hw->ch[dma->channel[second]].al1_ctrl |= DMA_CH0_CTRL_TRIG_EN_BITS;
324+
}
325+
326+
bool audio_dma_get_paused(audio_dma_t* dma) {
327+
if (dma->channel[0] >= AUDIO_DMA_CHANNEL_COUNT) {
328+
return false;
329+
}
330+
uint32_t control = dma_hw->ch[dma->channel[0]].ctrl_trig;
331+
332+
return (control & DMA_CH0_CTRL_TRIG_EN_BITS) == 0;
333+
}
334+
335+
void audio_dma_init(audio_dma_t* dma) {
336+
dma->first_buffer = NULL;
337+
dma->second_buffer = NULL;
338+
}
339+
340+
void audio_dma_deinit(audio_dma_t* dma) {
341+
m_free(dma->first_buffer);
342+
dma->first_buffer = NULL;
343+
344+
m_free(dma->second_buffer);
345+
dma->second_buffer = NULL;
346+
}
347+
348+
bool audio_dma_get_playing(audio_dma_t* dma) {
349+
if (dma->channel[0] == NUM_DMA_CHANNELS) {
350+
return false;
351+
}
352+
if (!dma_channel_is_busy(dma->channel[0]) &&
353+
!dma_channel_is_busy(dma->channel[1])) {
354+
audio_dma_stop(dma);
355+
return false;
356+
}
357+
358+
return true;
359+
}
360+
361+
// WARN(tannewt): DO NOT print from here, or anything it calls. Printing calls
362+
// background tasks such as this and causes a stack overflow.
363+
STATIC void dma_callback_fun(void *arg) {
364+
audio_dma_t* dma = arg;
365+
if (dma == NULL) {
366+
return;
367+
}
368+
369+
audio_dma_load_next_block(dma);
370+
}
371+
372+
void isr_dma_0(void) {
373+
for (size_t i = 0; i < NUM_DMA_CHANNELS; i++) {
374+
uint32_t mask = 1 << i;
375+
if ((dma_hw->intr & mask) != 0 && MP_STATE_PORT(playing_audio)[i] != NULL) {
376+
audio_dma_t* dma = MP_STATE_PORT(playing_audio)[i];
377+
background_callback_add(&dma->callback, dma_callback_fun, (void*)dma);
378+
dma_hw->ints0 = mask;
379+
}
380+
}
381+
}
382+
383+
#endif

0 commit comments

Comments
 (0)