summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorScott Shawcroft <scott.shawcroft@gmail.com>2018-05-07 17:47:29 -0700
committerScott Shawcroft <scott.shawcroft@gmail.com>2018-05-08 11:53:13 -0700
commit50fc90bc5f74dbd7c01e53b02650127ca408f0ae (patch)
treede5b3c67000c30d1ad96bb7a6abbeab53c966358
parentd3a5d40374043176843a02a4f04204746187883b (diff)
Add pause/resume control to AudioOut and I2SOut
Fixes #808
-rw-r--r--ports/atmel-samd/audio_dma.c21
-rw-r--r--ports/atmel-samd/audio_dma.h3
-rw-r--r--ports/atmel-samd/common-hal/audiobusio/I2SOut.c20
-rw-r--r--ports/atmel-samd/common-hal/audioio/AudioOut.c26
-rw-r--r--ports/atmel-samd/shared_dma.c31
-rw-r--r--ports/atmel-samd/shared_dma.h2
-rw-r--r--shared-bindings/audiobusio/I2SOut.c53
-rw-r--r--shared-bindings/audiobusio/I2SOut.h3
-rw-r--r--shared-bindings/audioio/AudioOut.c57
-rw-r--r--shared-bindings/audioio/AudioOut.h3
10 files changed, 215 insertions, 4 deletions
diff --git a/ports/atmel-samd/audio_dma.c b/ports/atmel-samd/audio_dma.c
index b468acf51..e8dbdc61d 100644
--- a/ports/atmel-samd/audio_dma.c
+++ b/ports/atmel-samd/audio_dma.c
@@ -323,6 +323,23 @@ void audio_dma_stop(audio_dma_t* dma) {
dma->dma_channel = AUDIO_DMA_CHANNEL_COUNT;
}
+void audio_dma_pause(audio_dma_t* dma) {
+ dma_suspend_channel(dma->dma_channel);
+}
+
+void audio_dma_resume(audio_dma_t* dma) {
+ dma_resume_channel(dma->dma_channel);
+}
+
+bool audio_dma_get_paused(audio_dma_t* dma) {
+ if (dma->dma_channel >= AUDIO_DMA_CHANNEL_COUNT) {
+ return false;
+ }
+ uint32_t status = dma_transfer_status(dma->dma_channel);
+
+ return (status & DMAC_CHINTFLAG_SUSP) != 0;
+}
+
void audio_dma_init(audio_dma_t* dma) {
dma->dma_channel = AUDIO_DMA_CHANNEL_COUNT;
}
@@ -341,11 +358,11 @@ bool audio_dma_get_playing(audio_dma_t* dma) {
return false;
}
uint32_t status = dma_transfer_status(dma->dma_channel);
- if ((status & DMAC_CHINTFLAG_TCMPL) != 0) {
+ if ((status & DMAC_CHINTFLAG_TCMPL) != 0 || (status & DMAC_CHINTFLAG_TERR) != 0) {
audio_dma_stop(dma);
}
- return status == 0;
+ return (status & DMAC_CHINTFLAG_TERR) == 0;
}
// WARN(tannewt): DO NOT print from here. Printing calls background tasks such as this and causes a
diff --git a/ports/atmel-samd/audio_dma.h b/ports/atmel-samd/audio_dma.h
index fd2e6a153..6734adb64 100644
--- a/ports/atmel-samd/audio_dma.h
+++ b/ports/atmel-samd/audio_dma.h
@@ -85,6 +85,9 @@ audio_dma_result audio_dma_setup_playback(audio_dma_t* dma,
uint8_t dma_trigger_source);
void audio_dma_stop(audio_dma_t* dma);
bool audio_dma_get_playing(audio_dma_t* dma);
+void audio_dma_pause(audio_dma_t* dma);
+void audio_dma_resume(audio_dma_t* dma);
+bool audio_dma_get_paused(audio_dma_t* dma);
void audio_dma_background(void);
diff --git a/ports/atmel-samd/common-hal/audiobusio/I2SOut.c b/ports/atmel-samd/common-hal/audiobusio/I2SOut.c
index 65dae5dcb..f4a8eb52e 100644
--- a/ports/atmel-samd/common-hal/audiobusio/I2SOut.c
+++ b/ports/atmel-samd/common-hal/audiobusio/I2SOut.c
@@ -324,6 +324,26 @@ void common_hal_audiobusio_i2sout_play(audiobusio_i2sout_obj_t* self,
self->playing = true;
}
+void common_hal_audiobusio_i2sout_pause(audiobusio_i2sout_obj_t* self) {
+ audio_dma_pause(&self->dma);
+}
+
+void common_hal_audiobusio_i2sout_resume(audiobusio_i2sout_obj_t* self) {
+ // Clear any overrun/underrun errors
+ #ifdef SAMD21
+ I2S->INTFLAG.reg = I2S_INTFLAG_TXUR0 << self->serializer;
+ #endif
+ #ifdef SAMD51
+ I2S->INTFLAG.reg = I2S_INTFLAG_TXUR0 | I2S_INTFLAG_TXUR1;
+ #endif
+
+ audio_dma_resume(&self->dma);
+}
+
+bool common_hal_audiobusio_i2sout_get_paused(audiobusio_i2sout_obj_t* self) {
+ return audio_dma_get_paused(&self->dma);
+}
+
void common_hal_audiobusio_i2sout_stop(audiobusio_i2sout_obj_t* self) {
audio_dma_stop(&self->dma);
diff --git a/ports/atmel-samd/common-hal/audioio/AudioOut.c b/ports/atmel-samd/common-hal/audioio/AudioOut.c
index 3d03bfd67..6971c020b 100644
--- a/ports/atmel-samd/common-hal/audioio/AudioOut.c
+++ b/ports/atmel-samd/common-hal/audioio/AudioOut.c
@@ -327,6 +327,32 @@ void common_hal_audioio_audioout_play(audioio_audioout_obj_t* self,
self->playing = true;
}
+void common_hal_audioio_audioout_pause(audioio_audioout_obj_t* self) {
+ audio_dma_pause(&self->left_dma);
+ #ifdef SAMD51
+ audio_dma_pause(&self->right_dma);
+ #endif
+}
+
+void common_hal_audioio_audioout_resume(audioio_audioout_obj_t* self) {
+ // Clear any overrun/underrun errors
+ #ifdef SAMD21
+ DAC->INTFLAG.reg = DAC_INTFLAG_UNDERRUN;
+ #endif
+ #ifdef SAMD51
+ DAC->INTFLAG.reg = DAC_INTFLAG_UNDERRUN0 | DAC_INTFLAG_UNDERRUN1;
+ #endif
+
+ audio_dma_resume(&self->left_dma);
+ #ifdef SAMD51
+ audio_dma_resume(&self->right_dma);
+ #endif
+}
+
+bool common_hal_audioio_audioout_get_paused(audioio_audioout_obj_t* self) {
+ return audio_dma_get_paused(&self->left_dma);
+}
+
void common_hal_audioio_audioout_stop(audioio_audioout_obj_t* self) {
Tc* timer = tc_insts[self->tc_index];
timer->COUNT16.CTRLBSET.reg = TC_CTRLBSET_CMD_STOP;
diff --git a/ports/atmel-samd/shared_dma.c b/ports/atmel-samd/shared_dma.c
index a7c27a46e..edc2d0f1a 100644
--- a/ports/atmel-samd/shared_dma.c
+++ b/ports/atmel-samd/shared_dma.c
@@ -128,6 +128,37 @@ void dma_disable_channel(uint8_t channel_number) {
#endif
}
+void dma_suspend_channel(uint8_t channel_number) {
+ #ifdef SAMD21
+ common_hal_mcu_disable_interrupts();
+ /** Select the DMA channel and clear software trigger */
+ DMAC->CHID.reg = DMAC_CHID_ID(channel_number);
+ DMAC->CHCTRLB.bit.CMD = DMAC_CHCTRLB_CMD_SUSPEND_Val;
+ common_hal_mcu_enable_interrupts();
+ #endif
+
+ #ifdef SAMD51
+ DmacChannel* channel = &DMAC->Channel[channel_number];
+ channel->CHCTRLB.reg = DMAC_CHCTRLB_CMD_SUSPEND;
+ #endif
+}
+
+void dma_resume_channel(uint8_t channel_number) {
+ #ifdef SAMD21
+ common_hal_mcu_disable_interrupts();
+ /** Select the DMA channel and clear software trigger */
+ DMAC->CHID.reg = DMAC_CHID_ID(channel_number);
+ DMAC->CHCTRLB.bit.CMD = DMAC_CHCTRLB_CMD_RESUME_Val;
+ DMAC->CHINTFLAG.reg = DMAC_CHINTFLAG_SUSP;
+ common_hal_mcu_enable_interrupts();
+ #endif
+
+ #ifdef SAMD51
+ DmacChannel* channel = &DMAC->Channel[channel_number];
+ channel->CHCTRLB.reg = DMAC_CHCTRLB_CMD_RESUME;
+ #endif
+}
+
bool dma_channel_enabled(uint8_t channel_number) {
#ifdef SAMD21
common_hal_mcu_disable_interrupts();
diff --git a/ports/atmel-samd/shared_dma.h b/ports/atmel-samd/shared_dma.h
index 5f51d404f..03e5b276c 100644
--- a/ports/atmel-samd/shared_dma.h
+++ b/ports/atmel-samd/shared_dma.h
@@ -56,6 +56,8 @@ int32_t sercom_dma_transfer(Sercom* sercom, const uint8_t* buffer_out, uint8_t*
void dma_configure(uint8_t channel_number, uint8_t trigsrc, bool output_event);
void dma_enable_channel(uint8_t channel_number);
void dma_disable_channel(uint8_t channel_number);
+void dma_suspend_channel(uint8_t channel_number);
+void dma_resume_channel(uint8_t channel_number);
bool dma_channel_enabled(uint8_t channel_number);
uint8_t dma_transfer_status(uint8_t channel_number);
DmacDescriptor* dma_descriptor(uint8_t channel_number);
diff --git a/shared-bindings/audiobusio/I2SOut.c b/shared-bindings/audiobusio/I2SOut.c
index 6233bb51a..b177d353c 100644
--- a/shared-bindings/audiobusio/I2SOut.c
+++ b/shared-bindings/audiobusio/I2SOut.c
@@ -212,6 +212,56 @@ const mp_obj_property_t audiobusio_i2sout_playing_obj = {
(mp_obj_t)&mp_const_none_obj},
};
+//| .. method:: pause()
+//|
+//| Stops playback temporarily while remembering the position. Use `resume` to resume playback.
+//|
+STATIC mp_obj_t audiobusio_i2sout_obj_pause(mp_obj_t self_in) {
+ audiobusio_i2sout_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ raise_error_if_deinited(common_hal_audiobusio_i2sout_deinited(self));
+
+ if (!common_hal_audiobusio_i2sout_get_playing(self)) {
+ mp_raise_RuntimeError("No sample playing cannot pause");
+ }
+ common_hal_audiobusio_i2sout_pause(self);
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_1(audiobusio_i2sout_pause_obj, audiobusio_i2sout_obj_pause);
+
+//| .. method:: resume()
+//|
+//| Resumes sample playback after :py:func:`pause`.
+//|
+STATIC mp_obj_t audiobusio_i2sout_obj_resume(mp_obj_t self_in) {
+ audiobusio_i2sout_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ raise_error_if_deinited(common_hal_audiobusio_i2sout_deinited(self));
+
+ if (!common_hal_audiobusio_i2sout_get_paused(self)) {
+ mp_raise_RuntimeError("No paused sample");
+ }
+ common_hal_audiobusio_i2sout_resume(self);
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_1(audiobusio_i2sout_resume_obj, audiobusio_i2sout_obj_resume);
+
+//| .. attribute:: paused
+//|
+//| True when playback is paused. (read-only)
+//|
+STATIC mp_obj_t audiobusio_i2sout_obj_get_paused(mp_obj_t self_in) {
+ audiobusio_i2sout_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ raise_error_if_deinited(common_hal_audiobusio_i2sout_deinited(self));
+ return mp_obj_new_bool(common_hal_audiobusio_i2sout_get_paused(self));
+}
+MP_DEFINE_CONST_FUN_OBJ_1(audiobusio_i2sout_get_paused_obj, audiobusio_i2sout_obj_get_paused);
+
+const mp_obj_property_t audiobusio_i2sout_paused_obj = {
+ .base.type = &mp_type_property,
+ .proxy = {(mp_obj_t)&audiobusio_i2sout_get_paused_obj,
+ (mp_obj_t)&mp_const_none_obj,
+ (mp_obj_t)&mp_const_none_obj},
+};
+
STATIC const mp_rom_map_elem_t audiobusio_i2sout_locals_dict_table[] = {
// Methods
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&audiobusio_i2sout_deinit_obj) },
@@ -219,9 +269,12 @@ STATIC const mp_rom_map_elem_t audiobusio_i2sout_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&audiobusio_i2sout___exit___obj) },
{ MP_ROM_QSTR(MP_QSTR_play), MP_ROM_PTR(&audiobusio_i2sout_play_obj) },
{ MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&audiobusio_i2sout_stop_obj) },
+ { MP_ROM_QSTR(MP_QSTR_pause), MP_ROM_PTR(&audiobusio_i2sout_pause_obj) },
+ { MP_ROM_QSTR(MP_QSTR_resume), MP_ROM_PTR(&audiobusio_i2sout_resume_obj) },
// Properties
{ MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&audiobusio_i2sout_playing_obj) },
+ { MP_ROM_QSTR(MP_QSTR_paused), MP_ROM_PTR(&audiobusio_i2sout_paused_obj) },
};
STATIC MP_DEFINE_CONST_DICT(audiobusio_i2sout_locals_dict, audiobusio_i2sout_locals_dict_table);
diff --git a/shared-bindings/audiobusio/I2SOut.h b/shared-bindings/audiobusio/I2SOut.h
index 930044054..edf4ecfa0 100644
--- a/shared-bindings/audiobusio/I2SOut.h
+++ b/shared-bindings/audiobusio/I2SOut.h
@@ -41,5 +41,8 @@ bool common_hal_audiobusio_i2sout_deinited(audiobusio_i2sout_obj_t* self);
void common_hal_audiobusio_i2sout_play(audiobusio_i2sout_obj_t* self, mp_obj_t sample, bool loop);
void common_hal_audiobusio_i2sout_stop(audiobusio_i2sout_obj_t* self);
bool common_hal_audiobusio_i2sout_get_playing(audiobusio_i2sout_obj_t* self);
+void common_hal_audiobusio_i2sout_pause(audiobusio_i2sout_obj_t* self);
+void common_hal_audiobusio_i2sout_resume(audiobusio_i2sout_obj_t* self);
+bool common_hal_audiobusio_i2sout_get_paused(audiobusio_i2sout_obj_t* self);
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOBUSIO_I2SOUT_H
diff --git a/shared-bindings/audioio/AudioOut.c b/shared-bindings/audioio/AudioOut.c
index 189a217d4..9d79bfbb5 100644
--- a/shared-bindings/audioio/AudioOut.c
+++ b/shared-bindings/audioio/AudioOut.c
@@ -181,7 +181,7 @@ MP_DEFINE_CONST_FUN_OBJ_KW(audioio_audioout_play_obj, 1, audioio_audioout_obj_pl
//| .. method:: stop()
//|
-//| Stops playback.
+//| Stops playback and resets to the start of the sample.
//|
STATIC mp_obj_t audioio_audioout_obj_stop(mp_obj_t self_in) {
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);
//| .. attribute:: playing
//|
-//| True when an audio sample is being output. (read-only)
+//| True when an audio sample is being output even if `paused`. (read-only)
//|
STATIC mp_obj_t audioio_audioout_obj_get_playing(mp_obj_t self_in) {
audioio_audioout_obj_t *self = MP_OBJ_TO_PTR(self_in);
@@ -209,6 +209,56 @@ const mp_obj_property_t audioio_audioout_playing_obj = {
(mp_obj_t)&mp_const_none_obj},
};
+//| .. method:: pause()
+//|
+//| Stops playback temporarily while remembering the position. Use `resume` to resume playback.
+//|
+STATIC mp_obj_t audioio_audioout_obj_pause(mp_obj_t self_in) {
+ audioio_audioout_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ raise_error_if_deinited(common_hal_audioio_audioout_deinited(self));
+
+ if (!common_hal_audioio_audioout_get_playing(self)) {
+ mp_raise_RuntimeError("No sample playing cannot pause");
+ }
+ common_hal_audioio_audioout_pause(self);
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_1(audioio_audioout_pause_obj, audioio_audioout_obj_pause);
+
+//| .. method:: resume()
+//|
+//| Resumes sample playback after :py:func:`pause`.
+//|
+STATIC mp_obj_t audioio_audioout_obj_resume(mp_obj_t self_in) {
+ audioio_audioout_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ raise_error_if_deinited(common_hal_audioio_audioout_deinited(self));
+
+ if (!common_hal_audioio_audioout_get_paused(self)) {
+ mp_raise_RuntimeError("No paused sample");
+ }
+ common_hal_audioio_audioout_resume(self);
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_1(audioio_audioout_resume_obj, audioio_audioout_obj_resume);
+
+//| .. attribute:: paused
+//|
+//| True when playback is paused. (read-only)
+//|
+STATIC mp_obj_t audioio_audioout_obj_get_paused(mp_obj_t self_in) {
+ audioio_audioout_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ raise_error_if_deinited(common_hal_audioio_audioout_deinited(self));
+ return mp_obj_new_bool(common_hal_audioio_audioout_get_paused(self));
+}
+MP_DEFINE_CONST_FUN_OBJ_1(audioio_audioout_get_paused_obj, audioio_audioout_obj_get_paused);
+
+const mp_obj_property_t audioio_audioout_paused_obj = {
+ .base.type = &mp_type_property,
+ .proxy = {(mp_obj_t)&audioio_audioout_get_paused_obj,
+ (mp_obj_t)&mp_const_none_obj,
+ (mp_obj_t)&mp_const_none_obj},
+};
+
STATIC const mp_rom_map_elem_t audioio_audioout_locals_dict_table[] = {
// Methods
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&audioio_audioout_deinit_obj) },
@@ -216,9 +266,12 @@ STATIC const mp_rom_map_elem_t audioio_audioout_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&audioio_audioout___exit___obj) },
{ MP_ROM_QSTR(MP_QSTR_play), MP_ROM_PTR(&audioio_audioout_play_obj) },
{ MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&audioio_audioout_stop_obj) },
+ { MP_ROM_QSTR(MP_QSTR_pause), MP_ROM_PTR(&audioio_audioout_pause_obj) },
+ { MP_ROM_QSTR(MP_QSTR_resume), MP_ROM_PTR(&audioio_audioout_resume_obj) },
// Properties
{ MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&audioio_audioout_playing_obj) },
+ { MP_ROM_QSTR(MP_QSTR_paused), MP_ROM_PTR(&audioio_audioout_paused_obj) },
};
STATIC MP_DEFINE_CONST_DICT(audioio_audioout_locals_dict, audioio_audioout_locals_dict_table);
diff --git a/shared-bindings/audioio/AudioOut.h b/shared-bindings/audioio/AudioOut.h
index 55958af79..751473605 100644
--- a/shared-bindings/audioio/AudioOut.h
+++ b/shared-bindings/audioio/AudioOut.h
@@ -42,5 +42,8 @@ bool common_hal_audioio_audioout_deinited(audioio_audioout_obj_t* self);
void common_hal_audioio_audioout_play(audioio_audioout_obj_t* self, mp_obj_t sample, bool loop);
void common_hal_audioio_audioout_stop(audioio_audioout_obj_t* self);
bool common_hal_audioio_audioout_get_playing(audioio_audioout_obj_t* self);
+void common_hal_audioio_audioout_pause(audioio_audioout_obj_t* self);
+void common_hal_audioio_audioout_resume(audioio_audioout_obj_t* self);
+bool common_hal_audioio_audioout_get_paused(audioio_audioout_obj_t* self);
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOIO_AUDIOOUT_H