Skip to content

Commit 50fc90b

Browse files
committed
Add pause/resume control to AudioOut and I2SOut
Fixes adafruit#808
1 parent d3a5d40 commit 50fc90b

File tree

10 files changed

+215
-4
lines changed

10 files changed

+215
-4
lines changed

ports/atmel-samd/audio_dma.c

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,23 @@ void audio_dma_stop(audio_dma_t* dma) {
323323
dma->dma_channel = AUDIO_DMA_CHANNEL_COUNT;
324324
}
325325

326+
void audio_dma_pause(audio_dma_t* dma) {
327+
dma_suspend_channel(dma->dma_channel);
328+
}
329+
330+
void audio_dma_resume(audio_dma_t* dma) {
331+
dma_resume_channel(dma->dma_channel);
332+
}
333+
334+
bool audio_dma_get_paused(audio_dma_t* dma) {
335+
if (dma->dma_channel >= AUDIO_DMA_CHANNEL_COUNT) {
336+
return false;
337+
}
338+
uint32_t status = dma_transfer_status(dma->dma_channel);
339+
340+
return (status & DMAC_CHINTFLAG_SUSP) != 0;
341+
}
342+
326343
void audio_dma_init(audio_dma_t* dma) {
327344
dma->dma_channel = AUDIO_DMA_CHANNEL_COUNT;
328345
}
@@ -341,11 +358,11 @@ bool audio_dma_get_playing(audio_dma_t* dma) {
341358
return false;
342359
}
343360
uint32_t status = dma_transfer_status(dma->dma_channel);
344-
if ((status & DMAC_CHINTFLAG_TCMPL) != 0) {
361+
if ((status & DMAC_CHINTFLAG_TCMPL) != 0 || (status & DMAC_CHINTFLAG_TERR) != 0) {
345362
audio_dma_stop(dma);
346363
}
347364

348-
return status == 0;
365+
return (status & DMAC_CHINTFLAG_TERR) == 0;
349366
}
350367

351368
// WARN(tannewt): DO NOT print from here. Printing calls background tasks such as this and causes a

ports/atmel-samd/audio_dma.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ audio_dma_result audio_dma_setup_playback(audio_dma_t* dma,
8585
uint8_t dma_trigger_source);
8686
void audio_dma_stop(audio_dma_t* dma);
8787
bool audio_dma_get_playing(audio_dma_t* dma);
88+
void audio_dma_pause(audio_dma_t* dma);
89+
void audio_dma_resume(audio_dma_t* dma);
90+
bool audio_dma_get_paused(audio_dma_t* dma);
8891

8992
void audio_dma_background(void);
9093

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,26 @@ void common_hal_audiobusio_i2sout_play(audiobusio_i2sout_obj_t* self,
324324
self->playing = true;
325325
}
326326

327+
void common_hal_audiobusio_i2sout_pause(audiobusio_i2sout_obj_t* self) {
328+
audio_dma_pause(&self->dma);
329+
}
330+
331+
void common_hal_audiobusio_i2sout_resume(audiobusio_i2sout_obj_t* self) {
332+
// Clear any overrun/underrun errors
333+
#ifdef SAMD21
334+
I2S->INTFLAG.reg = I2S_INTFLAG_TXUR0 << self->serializer;
335+
#endif
336+
#ifdef SAMD51
337+
I2S->INTFLAG.reg = I2S_INTFLAG_TXUR0 | I2S_INTFLAG_TXUR1;
338+
#endif
339+
340+
audio_dma_resume(&self->dma);
341+
}
342+
343+
bool common_hal_audiobusio_i2sout_get_paused(audiobusio_i2sout_obj_t* self) {
344+
return audio_dma_get_paused(&self->dma);
345+
}
346+
327347
void common_hal_audiobusio_i2sout_stop(audiobusio_i2sout_obj_t* self) {
328348
audio_dma_stop(&self->dma);
329349

ports/atmel-samd/common-hal/audioio/AudioOut.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,32 @@ void common_hal_audioio_audioout_play(audioio_audioout_obj_t* self,
327327
self->playing = true;
328328
}
329329

330+
void common_hal_audioio_audioout_pause(audioio_audioout_obj_t* self) {
331+
audio_dma_pause(&self->left_dma);
332+
#ifdef SAMD51
333+
audio_dma_pause(&self->right_dma);
334+
#endif
335+
}
336+
337+
void common_hal_audioio_audioout_resume(audioio_audioout_obj_t* self) {
338+
// Clear any overrun/underrun errors
339+
#ifdef SAMD21
340+
DAC->INTFLAG.reg = DAC_INTFLAG_UNDERRUN;
341+
#endif
342+
#ifdef SAMD51
343+
DAC->INTFLAG.reg = DAC_INTFLAG_UNDERRUN0 | DAC_INTFLAG_UNDERRUN1;
344+
#endif
345+
346+
audio_dma_resume(&self->left_dma);
347+
#ifdef SAMD51
348+
audio_dma_resume(&self->right_dma);
349+
#endif
350+
}
351+
352+
bool common_hal_audioio_audioout_get_paused(audioio_audioout_obj_t* self) {
353+
return audio_dma_get_paused(&self->left_dma);
354+
}
355+
330356
void common_hal_audioio_audioout_stop(audioio_audioout_obj_t* self) {
331357
Tc* timer = tc_insts[self->tc_index];
332358
timer->COUNT16.CTRLBSET.reg = TC_CTRLBSET_CMD_STOP;

ports/atmel-samd/shared_dma.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,37 @@ void dma_disable_channel(uint8_t channel_number) {
128128
#endif
129129
}
130130

131+
void dma_suspend_channel(uint8_t channel_number) {
132+
#ifdef SAMD21
133+
common_hal_mcu_disable_interrupts();
134+
/** Select the DMA channel and clear software trigger */
135+
DMAC->CHID.reg = DMAC_CHID_ID(channel_number);
136+
DMAC->CHCTRLB.bit.CMD = DMAC_CHCTRLB_CMD_SUSPEND_Val;
137+
common_hal_mcu_enable_interrupts();
138+
#endif
139+
140+
#ifdef SAMD51
141+
DmacChannel* channel = &DMAC->Channel[channel_number];
142+
channel->CHCTRLB.reg = DMAC_CHCTRLB_CMD_SUSPEND;
143+
#endif
144+
}
145+
146+
void dma_resume_channel(uint8_t channel_number) {
147+
#ifdef SAMD21
148+
common_hal_mcu_disable_interrupts();
149+
/** Select the DMA channel and clear software trigger */
150+
DMAC->CHID.reg = DMAC_CHID_ID(channel_number);
151+
DMAC->CHCTRLB.bit.CMD = DMAC_CHCTRLB_CMD_RESUME_Val;
152+
DMAC->CHINTFLAG.reg = DMAC_CHINTFLAG_SUSP;
153+
common_hal_mcu_enable_interrupts();
154+
#endif
155+
156+
#ifdef SAMD51
157+
DmacChannel* channel = &DMAC->Channel[channel_number];
158+
channel->CHCTRLB.reg = DMAC_CHCTRLB_CMD_RESUME;
159+
#endif
160+
}
161+
131162
bool dma_channel_enabled(uint8_t channel_number) {
132163
#ifdef SAMD21
133164
common_hal_mcu_disable_interrupts();

ports/atmel-samd/shared_dma.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ int32_t sercom_dma_transfer(Sercom* sercom, const uint8_t* buffer_out, uint8_t*
5656
void dma_configure(uint8_t channel_number, uint8_t trigsrc, bool output_event);
5757
void dma_enable_channel(uint8_t channel_number);
5858
void dma_disable_channel(uint8_t channel_number);
59+
void dma_suspend_channel(uint8_t channel_number);
60+
void dma_resume_channel(uint8_t channel_number);
5961
bool dma_channel_enabled(uint8_t channel_number);
6062
uint8_t dma_transfer_status(uint8_t channel_number);
6163
DmacDescriptor* dma_descriptor(uint8_t channel_number);

shared-bindings/audiobusio/I2SOut.c

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,16 +212,69 @@ const mp_obj_property_t audiobusio_i2sout_playing_obj = {
212212
(mp_obj_t)&mp_const_none_obj},
213213
};
214214

215+
//| .. method:: pause()
216+
//|
217+
//| Stops playback temporarily while remembering the position. Use `resume` to resume playback.
218+
//|
219+
STATIC mp_obj_t audiobusio_i2sout_obj_pause(mp_obj_t self_in) {
220+
audiobusio_i2sout_obj_t *self = MP_OBJ_TO_PTR(self_in);
221+
raise_error_if_deinited(common_hal_audiobusio_i2sout_deinited(self));
222+
223+
if (!common_hal_audiobusio_i2sout_get_playing(self)) {
224+
mp_raise_RuntimeError("No sample playing cannot pause");
225+
}
226+
common_hal_audiobusio_i2sout_pause(self);
227+
return mp_const_none;
228+
}
229+
MP_DEFINE_CONST_FUN_OBJ_1(audiobusio_i2sout_pause_obj, audiobusio_i2sout_obj_pause);
230+
231+
//| .. method:: resume()
232+
//|
233+
//| Resumes sample playback after :py:func:`pause`.
234+
//|
235+
STATIC mp_obj_t audiobusio_i2sout_obj_resume(mp_obj_t self_in) {
236+
audiobusio_i2sout_obj_t *self = MP_OBJ_TO_PTR(self_in);
237+
raise_error_if_deinited(common_hal_audiobusio_i2sout_deinited(self));
238+
239+
if (!common_hal_audiobusio_i2sout_get_paused(self)) {
240+
mp_raise_RuntimeError("No paused sample");
241+
}
242+
common_hal_audiobusio_i2sout_resume(self);
243+
return mp_const_none;
244+
}
245+
MP_DEFINE_CONST_FUN_OBJ_1(audiobusio_i2sout_resume_obj, audiobusio_i2sout_obj_resume);
246+
247+
//| .. attribute:: paused
248+
//|
249+
//| True when playback is paused. (read-only)
250+
//|
251+
STATIC mp_obj_t audiobusio_i2sout_obj_get_paused(mp_obj_t self_in) {
252+
audiobusio_i2sout_obj_t *self = MP_OBJ_TO_PTR(self_in);
253+
raise_error_if_deinited(common_hal_audiobusio_i2sout_deinited(self));
254+
return mp_obj_new_bool(common_hal_audiobusio_i2sout_get_paused(self));
255+
}
256+
MP_DEFINE_CONST_FUN_OBJ_1(audiobusio_i2sout_get_paused_obj, audiobusio_i2sout_obj_get_paused);
257+
258+
const mp_obj_property_t audiobusio_i2sout_paused_obj = {
259+
.base.type = &mp_type_property,
260+
.proxy = {(mp_obj_t)&audiobusio_i2sout_get_paused_obj,
261+
(mp_obj_t)&mp_const_none_obj,
262+
(mp_obj_t)&mp_const_none_obj},
263+
};
264+
215265
STATIC const mp_rom_map_elem_t audiobusio_i2sout_locals_dict_table[] = {
216266
// Methods
217267
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&audiobusio_i2sout_deinit_obj) },
218268
{ MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) },
219269
{ MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&audiobusio_i2sout___exit___obj) },
220270
{ MP_ROM_QSTR(MP_QSTR_play), MP_ROM_PTR(&audiobusio_i2sout_play_obj) },
221271
{ MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&audiobusio_i2sout_stop_obj) },
272+
{ MP_ROM_QSTR(MP_QSTR_pause), MP_ROM_PTR(&audiobusio_i2sout_pause_obj) },
273+
{ MP_ROM_QSTR(MP_QSTR_resume), MP_ROM_PTR(&audiobusio_i2sout_resume_obj) },
222274

223275
// Properties
224276
{ MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&audiobusio_i2sout_playing_obj) },
277+
{ MP_ROM_QSTR(MP_QSTR_paused), MP_ROM_PTR(&audiobusio_i2sout_paused_obj) },
225278
};
226279
STATIC MP_DEFINE_CONST_DICT(audiobusio_i2sout_locals_dict, audiobusio_i2sout_locals_dict_table);
227280

shared-bindings/audiobusio/I2SOut.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,5 +41,8 @@ bool common_hal_audiobusio_i2sout_deinited(audiobusio_i2sout_obj_t* self);
4141
void common_hal_audiobusio_i2sout_play(audiobusio_i2sout_obj_t* self, mp_obj_t sample, bool loop);
4242
void common_hal_audiobusio_i2sout_stop(audiobusio_i2sout_obj_t* self);
4343
bool common_hal_audiobusio_i2sout_get_playing(audiobusio_i2sout_obj_t* self);
44+
void common_hal_audiobusio_i2sout_pause(audiobusio_i2sout_obj_t* self);
45+
void common_hal_audiobusio_i2sout_resume(audiobusio_i2sout_obj_t* self);
46+
bool common_hal_audiobusio_i2sout_get_paused(audiobusio_i2sout_obj_t* self);
4447

4548
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOBUSIO_I2SOUT_H

shared-bindings/audioio/AudioOut.c

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ MP_DEFINE_CONST_FUN_OBJ_KW(audioio_audioout_play_obj, 1, audioio_audioout_obj_pl
181181

182182
//| .. method:: stop()
183183
//|
184-
//| Stops playback.
184+
//| Stops playback and resets to the start of the sample.
185185
//|
186186
STATIC mp_obj_t audioio_audioout_obj_stop(mp_obj_t self_in) {
187187
audioio_audioout_obj_t *self = MP_OBJ_TO_PTR(self_in);
@@ -193,7 +193,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(audioio_audioout_stop_obj, audioio_audioout_obj_stop);
193193

194194
//| .. attribute:: playing
195195
//|
196-
//| True when an audio sample is being output. (read-only)
196+
//| True when an audio sample is being output even if `paused`. (read-only)
197197
//|
198198
STATIC mp_obj_t audioio_audioout_obj_get_playing(mp_obj_t self_in) {
199199
audioio_audioout_obj_t *self = MP_OBJ_TO_PTR(self_in);
@@ -209,16 +209,69 @@ const mp_obj_property_t audioio_audioout_playing_obj = {
209209
(mp_obj_t)&mp_const_none_obj},
210210
};
211211

212+
//| .. method:: pause()
213+
//|
214+
//| Stops playback temporarily while remembering the position. Use `resume` to resume playback.
215+
//|
216+
STATIC mp_obj_t audioio_audioout_obj_pause(mp_obj_t self_in) {
217+
audioio_audioout_obj_t *self = MP_OBJ_TO_PTR(self_in);
218+
raise_error_if_deinited(common_hal_audioio_audioout_deinited(self));
219+
220+
if (!common_hal_audioio_audioout_get_playing(self)) {
221+
mp_raise_RuntimeError("No sample playing cannot pause");
222+
}
223+
common_hal_audioio_audioout_pause(self);
224+
return mp_const_none;
225+
}
226+
MP_DEFINE_CONST_FUN_OBJ_1(audioio_audioout_pause_obj, audioio_audioout_obj_pause);
227+
228+
//| .. method:: resume()
229+
//|
230+
//| Resumes sample playback after :py:func:`pause`.
231+
//|
232+
STATIC mp_obj_t audioio_audioout_obj_resume(mp_obj_t self_in) {
233+
audioio_audioout_obj_t *self = MP_OBJ_TO_PTR(self_in);
234+
raise_error_if_deinited(common_hal_audioio_audioout_deinited(self));
235+
236+
if (!common_hal_audioio_audioout_get_paused(self)) {
237+
mp_raise_RuntimeError("No paused sample");
238+
}
239+
common_hal_audioio_audioout_resume(self);
240+
return mp_const_none;
241+
}
242+
MP_DEFINE_CONST_FUN_OBJ_1(audioio_audioout_resume_obj, audioio_audioout_obj_resume);
243+
244+
//| .. attribute:: paused
245+
//|
246+
//| True when playback is paused. (read-only)
247+
//|
248+
STATIC mp_obj_t audioio_audioout_obj_get_paused(mp_obj_t self_in) {
249+
audioio_audioout_obj_t *self = MP_OBJ_TO_PTR(self_in);
250+
raise_error_if_deinited(common_hal_audioio_audioout_deinited(self));
251+
return mp_obj_new_bool(common_hal_audioio_audioout_get_paused(self));
252+
}
253+
MP_DEFINE_CONST_FUN_OBJ_1(audioio_audioout_get_paused_obj, audioio_audioout_obj_get_paused);
254+
255+
const mp_obj_property_t audioio_audioout_paused_obj = {
256+
.base.type = &mp_type_property,
257+
.proxy = {(mp_obj_t)&audioio_audioout_get_paused_obj,
258+
(mp_obj_t)&mp_const_none_obj,
259+
(mp_obj_t)&mp_const_none_obj},
260+
};
261+
212262
STATIC const mp_rom_map_elem_t audioio_audioout_locals_dict_table[] = {
213263
// Methods
214264
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&audioio_audioout_deinit_obj) },
215265
{ MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) },
216266
{ MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&audioio_audioout___exit___obj) },
217267
{ MP_ROM_QSTR(MP_QSTR_play), MP_ROM_PTR(&audioio_audioout_play_obj) },
218268
{ MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&audioio_audioout_stop_obj) },
269+
{ MP_ROM_QSTR(MP_QSTR_pause), MP_ROM_PTR(&audioio_audioout_pause_obj) },
270+
{ MP_ROM_QSTR(MP_QSTR_resume), MP_ROM_PTR(&audioio_audioout_resume_obj) },
219271

220272
// Properties
221273
{ MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&audioio_audioout_playing_obj) },
274+
{ MP_ROM_QSTR(MP_QSTR_paused), MP_ROM_PTR(&audioio_audioout_paused_obj) },
222275
};
223276
STATIC MP_DEFINE_CONST_DICT(audioio_audioout_locals_dict, audioio_audioout_locals_dict_table);
224277

shared-bindings/audioio/AudioOut.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,8 @@ bool common_hal_audioio_audioout_deinited(audioio_audioout_obj_t* self);
4242
void common_hal_audioio_audioout_play(audioio_audioout_obj_t* self, mp_obj_t sample, bool loop);
4343
void common_hal_audioio_audioout_stop(audioio_audioout_obj_t* self);
4444
bool common_hal_audioio_audioout_get_playing(audioio_audioout_obj_t* self);
45+
void common_hal_audioio_audioout_pause(audioio_audioout_obj_t* self);
46+
void common_hal_audioio_audioout_resume(audioio_audioout_obj_t* self);
47+
bool common_hal_audioio_audioout_get_paused(audioio_audioout_obj_t* self);
4548

4649
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOIO_AUDIOOUT_H

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy