8000 Merge pull request #820 from tannewt/pause_audio · godlygeek/circuitpython@734596c · GitHub 8000
[go: up one dir, main page]

Skip to content

Commit 734596c

Browse files
authored
Merge pull request adafruit#820 from tannewt/pause_audio
Add pause/resume control to AudioOut and I2SOut
2 parents d3a5d40 + fc7c25a commit 734596c

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("Not playing");
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+
common_hal_audiobusio_i2sout_resume(self);
241+
}
242+
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("Not playing");
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+
common_hal_audioio_audioout_resume(self);
238+
}
239+
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)
0