From 59c6ed34fdb7312de4747110d6a40f67a8787f13 Mon Sep 17 00:00:00 2001 From: dean Date: Wed, 14 Nov 2018 18:05:29 -0500 Subject: [PATCH] DM: mixer voice object skeleton --- .../trellis_m4_express/mpconfigboard.mk | 2 +- shared-bindings/audioio/Mixer.c | 260 +++++++++++++++--- shared-bindings/audioio/Mixer.h | 10 + shared-module/audioio/Mixer.c | 6 +- shared-module/audioio/Mixer.h | 4 +- 5 files changed, 240 insertions(+), 42 deletions(-) diff --git a/ports/atmel-samd/boards/trellis_m4_express/mpconfigboard.mk b/ports/atmel-samd/boards/trellis_m4_express/mpconfigboard.mk index 3841a87c76..4d923bafd7 100644 --- a/ports/atmel-samd/boards/trellis_m4_express/mpconfigboard.mk +++ b/ports/atmel-samd/boards/trellis_m4_express/mpconfigboard.mk @@ -1,4 +1,4 @@ -LD_FILE = boards/samd51x19-bootloader-external-flash.ld +LD_FILE = boards/samd51x19-external-flash.ld USB_VID = 0x239A USB_PID = 0x8030 USB_PRODUCT = "Trellis M4 Express" diff --git a/shared-bindings/audioio/Mixer.c b/shared-bindings/audioio/Mixer.c index a20e95b3b4..c07c05bc1b 100644 --- a/shared-bindings/audioio/Mixer.c +++ b/shared-bindings/audioio/Mixer.c @@ -106,7 +106,7 @@ STATIC mp_obj_t audioio_mixer_make_new(const mp_obj_type_t *type, size_t n_args, if (bits_per_sample != 8 && bits_per_sample != 16) { mp_raise_ValueError(translate("bits_per_sample must be 8 or 16")); } - audioio_mixer_obj_t *self = m_new_obj_var(audioio_mixer_obj_t, audioio_mixer_voice_t, voice_count); + audioio_mixer_obj_t *self = m_new_obj_var(audioio_mixer_obj_t, audioio_mixer_voice_obj_t, voice_count); self->base.type = &audioio_mixer_type; common_hal_audioio_mixer_construct(self, voice_count, args[ARG_buffer_size].u_int, bits_per_sample, args[ARG_samples_signed].u_bool, channel_count, sample_rate); @@ -190,39 +190,6 @@ STATIC mp_obj_t audioio_mixer_obj_stop_voice(size_t n_args, const mp_obj_t *pos_ } MP_DEFINE_CONST_FUN_OBJ_KW(audioio_mixer_stop_voice_obj, 1, audioio_mixer_obj_stop_voice); -//| .. method:: set_gain(voice, gain) -//| -//| Set the gain of a voice. -//| -//| gain must be a floating point number between 0 and 1 -//| -STATIC mp_obj_t audioio_mixer_obj_set_gain(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_voice, ARG_gain }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_voice, MP_ARG_INT | MP_ARG_REQUIRED }, - { MP_QSTR_gain, MP_ARG_OBJ | MP_ARG_REQUIRED }, - }; - audioio_mixer_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); - raise_error_if_deinited(common_hal_audioio_mixer_deinited(self)); - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - #if MICROPY_PY_BUILTINS_FLOAT - float gain = mp_obj_get_float(args[ARG_gain].u_obj); - #else - #error "floating point not supported" - #endif - - if (gain > 1 || gain < 0) { - mp_raise_ValueError(translate("gain must be between 0 and 1")); - } - - common_hal_audioio_mixer_set_gain(self,args[ARG_voice].u_int, gain); - - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_KW(audioio_mixer_set_gain_obj, 1, audioio_mixer_obj_set_gain); - //| .. attribute:: playing //| //| True when any voice is being output. (read-only) @@ -252,7 +219,6 @@ STATIC mp_obj_t audioio_mixer_obj_get_sample_rate(mp_obj_t self_in) { } MP_DEFINE_CONST_FUN_OBJ_1(audioio_mixer_get_sample_rate_obj, audioio_mixer_obj_get_sample_rate); - const mp_obj_property_t audioio_mixer_sample_rate_obj = { .base.type = &mp_type_property, .proxy = {(mp_obj_t)&audioio_mixer_get_sample_rate_obj, @@ -260,6 +226,208 @@ const mp_obj_property_t audioio_mixer_sample_rate_obj = { (mp_obj_t)&mp_const_none_obj}, }; +//| .. currentmodule:: audioio +//| +//| :class:`Mixer` -- Mixes one or more audio samples together +//| =========================================================== +//| +//| Mixer mixes multiple samples into one sample. +//| +//| .. class:: Mixer(channel_count=2, buffer_size=1024) +//| +//| Create a Mixer object that can mix multiple channels with the same sample rate. +//| +//| :param int channel_count: The maximum number of samples to mix at once +//| :param int buffer_size: The total size in bytes of the buffers to mix into +//| +STATIC mp_obj_t audioio_mixer_voice_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *pos_args) { +#if 0 + mp_arg_check_num(n_args, n_kw, 0, 2, true); + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, pos_args + n_args); + enum { ARG_voice_count, ARG_buffer_size, ARG_channel_count, ARG_bits_per_sample, ARG_samples_signed, ARG_sample_rate }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_voice_count, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 2} }, + { MP_QSTR_buffer_size, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 1024} }, + { MP_QSTR_channel_count, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 2} }, + { MP_QSTR_bits_per_sample, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 16} }, + { MP_QSTR_samples_signed, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = true} }, + { MP_QSTR_sample_rate, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 8000} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, &kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_int_t voice_count = args[ARG_voice_count].u_int; + if (voice_count < 1 || voice_count > 255) { + mp_raise_ValueError(translate("Invalid voice count")); + } + + mp_int_t channel_count = args[ARG_channel_count].u_int; + if (channel_count < 1 || channel_count > 2) { + mp_raise_ValueError(translate("Invalid channel count")); + } + mp_int_t sample_rate = args[ARG_sample_rate].u_int; + if (sample_rate < 1) { + mp_raise_ValueError(translate("Sample rate must be positive")); + } + mp_int_t bits_per_sample = args[ARG_bits_per_sample].u_int; + if (bits_per_sample != 8 && bits_per_sample != 16) { + mp_raise_ValueError(translate("bits_per_sample must be 8 or 16")); + } + audioio_mixer_obj_t *self = m_new_obj_var(audioio_mixer_obj_t, audioio_mixer_voice_obj_t, voice_count); + self->base.type = &audioio_mixer_type; + common_hal_audioio_mixer_construct(self, voice_count, args[ARG_buffer_size].u_int, bits_per_sample, args[ARG_samples_signed].u_bool, channel_count, sample_rate); + + return MP_OBJ_FROM_PTR(self); +#endif + return mp_const_none; +} + +//| .. method:: deinit() +//| +//| Deinitialises the Voice and releases any hardware resources for reuse. +//| +STATIC mp_obj_t audioio_mixer_voice_deinit(mp_obj_t self_in) { +#if 0 + audioio_mixer_voice_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_audioio_mixer_voice_deinit(self); +#endif + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(audioio_mixer_voice_deinit_obj, audioio_mixer_voice_deinit); + +//| .. method:: __enter__() +//| +//| No-op used by Context Managers. +//| +// Provided by context manager helper. + +//| .. method:: __exit__() +//| +//| Automatically deinitializes the hardware when exiting a context. See +//| :ref:`lifetime-and-contextmanagers` for more info. +//| +STATIC mp_obj_t audioio_mixer_voice_obj___exit__(size_t n_args, const mp_obj_t *args) { +#if 0 + (void)n_args; + common_hal_audioio_mixer_voice_deinit(args[0]); +#endif + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(audioio_mixer_voice___exit___obj, 4, 4, audioio_mixer_voice_obj___exit__); + + +//| .. method:: play(sample, *, loop=False) +//| +//| Plays the sample once when loop=False and continuously when loop=True. +//| Does not block. Use `playing` to block. +//| +//| Sample must be an `audioio.WaveFile`, `audioio.Mixer` or `audioio.RawSample`. +//| +//| The sample must match the Mixer's encoding settings given in the constructor. +//| +STATIC mp_obj_t audioio_mixer_voice_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { +#if 0 + enum { ARG_sample, ARG_loop }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_sample, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_loop, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, + }; + audioio_mixer_voice_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + raise_error_if_deinited(common_hal_audioio_mixer_voice_deinited(self)); + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_obj_t sample = args[ARG_sample].u_obj; + common_hal_audioio_mixer_play(self, sample, self->u_int, args[ARG_loop].u_ool); +#endif + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(audioio_mixer_voice_play_obj, 1, audioio_mixer_voice_obj_play); + +//| .. method:: stop_voice() +//| +//| Stops playback of the sample on this voice. +//| +STATIC mp_obj_t audioio_mixer_voice_obj_stop(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { +#if 0 + enum { ARG_voice }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_voice, MP_ARG_INT, {.u_int = 0} }, + }; + audioio_mixer_voice_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + raise_error_if_deinited(common_hal_audioio_mixer_deinited(self)); + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + common_hal_audioio_mixer_stop_voice(self, args[ARG_voice].u_int); +#endif + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(audioio_mixer_voice_stop_obj, 1, audioio_mixer_voice_obj_stop); + +//| .. method:: set_gain(voice, gain) +//| +//| Set the gain of a voice. +//| +//| gain must be a floating point number between 0 and 1 +//| +STATIC mp_obj_t audioio_mixer_voice_obj_set_gain(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { +#if 0 + enum { ARG_voice, ARG_gain }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_voice, MP_ARG_INT | MP_ARG_REQUIRED }, + { MP_QSTR_gain, MP_ARG_OBJ | MP_ARG_REQUIRED }, + }; + audioio_mixer_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + raise_error_if_deinited(common_hal_audioio_mixer_deinited(self)); + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + #if MICROPY_PY_BUILTINS_FLOAT + float gain = mp_obj_get_float(args[ARG_gain].u_obj); + #else + #error "floating point not supported" + #endif + + if (gain > 1 || gain < 0) { + mp_raise_ValueError(translate("gain must be between 0 and 1")); + } + + common_hal_audioio_mixer_set_gain(self,args[ARG_voice].u_int, gain); +#endif + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(audioio_mixer_voice_set_gain_obj, 1, audioio_mixer_voice_obj_set_gain); + +const mp_obj_property_t audioio_mixer_voice_gain_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&audioio_mixer_voice_set_gain_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + +//| .. attribute:: playing +//| +//| True when any voice is being output. (read-only) +//| +STATIC mp_obj_t audioio_mixer_voice_obj_get_playing(mp_obj_t self_in) { +#if 0 + audioio_mixer_obj_t *self = MP_OBJ_TO_PTR(self_in); + raise_error_if_deinited(common_hal_audioio_mixer_deinited(self)); + return mp_obj_new_bool(common_hal_audioio_mixer_get_playing(self)); +#endif + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(audioio_mixer_voice_get_playing_obj, audioio_mixer_voice_obj_get_playing); + +const mp_obj_property_t audioio_mixer_voice_playing_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&audioio_mixer_voice_get_playing_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + STATIC const mp_rom_map_elem_t audioio_mixer_locals_dict_table[] = { // Methods { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&audioio_mixer_deinit_obj) }, @@ -267,7 +435,6 @@ STATIC const mp_rom_map_elem_t audioio_mixer_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&audioio_mixer___exit___obj) }, { MP_ROM_QSTR(MP_QSTR_play), MP_ROM_PTR(&audioio_mixer_play_obj) }, { MP_ROM_QSTR(MP_QSTR_stop_voice), MP_ROM_PTR(&audioio_mixer_stop_voice_obj) }, - { MP_ROM_QSTR(MP_QSTR_set_gain), MP_ROM_PTR(&audioio_mixer_set_gain_obj) }, // Properties { MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&audioio_mixer_playing_obj) }, @@ -281,3 +448,24 @@ const mp_obj_type_t audioio_mixer_type = { .make_new = audioio_mixer_make_new, .locals_dict = (mp_obj_dict_t*)&audioio_mixer_locals_dict, }; + +STATIC const mp_rom_map_elem_t audioio_mixer_voice_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&audioio_mixer_voice_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&audioio_mixer_voice___exit___obj) }, + { MP_ROM_QSTR(MP_QSTR_play), MP_ROM_PTR(&audioio_mixer_voice_play_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&audioio_mixer_voice_stop_obj) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&audioio_mixer_voice_playing_obj) }, + { MP_ROM_QSTR(MP_QSTR_gain), MP_ROM_PTR(&audioio_mixer_voice_gain_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(audioio_mixer_voice_locals_dict, audioio_mixer_voice_locals_dict_table); + +const mp_obj_type_t audioio_mixer_voice_type = { + { &mp_type_type }, + .name = MP_QSTR_Mixer_Voice, + .make_new = audioio_mixer_voice_make_new, + .locals_dict = (mp_obj_dict_t*)&audioio_mixer_voice_locals_dict, +}; diff --git a/shared-bindings/audioio/Mixer.h b/shared-bindings/audioio/Mixer.h index 11ca2aac54..52fb9bdc95 100644 --- a/shared-bindings/audioio/Mixer.h +++ b/shared-bindings/audioio/Mixer.h @@ -32,6 +32,7 @@ #include "shared-bindings/audioio/RawSample.h" extern const mp_obj_type_t audioio_mixer_type; +extern const mp_obj_type_t audioio_mixer_voice_type; void common_hal_audioio_mixer_construct(audioio_mixer_obj_t* self, uint8_t voice_count, @@ -47,7 +48,16 @@ void common_hal_audioio_mixer_play(audioio_mixer_obj_t* self, mp_obj_t sample, u void common_hal_audioio_mixer_stop_voice(audioio_mixer_obj_t* self, uint8_t voice); void common_hal_audioio_mixer_set_gain(audioio_mixer_obj_t* self, uint8_t voice, float gain); + bool common_hal_audioio_mixer_get_playing(audioio_mixer_obj_t* self); uint32_t common_hal_audioio_mixer_get_sample_rate(audioio_mixer_obj_t* self); +void common_hal_audioio_mixer_voice_deinit(audioio_mixer_voice_obj_t* self); +bool common_hal_audioio_mixer_voice_deinited(audioio_mixer_voice_obj_t* self); +void common_hal_audioio_mixer_voice_play(audioio_mixer_voice_obj_t* self, mp_obj_t sample, bool loop); +void common_hal_audioio_mixer_voice_stop_voice(audioio_mixer_voice_obj_t* self); +void common_hal_audioio_mixer_voice_set_gain(audioio_mixer_voice_obj_t* self, float gain); + +bool common_hal_audioio_mixer_get_playing(audioio_mixer_obj_t* self); + #endif // MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOIO_MIXER_H diff --git a/shared-module/audioio/Mixer.c b/shared-module/audioio/Mixer.c index a6ce1937b7..5125574eff 100644 --- a/shared-module/audioio/Mixer.c +++ b/shared-module/audioio/Mixer.c @@ -100,7 +100,7 @@ void common_hal_audioio_mixer_play(audioio_mixer_obj_t* self, mp_obj_t sample, u if (samples_signed != self->samples_signed) { mp_raise_ValueError(translate("The sample's signedness does not match the mixer's")); } - audioio_mixer_voice_t* voice = &self->voice[v]; + audioio_mixer_voice_obj_t* voice = &self->voice[v]; voice->sample = sample; voice->loop = loop; @@ -133,7 +133,7 @@ void audioio_mixer_reset_buffer(audioio_mixer_obj_t* self, } void common_hal_audioio_mixer_set_gain(audioio_mixer_obj_t* self, uint8_t voice, float gain) { - audioio_mixer_voice_t* v = &self->voice[voice]; + audioio_mixer_voice_obj_t* v = &self->voice[voice]; v->gain = gain * ((1 << 15)-1); } @@ -311,7 +311,7 @@ audioio_get_buffer_result_t audioio_mixer_get_buffer(audioio_mixer_obj_t* self, self->use_first_buffer = !self->use_first_buffer; bool voices_active = false; for (int32_t v = 0; v < self->voice_count; v++) { - audioio_mixer_voice_t* voice = &self->voice[v]; + audioio_mixer_voice_obj_t* voice = &self->voice[v]; uint32_t j = 0; bool voice_done = voice->sample == NULL; diff --git a/shared-module/audioio/Mixer.h b/shared-module/audioio/Mixer.h index a48793a8ff..3451aa25df 100644 --- a/shared-module/audioio/Mixer.h +++ b/shared-module/audioio/Mixer.h @@ -38,7 +38,7 @@ typedef struct { uint32_t* remaining_buffer; uint32_t buffer_length; int16_t gain; -} audioio_mixer_voice_t; +} audioio_mixer_voice_obj_t; typedef struct { mp_obj_base_t base; @@ -56,7 +56,7 @@ typedef struct { uint32_t right_read_count; uint8_t voice_count; - audioio_mixer_voice_t voice[]; + audioio_mixer_voice_obj_t voice[]; } audioio_mixer_obj_t;