From 008799dc520218ecb96a782feb145b37b842862a Mon Sep 17 00:00:00 2001 From: dean Date: Wed, 31 Oct 2018 18:27:08 -0400 Subject: DM: adding mixer gain --- shared-module/audioio/Mixer.c | 84 +++++++++++++++++++++++++++++++++++++++++++ shared-module/audioio/Mixer.h | 1 + 2 files changed, 85 insertions(+) (limited to 'shared-module') diff --git a/shared-module/audioio/Mixer.c b/shared-module/audioio/Mixer.c index 8020a621e..a6ce1937b 100644 --- a/shared-module/audioio/Mixer.c +++ b/shared-module/audioio/Mixer.c @@ -61,6 +61,7 @@ void common_hal_audioio_mixer_construct(audioio_mixer_obj_t* self, for (uint8_t i = 0; i < self->voice_count; i++) { self->voice[i].sample = NULL; + self->voice[i].gain = ((1<<15)-1); } } @@ -131,6 +132,11 @@ 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]; + v->gain = gain * ((1 << 15)-1); +} + uint32_t add8signed(uint32_t a, uint32_t b) { #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) return __QADD8(a, b); @@ -215,6 +221,68 @@ uint32_t add16unsigned(uint32_t a, uint32_t b) { #endif } +//TODO: +static inline uint32_t mult8unsigned(uint32_t val, int32_t mul) { + #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + return val; + val = __USUB8(val, 0x80808080); + #else + uint32_t result = 0; + for (int8_t i = 0; i < 4; i++) { + int8_t ai = (val >> (sizeof(uint8_t) * 8 * i)) - 128; + } + return result; + #endif +} + +//TODO: +static inline uint32_t mult8signed(uint32_t val, int32_t mul) { + #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + return val; + #else + uint32_t result = 0; + for (int8_t i = 0; i < 4; i++) { + int8_t ai = val >> (sizeof(int8_t) * 8 * i); + } + return result; + #endif +} + +//TODO: +static inline uint32_t mult16unsigned(uint32_t val, int32_t mul) { + #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + return val; + val = __USUB16(val, 0x80008000); + #else + uint32_t result = 0; + for (int8_t i = 0; i < 2; i++) { + int16_t ai = (val >> (sizeof(uint16_t) * 8 * i)) - 0x8000; + } + return result; + #endif +} + +static inline uint32_t mult16signed(uint32_t val, int32_t mul) { + #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + int32_t hi, lo; + int32_t bits = 16; // saturate to 16 bits + int32_t shift = 0; // shift is done automatically + asm volatile("smulwb %0, %1, %2" : "=r" (lo) : "r" (mul), "r" (val)); + asm volatile("smulwt %0, %1, %2" : "=r" (hi) : "r" (mul), "r" (val)); + asm volatile("ssat %0, %1, %2, asr %3" : "=r" (lo) : "I" (bits), "r" (lo), "I" (shift)); + asm volatile("ssat %0, %1, %2, asr %3" : "=r" (hi) : "I" (bits), "r" (hi), "I" (shift)); + asm volatile("pkhbt %0, %1, %2, lsl #16" : "=r" (val) : "r" (lo), "r" (hi)); // pack + return val; + #else + uint32_t result = 0; + //TODO: + for (int8_t i = 0; i < 2; i++) { + int16_t ai = val >> (sizeof(int16_t) * 8 * i); + } + return result; + #endif +} + audioio_get_buffer_result_t audioio_mixer_get_buffer(audioio_mixer_obj_t* self, bool single_channel, uint8_t channel, @@ -285,6 +353,22 @@ audioio_get_buffer_result_t audioio_mixer_get_buffer(audioio_mixer_obj_t* self, sample_value = voice->remaining_buffer[j]; } + // apply the mixer gain + if (!self->samples_signed) { + if (self->bits_per_sample == 8) { + sample_value = mult8unsigned(sample_value, voice->gain); + } else { + sample_value = mult16unsigned(sample_value, voice->gain); + } + } + else{ + if (self->bits_per_sample == 8) { + sample_value = mult8signed(sample_value, voice->gain); + } else { + sample_value = mult16signed(sample_value, voice->gain); + } + } + if (!voices_active) { word_buffer[i] = sample_value; } else { diff --git a/shared-module/audioio/Mixer.h b/shared-module/audioio/Mixer.h index 6a88fe0bd..a48793a8f 100644 --- a/shared-module/audioio/Mixer.h +++ b/shared-module/audioio/Mixer.h @@ -37,6 +37,7 @@ typedef struct { bool more_data; uint32_t* remaining_buffer; uint32_t buffer_length; + int16_t gain; } audioio_mixer_voice_t; typedef struct { -- cgit v1.2.3 From 59c6ed34fdb7312de4747110d6a40f67a8787f13 Mon Sep 17 00:00:00 2001 From: dean Date: Wed, 14 Nov 2018 18:05:29 -0500 Subject: DM: mixer voice object skeleton --- .../boards/trellis_m4_express/mpconfigboard.mk | 2 +- shared-bindings/audioio/Mixer.c | 242 ++++++++++++++++++--- shared-bindings/audioio/Mixer.h | 10 + shared-module/audioio/Mixer.c | 6 +- shared-module/audioio/Mixer.h | 4 +- 5 files changed, 231 insertions(+), 33 deletions(-) (limited to 'shared-module') diff --git a/ports/atmel-samd/boards/trellis_m4_express/mpconfigboard.mk b/ports/atmel-samd/boards/trellis_m4_express/mpconfigboard.mk index 3841a87c7..4d923bafd 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 a20e95b3b..c07c05bc1 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,13 +190,190 @@ 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); +//| .. attribute:: playing +//| +//| True when any voice is being output. (read-only) +//| +STATIC mp_obj_t audioio_mixer_obj_get_playing(mp_obj_t self_in) { + 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)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audioio_mixer_get_playing_obj, audioio_mixer_obj_get_playing); + +const mp_obj_property_t audioio_mixer_playing_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&audioio_mixer_get_playing_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + +//| .. attribute:: sample_rate +//| +//| 32 bit value that dictates how quickly samples are played in Hertz (cycles per second). +//| +STATIC mp_obj_t audioio_mixer_obj_get_sample_rate(mp_obj_t self_in) { + 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_SMALL_INT(common_hal_audioio_mixer_get_sample_rate(self)); +} +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, + (mp_obj_t)&mp_const_none_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_obj_set_gain(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { +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 }, @@ -218,44 +395,35 @@ STATIC mp_obj_t audioio_mixer_obj_set_gain(size_t n_args, const mp_obj_t *pos_ar } 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_set_gain_obj, 1, audioio_mixer_obj_set_gain); +MP_DEFINE_CONST_FUN_OBJ_KW(audioio_mixer_voice_set_gain_obj, 1, audioio_mixer_voice_obj_set_gain); -//| .. attribute:: playing -//| -//| True when any voice is being output. (read-only) -//| -STATIC mp_obj_t audioio_mixer_obj_get_playing(mp_obj_t self_in) { - 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)); -} -MP_DEFINE_CONST_FUN_OBJ_1(audioio_mixer_get_playing_obj, audioio_mixer_obj_get_playing); - -const mp_obj_property_t audioio_mixer_playing_obj = { +const mp_obj_property_t audioio_mixer_voice_gain_obj = { .base.type = &mp_type_property, - .proxy = {(mp_obj_t)&audioio_mixer_get_playing_obj, - (mp_obj_t)&mp_const_none_obj, + .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:: sample_rate +//| .. attribute:: playing //| -//| 32 bit value that dictates how quickly samples are played in Hertz (cycles per second). +//| True when any voice is being output. (read-only) //| -STATIC mp_obj_t audioio_mixer_obj_get_sample_rate(mp_obj_t self_in) { +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_SMALL_INT(common_hal_audioio_mixer_get_sample_rate(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_get_sample_rate_obj, audioio_mixer_obj_get_sample_rate); - +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_sample_rate_obj = { +const mp_obj_property_t audioio_mixer_voice_playing_obj = { .base.type = &mp_type_property, - .proxy = {(mp_obj_t)&audioio_mixer_get_sample_rate_obj, + .proxy = {(mp_obj_t)&audioio_mixer_voice_get_playing_obj, (mp_obj_t)&mp_const_none_obj, (mp_obj_t)&mp_const_none_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 11ca2aac5..52fb9bdc9 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 a6ce1937b..5125574ef 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 a48793a8f..3451aa25d 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; -- cgit v1.2.3 From dd05aafb9bcbfeae77ce8b21b425b26e67891082 Mon Sep 17 00:00:00 2001 From: dean Date: Thu, 15 Nov 2018 14:11:45 -0500 Subject: DM: adding mixer voice API --- shared-bindings/audioio/Mixer.c | 223 ++--------------------------------- shared-bindings/audioio/Mixer.h | 10 +- shared-bindings/audioio/MixerVoice.c | 196 ++++++++++++++++++++++++++++++ shared-bindings/audioio/MixerVoice.h | 38 ++++++ shared-module/audioio/Mixer.c | 9 +- shared-module/audioio/Mixer.h | 12 +- shared-module/audioio/MixerVoice.c | 18 +++ shared-module/audioio/MixerVoice.h | 12 ++ 8 files changed, 279 insertions(+), 239 deletions(-) create mode 100644 shared-bindings/audioio/MixerVoice.c create mode 100644 shared-bindings/audioio/MixerVoice.h create mode 100644 shared-module/audioio/MixerVoice.c create mode 100644 shared-module/audioio/MixerVoice.h (limited to 'shared-module') diff --git a/shared-bindings/audioio/Mixer.c b/shared-bindings/audioio/Mixer.c index c07c05bc1..baf86c803 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_obj_t, voice_count); + audioio_mixer_obj_t *self = m_new_obj_var(audioio_mixer_obj_t, mp_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); @@ -226,208 +226,25 @@ 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() +//| .. attribute:: 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) +//| tuple of voice objects //| -//| 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 +STATIC mp_obj_t audioio_mixer_obj_get_voice(mp_obj_t self_in) { 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; + return mp_obj_new_tuple(self->voice_count, self->voice); } -MP_DEFINE_CONST_FUN_OBJ_1(audioio_mixer_voice_get_playing_obj, audioio_mixer_voice_obj_get_playing); +MP_DEFINE_CONST_FUN_OBJ_1(audioio_mixer_get_voice_obj, audioio_mixer_obj_get_voice); -const mp_obj_property_t audioio_mixer_voice_playing_obj = { +const mp_obj_property_t audioio_mixer_voice_obj = { .base.type = &mp_type_property, - .proxy = {(mp_obj_t)&audioio_mixer_voice_get_playing_obj, + .proxy = {(mp_obj_t)&audioio_mixer_obj_get_voice, (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) }, @@ -439,6 +256,7 @@ STATIC const mp_rom_map_elem_t audioio_mixer_locals_dict_table[] = { // Properties { MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&audioio_mixer_playing_obj) }, { MP_ROM_QSTR(MP_QSTR_sample_rate), MP_ROM_PTR(&audioio_mixer_sample_rate_obj) }, + { MP_ROM_QSTR(MP_QSTR_voice), MP_ROM_PTR(&audioio_mixer_voice_obj) } }; STATIC MP_DEFINE_CONST_DICT(audioio_mixer_locals_dict, audioio_mixer_locals_dict_table); @@ -448,24 +266,3 @@ 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 52fb9bdc9..1769f3bb7 100644 --- a/shared-bindings/audioio/Mixer.h +++ b/shared-bindings/audioio/Mixer.h @@ -32,7 +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; +extern const mp_obj_type_t audioio_mixervoice_type; void common_hal_audioio_mixer_construct(audioio_mixer_obj_t* self, uint8_t voice_count, @@ -52,12 +52,4 @@ void common_hal_audioio_mixer_set_gain(audioio_mixer_obj_t* self, uint8_t voice, 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-bindings/audioio/MixerVoice.c b/shared-bindings/audioio/MixerVoice.c new file mode 100644 index 000000000..9cc8a6339 --- /dev/null +++ b/shared-bindings/audioio/MixerVoice.c @@ -0,0 +1,196 @@ +/* + * MixerVoice.c + * + * Created on: Nov 15, 2018 + * Author: dean + */ +#include "shared-bindings/audioio/Mixer.h" + +#include + +#include "lib/utils/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/audioio/RawSample.h" +#include "shared-bindings/util.h" +#include "supervisor/shared/translate.h" + +//| .. 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 +//| +// TODO: support mono or stereo voices +STATIC mp_obj_t audioio_mixervoice_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *pos_args) { + audioio_mixervoice_obj_t *self = m_new(audioio_mixervoice_obj_t, 1); + self->base.type = &audioio_mixervoice_type; + + return MP_OBJ_FROM_PTR(self); +} + +//| .. method:: deinit() +//| +//| Deinitialises the Voice and releases any hardware resources for reuse. +//| +STATIC mp_obj_t audioio_mixervoice_deinit(mp_obj_t self_in) { + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(audioio_mixervoice_deinit_obj, audioio_mixervoice_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_mixervoice_obj___exit__(size_t n_args, const mp_obj_t *args) { + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(audioio_mixervoice___exit___obj, 4, 4, audioio_mixervoice_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_mixervoice_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_mixervoice_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + raise_error_if_deinited(common_hal_audioio_mixervoice_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_mixervoice_play_obj, 1, audioio_mixervoice_obj_play); + +//| .. method:: stop_voice() +//| +//| Stops playback of the sample on this voice. +//| +STATIC mp_obj_t audioio_mixervoice_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_mixervoice_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_mixervoice_stop_obj, 1, audioio_mixervoice_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_mixervoice_obj_set_gain(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_gain }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_gain, MP_ARG_OBJ | MP_ARG_REQUIRED }, + }; + audioio_mixer_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + 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_mixervoice_set_gain(self, gain); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(audioio_mixervoice_set_gain_obj, 1, audioio_mixervoice_obj_set_gain); + +const mp_obj_property_t audioio_mixervoice_gain_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&audioio_mixervoice_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_mixervoice_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_mixervoice_get_playing_obj, audioio_mixervoice_obj_get_playing); + +const mp_obj_property_t audioio_mixervoice_playing_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&audioio_mixervoice_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_mixervoice_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&audioio_mixervoice_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&audioio_mixervoice___exit___obj) }, + { MP_ROM_QSTR(MP_QSTR_play), MP_ROM_PTR(&audioio_mixervoice_play_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&audioio_mixervoice_stop_obj) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&audioio_mixervoice_playing_obj) }, + { MP_ROM_QSTR(MP_QSTR_gain), MP_ROM_PTR(&audioio_mixervoice_gain_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(audioio_mixervoice_locals_dict, audioio_mixervoice_locals_dict_table); + +const mp_obj_type_t audioio_mixervoice_type = { + { &mp_type_type }, + .name = MP_QSTR_mixervoice, + .make_new = audioio_mixervoice_make_new, + .locals_dict = (mp_obj_dict_t*)&audioio_mixervoice_locals_dict, +}; + diff --git a/shared-bindings/audioio/MixerVoice.h b/shared-bindings/audioio/MixerVoice.h new file mode 100644 index 000000000..f4c868a0e --- /dev/null +++ b/shared-bindings/audioio/MixerVoice.h @@ -0,0 +1,38 @@ +/* + * MixerVoice.h + * + * Created on: Nov 15, 2018 + * Author: dean + */ + +#ifndef SHARED_BINDINGS_AUDIOIO_MIXERVOICE_H_ +#define SHARED_BINDINGS_AUDIOIO_MIXERVOICE_H_ + +#include "py/obj.h" + +#include "shared-module/audioio/__init__.h" + +#include "common-hal/microcontroller/Pin.h" +#include "shared-module/audioio/Mixer.h" +#include "shared-bindings/audioio/RawSample.h" + +typedef struct { + mp_obj_t sample; + bool loop; + bool more_data; + uint32_t* remaining_buffer; + uint32_t buffer_length; + int16_t gain; +} audioio_mixervoice_obj_t; + +extern const mp_obj_type_t audioio_mixer_type; +extern const mp_obj_type_t audioio_mixervoice_type; + +void common_hal_audioio_mixervoice_deinit(audioio_mixervoice_obj_t* self); +void common_hal_audioio_mixervoice_play(audioio_mixervoice_obj_t* self, mp_obj_t sample, bool loop); +void common_hal_audioio_mixervoice_stop(audioio_mixervoice_obj_t* self); +void common_hal_audioio_mixervoice_set_gain(audioio_mixervoice_obj_t* self, float gain); + +bool common_hal_audioio_mixervoice_get_playing(audioio_mixervoice_obj_t* self); + +#endif /* SHARED_BINDINGS_AUDIOIO_MIXERVOICE_H_ */ diff --git a/shared-module/audioio/Mixer.c b/shared-module/audioio/Mixer.c index 5125574ef..4797f4adc 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_obj_t* voice = &self->voice[v]; + audioio_mixervoice_obj_t* voice = &self->voice[v]; voice->sample = sample; voice->loop = loop; @@ -132,11 +132,6 @@ 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_obj_t* v = &self->voice[voice]; - v->gain = gain * ((1 << 15)-1); -} - uint32_t add8signed(uint32_t a, uint32_t b) { #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) return __QADD8(a, b); @@ -311,7 +306,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_obj_t* voice = &self->voice[v]; + audioio_mixervoice_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 3451aa25d..40b6df2fe 100644 --- a/shared-module/audioio/Mixer.h +++ b/shared-module/audioio/Mixer.h @@ -30,15 +30,7 @@ #include "py/obj.h" #include "shared-module/audioio/__init__.h" - -typedef struct { - mp_obj_t sample; - bool loop; - bool more_data; - uint32_t* remaining_buffer; - uint32_t buffer_length; - int16_t gain; -} audioio_mixer_voice_obj_t; +#include "shared-bindings/audioio/MixerVoice.h" typedef struct { mp_obj_base_t base; @@ -56,7 +48,7 @@ typedef struct { uint32_t right_read_count; uint8_t voice_count; - audioio_mixer_voice_obj_t voice[]; + mp_obj_t voice[]; } audioio_mixer_obj_t; diff --git a/shared-module/audioio/MixerVoice.c b/shared-module/audioio/MixerVoice.c new file mode 100644 index 000000000..ff40da34c --- /dev/null +++ b/shared-module/audioio/MixerVoice.c @@ -0,0 +1,18 @@ +/* + * MixerVoice.c + * + * Created on: Nov 15, 2018 + * Author: dean + */ + +#include "shared-bindings/audioio/Mixer.h" + +#include + +#include "py/runtime.h" +#include "shared-module/audioio/__init__.h" +#include "shared-module/audioio/RawSample.h" + +void common_hal_audioio_mixervoice_set_gain(audioio_mixer_obj_t* self, float gain) { + self->gain = gain * ((1 << 15)-1); +} diff --git a/shared-module/audioio/MixerVoice.h b/shared-module/audioio/MixerVoice.h new file mode 100644 index 000000000..c04e8ef24 --- /dev/null +++ b/shared-module/audioio/MixerVoice.h @@ -0,0 +1,12 @@ +/* + * MixerVoice.h + * + * Created on: Nov 15, 2018 + * Author: dean + */ + +#ifndef SHARED_MODULE_AUDIOIO_MIXERVOICE_H_ +#define SHARED_MODULE_AUDIOIO_MIXERVOICE_H_ + + +#endif /* SHARED_MODULE_AUDIOIO_MIXERVOICE_H_ */ -- cgit v1.2.3 From feef1778581e0811bc07da3aa3dc39744187b05b Mon Sep 17 00:00:00 2001 From: dean Date: Thu, 15 Nov 2018 15:04:11 -0500 Subject: DM: in progress mixer voice --- ports/atmel-samd/Makefile | 1 + shared-bindings/audioio/Mixer.c | 8 +++++++- shared-bindings/audioio/MixerVoice.c | 3 ++- shared-bindings/audioio/MixerVoice.h | 14 +------------- shared-module/audioio/Mixer.c | 13 +++++++++++++ shared-module/audioio/Mixer.h | 2 ++ shared-module/audioio/MixerVoice.c | 2 +- shared-module/audioio/MixerVoice.h | 14 ++++++++++++++ 8 files changed, 41 insertions(+), 16 deletions(-) (limited to 'shared-module') diff --git a/ports/atmel-samd/Makefile b/ports/atmel-samd/Makefile index 7861f4235..e628129a6 100644 --- a/ports/atmel-samd/Makefile +++ b/ports/atmel-samd/Makefile @@ -434,6 +434,7 @@ ifneq ($(CHIP_VARIANT),SAMR21G18A) SRC_SHARED_MODULE += \ audioio/__init__.c \ audioio/Mixer.c \ + audioio/MixerVoice.c \ audioio/RawSample.c \ audioio/WaveFile.c endif diff --git a/shared-bindings/audioio/Mixer.c b/shared-bindings/audioio/Mixer.c index baf86c803..b447ab277 100644 --- a/shared-bindings/audioio/Mixer.c +++ b/shared-bindings/audioio/Mixer.c @@ -24,6 +24,7 @@ * THE SOFTWARE. */ #include "shared-bindings/audioio/Mixer.h" +#include "shared-module/audioio/MixerVoice.h" #include @@ -110,6 +111,11 @@ STATIC mp_obj_t audioio_mixer_make_new(const mp_obj_type_t *type, size_t n_args, 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); + for(int v=0; vvoice[v] = audioio_mixervoice_type.make_new(&audioio_mixervoice_type, 0, 0, NULL); + } + self->voice_tuple = mp_obj_new_tuple(self->voice_count, self->voice); + return MP_OBJ_FROM_PTR(self); } @@ -233,7 +239,7 @@ const mp_obj_property_t audioio_mixer_sample_rate_obj = { STATIC mp_obj_t audioio_mixer_obj_get_voice(mp_obj_t self_in) { 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_tuple(self->voice_count, self->voice); + return self->voice_tuple; } MP_DEFINE_CONST_FUN_OBJ_1(audioio_mixer_get_voice_obj, audioio_mixer_obj_get_voice); diff --git a/shared-bindings/audioio/MixerVoice.c b/shared-bindings/audioio/MixerVoice.c index 9cc8a6339..d1680fa71 100644 --- a/shared-bindings/audioio/MixerVoice.c +++ b/shared-bindings/audioio/MixerVoice.c @@ -5,6 +5,7 @@ * Author: dean */ #include "shared-bindings/audioio/Mixer.h" +#include "shared-bindings/audioio/MixerVoice.h" #include @@ -125,7 +126,7 @@ STATIC mp_obj_t audioio_mixervoice_obj_set_gain(size_t n_args, const mp_obj_t *p static const mp_arg_t allowed_args[] = { { MP_QSTR_gain, MP_ARG_OBJ | MP_ARG_REQUIRED }, }; - audioio_mixer_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + audioio_mixervoice_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); 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); diff --git a/shared-bindings/audioio/MixerVoice.h b/shared-bindings/audioio/MixerVoice.h index f4c868a0e..4eb588e6e 100644 --- a/shared-bindings/audioio/MixerVoice.h +++ b/shared-bindings/audioio/MixerVoice.h @@ -8,22 +8,10 @@ #ifndef SHARED_BINDINGS_AUDIOIO_MIXERVOICE_H_ #define SHARED_BINDINGS_AUDIOIO_MIXERVOICE_H_ -#include "py/obj.h" - -#include "shared-module/audioio/__init__.h" - #include "common-hal/microcontroller/Pin.h" #include "shared-module/audioio/Mixer.h" #include "shared-bindings/audioio/RawSample.h" - -typedef struct { - mp_obj_t sample; - bool loop; - bool more_data; - uint32_t* remaining_buffer; - uint32_t buffer_length; - int16_t gain; -} audioio_mixervoice_obj_t; +#include "shared-module/audioio/MixerVoice.h" extern const mp_obj_type_t audioio_mixer_type; extern const mp_obj_type_t audioio_mixervoice_type; diff --git a/shared-module/audioio/Mixer.c b/shared-module/audioio/Mixer.c index 4797f4adc..28815470d 100644 --- a/shared-module/audioio/Mixer.c +++ b/shared-module/audioio/Mixer.c @@ -59,10 +59,12 @@ void common_hal_audioio_mixer_construct(audioio_mixer_obj_t* self, self->sample_rate = sample_rate; self->voice_count = voice_count; +#if 0 for (uint8_t i = 0; i < self->voice_count; i++) { self->voice[i].sample = NULL; self->voice[i].gain = ((1<<15)-1); } +#endif } void common_hal_audioio_mixer_deinit(audioio_mixer_obj_t* self) { @@ -100,6 +102,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")); } +#if 0 audioio_mixervoice_obj_t* voice = &self->voice[v]; voice->sample = sample; voice->loop = loop; @@ -109,27 +112,35 @@ void common_hal_audioio_mixer_play(audioio_mixer_obj_t* self, mp_obj_t sample, u // Track length in terms of words. voice->buffer_length /= sizeof(uint32_t); voice->more_data = result == GET_BUFFER_MORE_DATA; + +#endif } void common_hal_audioio_mixer_stop_voice(audioio_mixer_obj_t* self, uint8_t voice) { +#if 0 self->voice[voice].sample = NULL; +#endif } bool common_hal_audioio_mixer_get_playing(audioio_mixer_obj_t* self) { +#if 0 for (int32_t v = 0; v < self->voice_count; v++) { if (self->voice[v].sample != NULL) { return true; } } +#endif return false; } void audioio_mixer_reset_buffer(audioio_mixer_obj_t* self, bool single_channel, uint8_t channel) { +#if 0 for (int32_t i = 0; i < self->voice_count; i++) { self->voice[i].sample = NULL; } +#endif } uint32_t add8signed(uint32_t a, uint32_t b) { @@ -283,6 +294,7 @@ audioio_get_buffer_result_t audioio_mixer_get_buffer(audioio_mixer_obj_t* self, uint8_t channel, uint8_t** buffer, uint32_t* buffer_length) { +#if 0 if (!single_channel) { channel = 0; } @@ -403,6 +415,7 @@ audioio_get_buffer_result_t audioio_mixer_get_buffer(audioio_mixer_obj_t* self, self->right_read_count += 1; *buffer = *buffer + self->bits_per_sample / 8; } +#endif return GET_BUFFER_MORE_DATA; } diff --git a/shared-module/audioio/Mixer.h b/shared-module/audioio/Mixer.h index 40b6df2fe..083a3ce7e 100644 --- a/shared-module/audioio/Mixer.h +++ b/shared-module/audioio/Mixer.h @@ -28,6 +28,7 @@ #define MICROPY_INCLUDED_SHARED_MODULE_AUDIOIO_MIXER_H #include "py/obj.h" +#include "py/objtuple.h" #include "shared-module/audioio/__init__.h" #include "shared-bindings/audioio/MixerVoice.h" @@ -48,6 +49,7 @@ typedef struct { uint32_t right_read_count; uint8_t voice_count; + mp_obj_tuple_t *voice_tuple; mp_obj_t voice[]; } audioio_mixer_obj_t; diff --git a/shared-module/audioio/MixerVoice.c b/shared-module/audioio/MixerVoice.c index ff40da34c..28df1c5d1 100644 --- a/shared-module/audioio/MixerVoice.c +++ b/shared-module/audioio/MixerVoice.c @@ -13,6 +13,6 @@ #include "shared-module/audioio/__init__.h" #include "shared-module/audioio/RawSample.h" -void common_hal_audioio_mixervoice_set_gain(audioio_mixer_obj_t* self, float gain) { +void common_hal_audioio_mixervoice_set_gain(audioio_mixervoice_obj_t* self, float gain) { self->gain = gain * ((1 << 15)-1); } diff --git a/shared-module/audioio/MixerVoice.h b/shared-module/audioio/MixerVoice.h index c04e8ef24..b917829dd 100644 --- a/shared-module/audioio/MixerVoice.h +++ b/shared-module/audioio/MixerVoice.h @@ -8,5 +8,19 @@ #ifndef SHARED_MODULE_AUDIOIO_MIXERVOICE_H_ #define SHARED_MODULE_AUDIOIO_MIXERVOICE_H_ +#include "py/obj.h" + +#include "shared-module/audioio/__init__.h" + +typedef struct { + mp_obj_base_t base; + mp_obj_t sample; + bool loop; + bool more_data; + uint32_t* remaining_buffer; + uint32_t buffer_length; + int16_t gain; +} audioio_mixervoice_obj_t; + #endif /* SHARED_MODULE_AUDIOIO_MIXERVOICE_H_ */ -- cgit v1.2.3 From 1bf6c588e7e1ea11c865c33f660ec126f752b1dc Mon Sep 17 00:00:00 2001 From: dean Date: Tue, 20 Nov 2018 14:14:22 -0500 Subject: DM: mixer voice stuff --- shared-bindings/audioio/Mixer.c | 54 ++------------------------------- shared-bindings/audioio/Mixer.h | 4 --- shared-bindings/audioio/MixerVoice.c | 18 +++++------ shared-bindings/audioio/MixerVoice.h | 2 ++ shared-module/audioio/Mixer.c | 58 ++---------------------------------- shared-module/audioio/Mixer.h | 1 - shared-module/audioio/MixerVoice.c | 42 ++++++++++++++++++++++++++ shared-module/audioio/MixerVoice.h | 2 ++ 8 files changed, 60 insertions(+), 121 deletions(-) (limited to 'shared-module') diff --git a/shared-bindings/audioio/Mixer.c b/shared-bindings/audioio/Mixer.c index b447ab277..e962ed0cf 100644 --- a/shared-bindings/audioio/Mixer.c +++ b/shared-bindings/audioio/Mixer.c @@ -25,6 +25,7 @@ */ #include "shared-bindings/audioio/Mixer.h" #include "shared-module/audioio/MixerVoice.h" +#include "shared-bindings/audioio/MixerVoice.h" #include @@ -113,6 +114,7 @@ STATIC mp_obj_t audioio_mixer_make_new(const mp_obj_type_t *type, size_t n_args, for(int v=0; vvoice[v] = audioio_mixervoice_type.make_new(&audioio_mixervoice_type, 0, 0, NULL); + common_hal_audioio_mixervoice_set_parent(self->voice[v], self); } self->voice_tuple = mp_obj_new_tuple(self->voice_count, self->voice); @@ -148,54 +150,6 @@ STATIC mp_obj_t audioio_mixer_obj___exit__(size_t n_args, const mp_obj_t *args) } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(audioio_mixer___exit___obj, 4, 4, audioio_mixer_obj___exit__); - -//| .. method:: play(sample, *, voice=0, 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_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_sample, ARG_voice, ARG_loop }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_sample, MP_ARG_OBJ | MP_ARG_REQUIRED }, - { MP_QSTR_voice, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, - { MP_QSTR_loop, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, - }; - 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); - - mp_obj_t sample = args[ARG_sample].u_obj; - common_hal_audioio_mixer_play(self, sample, args[ARG_voice].u_int, args[ARG_loop].u_bool); - - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_KW(audioio_mixer_play_obj, 1, audioio_mixer_obj_play); - -//| .. method:: stop_voice(voice=0) -//| -//| Stops playback of the sample on the given voice. -//| -STATIC mp_obj_t audioio_mixer_obj_stop_voice(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_voice }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_voice, MP_ARG_INT, {.u_int = 0} }, - }; - 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); - - common_hal_audioio_mixer_stop_voice(self, args[ARG_voice].u_int); - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_KW(audioio_mixer_stop_voice_obj, 1, audioio_mixer_obj_stop_voice); - //| .. attribute:: playing //| //| True when any voice is being output. (read-only) @@ -245,7 +199,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(audioio_mixer_get_voice_obj, audioio_mixer_obj_get_voi const mp_obj_property_t audioio_mixer_voice_obj = { .base.type = &mp_type_property, - .proxy = {(mp_obj_t)&audioio_mixer_obj_get_voice, + .proxy = {(mp_obj_t)&audioio_mixer_get_voice_obj, (mp_obj_t)&mp_const_none_obj, (mp_obj_t)&mp_const_none_obj}, }; @@ -256,8 +210,6 @@ STATIC const mp_rom_map_elem_t audioio_mixer_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&audioio_mixer_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___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) }, // Properties { MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&audioio_mixer_playing_obj) }, diff --git a/shared-bindings/audioio/Mixer.h b/shared-bindings/audioio/Mixer.h index 1769f3bb7..2e5adddf7 100644 --- a/shared-bindings/audioio/Mixer.h +++ b/shared-bindings/audioio/Mixer.h @@ -44,10 +44,6 @@ void common_hal_audioio_mixer_construct(audioio_mixer_obj_t* self, void common_hal_audioio_mixer_deinit(audioio_mixer_obj_t* self); bool common_hal_audioio_mixer_deinited(audioio_mixer_obj_t* self); -void common_hal_audioio_mixer_play(audioio_mixer_obj_t* self, mp_obj_t sample, uint8_t voice, bool loop); -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); diff --git a/shared-bindings/audioio/MixerVoice.c b/shared-bindings/audioio/MixerVoice.c index d1680fa71..5cddb291c 100644 --- a/shared-bindings/audioio/MixerVoice.c +++ b/shared-bindings/audioio/MixerVoice.c @@ -36,7 +36,8 @@ STATIC mp_obj_t audioio_mixervoice_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *pos_args) { audioio_mixervoice_obj_t *self = m_new(audioio_mixervoice_obj_t, 1); self->base.type = &audioio_mixervoice_type; - + self->sample = NULL; + self->gain = ((1 << 15)-1); return MP_OBJ_FROM_PTR(self); } @@ -76,41 +77,38 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(audioio_mixervoice___exit___obj, 4, 4 //| The sample must match the Mixer's encoding settings given in the constructor. //| STATIC mp_obj_t audioio_mixervoice_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_mixervoice_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); - raise_error_if_deinited(common_hal_audioio_mixervoice_deinited(self)); + //raise_error_if_deinited(common_hal_audioio_mixervoice_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 + common_hal_audioio_mixervoice_play(self, sample, args[ARG_loop].u_bool); return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_KW(audioio_mixervoice_play_obj, 1, audioio_mixervoice_obj_play); -//| .. method:: stop_voice() +//| .. method:: stop() //| //| Stops playback of the sample on this voice. //| STATIC mp_obj_t audioio_mixervoice_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_mixervoice_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); - raise_error_if_deinited(common_hal_audioio_mixer_deinited(self)); + //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 + common_hal_audioio_mixervoice_stop(self); + return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_KW(audioio_mixervoice_stop_obj, 1, audioio_mixervoice_obj_stop); diff --git a/shared-bindings/audioio/MixerVoice.h b/shared-bindings/audioio/MixerVoice.h index 4eb588e6e..1320773ec 100644 --- a/shared-bindings/audioio/MixerVoice.h +++ b/shared-bindings/audioio/MixerVoice.h @@ -12,11 +12,13 @@ #include "shared-module/audioio/Mixer.h" #include "shared-bindings/audioio/RawSample.h" #include "shared-module/audioio/MixerVoice.h" +#include "shared-module/audioio/Mixer.h" extern const mp_obj_type_t audioio_mixer_type; extern const mp_obj_type_t audioio_mixervoice_type; void common_hal_audioio_mixervoice_deinit(audioio_mixervoice_obj_t* self); +void common_hal_audioio_mixervoice_set_parent(audioio_mixervoice_obj_t* self, audioio_mixer_obj_t *parent); void common_hal_audioio_mixervoice_play(audioio_mixervoice_obj_t* self, mp_obj_t sample, bool loop); void common_hal_audioio_mixervoice_stop(audioio_mixervoice_obj_t* self); void common_hal_audioio_mixervoice_set_gain(audioio_mixervoice_obj_t* self, float gain); diff --git a/shared-module/audioio/Mixer.c b/shared-module/audioio/Mixer.c index 28815470d..ea53140ab 100644 --- a/shared-module/audioio/Mixer.c +++ b/shared-module/audioio/Mixer.c @@ -25,6 +25,7 @@ */ #include "shared-bindings/audioio/Mixer.h" +#include "shared-bindings/audioio/MixerVoice.h" #include @@ -58,13 +59,6 @@ void common_hal_audioio_mixer_construct(audioio_mixer_obj_t* self, self->channel_count = channel_count; self->sample_rate = sample_rate; self->voice_count = voice_count; - -#if 0 - for (uint8_t i = 0; i < self->voice_count; i++) { - self->voice[i].sample = NULL; - self->voice[i].gain = ((1<<15)-1); - } -#endif } void common_hal_audioio_mixer_deinit(audioio_mixer_obj_t* self) { @@ -80,56 +74,12 @@ uint32_t common_hal_audioio_mixer_get_sample_rate(audioio_mixer_obj_t* self) { return self->sample_rate; } -void common_hal_audioio_mixer_play(audioio_mixer_obj_t* self, mp_obj_t sample, uint8_t v, bool loop) { - if (v >= self->voice_count) { - mp_raise_ValueError(translate("Voice index too high")); - } - if (audiosample_sample_rate(sample) != self->sample_rate) { - mp_raise_ValueError(translate("The sample's sample rate does not match the mixer's")); - } - if (audiosample_channel_count(sample) != self->channel_count) { - mp_raise_ValueError(translate("The sample's channel count does not match the mixer's")); - } - if (audiosample_bits_per_sample(sample) != self->bits_per_sample) { - mp_raise_ValueError(translate("The sample's bits_per_sample does not match the mixer's")); - } - bool single_buffer; - bool samples_signed; - uint32_t max_buffer_length; - uint8_t spacing; - audiosample_get_buffer_structure(sample, false, &single_buffer, &samples_signed, - &max_buffer_length, &spacing); - if (samples_signed != self->samples_signed) { - mp_raise_ValueError(translate("The sample's signedness does not match the mixer's")); - } -#if 0 - audioio_mixervoice_obj_t* voice = &self->voice[v]; - voice->sample = sample; - voice->loop = loop; - - audiosample_reset_buffer(sample, false, 0); - audioio_get_buffer_result_t result = audiosample_get_buffer(sample, false, 0, (uint8_t**) &voice->remaining_buffer, &voice->buffer_length); - // Track length in terms of words. - voice->buffer_length /= sizeof(uint32_t); - voice->more_data = result == GET_BUFFER_MORE_DATA; - -#endif -} - -void common_hal_audioio_mixer_stop_voice(audioio_mixer_obj_t* self, uint8_t voice) { -#if 0 - self->voice[voice].sample = NULL; -#endif -} - bool common_hal_audioio_mixer_get_playing(audioio_mixer_obj_t* self) { -#if 0 for (int32_t v = 0; v < self->voice_count; v++) { - if (self->voice[v].sample != NULL) { + if (common_hal_audioio_mixervoice_get_playing(MP_OBJ_TO_PTR(self->voice[v]))) { return true; } } -#endif return false; } @@ -294,7 +244,6 @@ audioio_get_buffer_result_t audioio_mixer_get_buffer(audioio_mixer_obj_t* self, uint8_t channel, uint8_t** buffer, uint32_t* buffer_length) { -#if 0 if (!single_channel) { channel = 0; } @@ -318,7 +267,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_mixervoice_obj_t* voice = &self->voice[v]; + audioio_mixervoice_obj_t* voice = MP_OBJ_TO_PTR(self->voice[v]); uint32_t j = 0; bool voice_done = voice->sample == NULL; @@ -415,7 +364,6 @@ audioio_get_buffer_result_t audioio_mixer_get_buffer(audioio_mixer_obj_t* self, self->right_read_count += 1; *buffer = *buffer + self->bits_per_sample / 8; } -#endif return GET_BUFFER_MORE_DATA; } diff --git a/shared-module/audioio/Mixer.h b/shared-module/audioio/Mixer.h index 083a3ce7e..db5c6ce6f 100644 --- a/shared-module/audioio/Mixer.h +++ b/shared-module/audioio/Mixer.h @@ -31,7 +31,6 @@ #include "py/objtuple.h" #include "shared-module/audioio/__init__.h" -#include "shared-bindings/audioio/MixerVoice.h" typedef struct { mp_obj_base_t base; diff --git a/shared-module/audioio/MixerVoice.c b/shared-module/audioio/MixerVoice.c index 28df1c5d1..3579d500f 100644 --- a/shared-module/audioio/MixerVoice.c +++ b/shared-module/audioio/MixerVoice.c @@ -12,7 +12,49 @@ #include "py/runtime.h" #include "shared-module/audioio/__init__.h" #include "shared-module/audioio/RawSample.h" +#include "shared-module/audioio/MixerVoice.h" + +void common_hal_audioio_mixervoice_set_parent(audioio_mixervoice_obj_t* self, audioio_mixer_obj_t *parent) { + self->parent = parent; +} void common_hal_audioio_mixervoice_set_gain(audioio_mixervoice_obj_t* self, float gain) { self->gain = gain * ((1 << 15)-1); } + +void common_hal_audioio_mixervoice_play(audioio_mixervoice_obj_t* self, mp_obj_t sample, bool loop) { + if (audiosample_sample_rate(sample) != self->parent->sample_rate) { + mp_raise_ValueError(translate("The sample's sample rate does not match the mixer's")); + } + if (audiosample_channel_count(sample) != self->parent->channel_count) { + mp_raise_ValueError(translate("The sample's channel count does not match the mixer's")); + } + if (audiosample_bits_per_sample(sample) != self->parent->bits_per_sample) { + mp_raise_ValueError(translate("The sample's bits_per_sample does not match the mixer's")); + } + bool single_buffer; + bool samples_signed; + uint32_t max_buffer_length; + uint8_t spacing; + audiosample_get_buffer_structure(sample, false, &single_buffer, &samples_signed, + &max_buffer_length, &spacing); + if (samples_signed != self->parent->samples_signed) { + mp_raise_ValueError(translate("The sample's signedness does not match the mixer's")); + } + self->sample = sample; + self->loop = loop; + + audiosample_reset_buffer(sample, false, 0); + audioio_get_buffer_result_t result = audiosample_get_buffer(sample, false, 0, (uint8_t**) &self->remaining_buffer, &self->buffer_length); + // Track length in terms of words. + self->buffer_length /= sizeof(uint32_t); + self->more_data = result == GET_BUFFER_MORE_DATA; +} + +bool common_hal_audioio_mixervoice_get_playing(audioio_mixervoice_obj_t* self) { + return self->sample != NULL; +} + +void common_hal_audioio_mixervoice_stop(audioio_mixervoice_obj_t* self) { + self->sample = NULL; +} diff --git a/shared-module/audioio/MixerVoice.h b/shared-module/audioio/MixerVoice.h index b917829dd..1cf686a38 100644 --- a/shared-module/audioio/MixerVoice.h +++ b/shared-module/audioio/MixerVoice.h @@ -11,9 +11,11 @@ #include "py/obj.h" #include "shared-module/audioio/__init__.h" +#include "shared-module/audioio/Mixer.h" typedef struct { mp_obj_base_t base; + audioio_mixer_obj_t *parent; mp_obj_t sample; bool loop; bool more_data; -- cgit v1.2.3 From 7aa0840b730279f33c769849e9fa3065a2eeaf79 Mon Sep 17 00:00:00 2001 From: sommersoft Date: Tue, 12 Mar 2019 10:24:54 -0500 Subject: change voice 'gain' to 'level' --- shared-bindings/audioio/MixerVoice.c | 24 ++++++++++++------------ shared-bindings/audioio/MixerVoice.h | 2 +- shared-module/audioio/Mixer.c | 10 +++++----- shared-module/audioio/MixerVoice.c | 4 ++-- shared-module/audioio/MixerVoice.h | 2 +- 5 files changed, 21 insertions(+), 21 deletions(-) (limited to 'shared-module') diff --git a/shared-bindings/audioio/MixerVoice.c b/shared-bindings/audioio/MixerVoice.c index 2bcafaae0..02f57d419 100644 --- a/shared-bindings/audioio/MixerVoice.c +++ b/shared-bindings/audioio/MixerVoice.c @@ -37,7 +37,7 @@ STATIC mp_obj_t audioio_mixervoice_make_new(const mp_obj_type_t *type, size_t n_ audioio_mixervoice_obj_t *self = m_new_obj(audioio_mixervoice_obj_t); self->base.type = &audioio_mixervoice_type; self->sample = NULL; - self->gain = ((1 << 15)-1); + self->level = ((1 << 15)-1); return MP_OBJ_FROM_PTR(self); } @@ -119,35 +119,35 @@ MP_DEFINE_CONST_FUN_OBJ_KW(audioio_mixervoice_stop_obj, 1, audioio_mixervoice_ob //| //| gain must be a floating point number between 0 and 1 //| -STATIC mp_obj_t audioio_mixervoice_obj_set_gain(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_gain }; +STATIC mp_obj_t audioio_mixervoice_obj_set_level(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_level }; static const mp_arg_t allowed_args[] = { - { MP_QSTR_gain, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_level, MP_ARG_OBJ | MP_ARG_REQUIRED }, }; audioio_mixervoice_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); 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); + float level = mp_obj_get_float(args[ARG_level].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")); + if (level > 1 || level < 0) { + mp_raise_ValueError(translate("level must be between 0 and 1")); } - common_hal_audioio_mixervoice_set_gain(self, gain); + common_hal_audioio_mixervoice_set_level(self, level); return mp_const_none; } -MP_DEFINE_CONST_FUN_OBJ_KW(audioio_mixervoice_set_gain_obj, 1, audioio_mixervoice_obj_set_gain); +MP_DEFINE_CONST_FUN_OBJ_KW(audioio_mixervoice_set_level_obj, 1, audioio_mixervoice_obj_set_level); -const mp_obj_property_t audioio_mixervoice_gain_obj = { +const mp_obj_property_t audioio_mixervoice_level_obj = { .base.type = &mp_type_property, .proxy = {(mp_obj_t)&mp_const_none_obj, - (mp_obj_t)&audioio_mixervoice_set_gain_obj, + (mp_obj_t)&audioio_mixervoice_set_level_obj, (mp_obj_t)&mp_const_none_obj}, }; @@ -182,7 +182,7 @@ STATIC const mp_rom_map_elem_t audioio_mixervoice_locals_dict_table[] = { // Properties { MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&audioio_mixervoice_playing_obj) }, - { MP_ROM_QSTR(MP_QSTR_gain), MP_ROM_PTR(&audioio_mixervoice_gain_obj) }, + { MP_ROM_QSTR(MP_QSTR_level), MP_ROM_PTR(&audioio_mixervoice_level_obj) }, }; STATIC MP_DEFINE_CONST_DICT(audioio_mixervoice_locals_dict, audioio_mixervoice_locals_dict_table); diff --git a/shared-bindings/audioio/MixerVoice.h b/shared-bindings/audioio/MixerVoice.h index 1320773ec..a67a5ec90 100644 --- a/shared-bindings/audioio/MixerVoice.h +++ b/shared-bindings/audioio/MixerVoice.h @@ -21,7 +21,7 @@ void common_hal_audioio_mixervoice_deinit(audioio_mixervoice_obj_t* self); void common_hal_audioio_mixervoice_set_parent(audioio_mixervoice_obj_t* self, audioio_mixer_obj_t *parent); void common_hal_audioio_mixervoice_play(audioio_mixervoice_obj_t* self, mp_obj_t sample, bool loop); void common_hal_audioio_mixervoice_stop(audioio_mixervoice_obj_t* self); -void common_hal_audioio_mixervoice_set_gain(audioio_mixervoice_obj_t* self, float gain); +void common_hal_audioio_mixervoice_set_level(audioio_mixervoice_obj_t* self, float gain); bool common_hal_audioio_mixervoice_get_playing(audioio_mixervoice_obj_t* self); diff --git a/shared-module/audioio/Mixer.c b/shared-module/audioio/Mixer.c index ea53140ab..dc92ce1da 100644 --- a/shared-module/audioio/Mixer.c +++ b/shared-module/audioio/Mixer.c @@ -309,19 +309,19 @@ audioio_get_buffer_result_t audioio_mixer_get_buffer(audioio_mixer_obj_t* self, sample_value = voice->remaining_buffer[j]; } - // apply the mixer gain + // apply the mixer level if (!self->samples_signed) { if (self->bits_per_sample == 8) { - sample_value = mult8unsigned(sample_value, voice->gain); + sample_value = mult8unsigned(sample_value, voice->level); } else { - sample_value = mult16unsigned(sample_value, voice->gain); + sample_value = mult16unsigned(sample_value, voice->level); } } else{ if (self->bits_per_sample == 8) { - sample_value = mult8signed(sample_value, voice->gain); + sample_value = mult8signed(sample_value, voice->level); } else { - sample_value = mult16signed(sample_value, voice->gain); + sample_value = mult16signed(sample_value, voice->level); } } diff --git a/shared-module/audioio/MixerVoice.c b/shared-module/audioio/MixerVoice.c index 3579d500f..024460af1 100644 --- a/shared-module/audioio/MixerVoice.c +++ b/shared-module/audioio/MixerVoice.c @@ -18,8 +18,8 @@ void common_hal_audioio_mixervoice_set_parent(audioio_mixervoice_obj_t* self, au self->parent = parent; } -void common_hal_audioio_mixervoice_set_gain(audioio_mixervoice_obj_t* self, float gain) { - self->gain = gain * ((1 << 15)-1); +void common_hal_audioio_mixervoice_set_level(audioio_mixervoice_obj_t* self, float level) { + self->level = level * ((1 << 15)-1); } void common_hal_audioio_mixervoice_play(audioio_mixervoice_obj_t* self, mp_obj_t sample, bool loop) { diff --git a/shared-module/audioio/MixerVoice.h b/shared-module/audioio/MixerVoice.h index 1cf686a38..2811a741a 100644 --- a/shared-module/audioio/MixerVoice.h +++ b/shared-module/audioio/MixerVoice.h @@ -21,7 +21,7 @@ typedef struct { bool more_data; uint32_t* remaining_buffer; uint32_t buffer_length; - int16_t gain; + int16_t level; } audioio_mixervoice_obj_t; -- cgit v1.2.3 From a498bcd94d7f869e2cb2e992babacb981de688c0 Mon Sep 17 00:00:00 2001 From: sommersoft Date: Tue, 12 Mar 2019 16:10:54 -0500 Subject: add voice level getter --- shared-bindings/audioio/MixerVoice.c | 18 +++++++++++++++++- shared-bindings/audioio/MixerVoice.h | 1 + shared-module/audioio/MixerVoice.c | 4 ++++ 3 files changed, 22 insertions(+), 1 deletion(-) (limited to 'shared-module') diff --git a/shared-bindings/audioio/MixerVoice.c b/shared-bindings/audioio/MixerVoice.c index 02f57d419..cba82feb2 100644 --- a/shared-bindings/audioio/MixerVoice.c +++ b/shared-bindings/audioio/MixerVoice.c @@ -113,6 +113,22 @@ STATIC mp_obj_t audioio_mixervoice_obj_stop(size_t n_args, const mp_obj_t *pos_a } MP_DEFINE_CONST_FUN_OBJ_KW(audioio_mixervoice_stop_obj, 1, audioio_mixervoice_obj_stop); +//| .. method:: get_gain(voice, gain) +//| +//| Get the gain of a voice. +//| +//| Returns as a floating point number between 0 and 1 +//| +STATIC mp_obj_t audioio_mixervoice_obj_get_level(mp_obj_t self_in) { + + #if !MICROPY_PY_BUILTINS_FLOAT + #error "floating point not supported" + #endif + + return mp_obj_new_float(common_hal_audioio_mixervoice_get_level(self_in)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audioio_mixervoice_get_level_obj, audioio_mixervoice_obj_get_level); + //| .. method:: set_gain(voice, gain) //| //| Set the gain of a voice. @@ -146,7 +162,7 @@ MP_DEFINE_CONST_FUN_OBJ_KW(audioio_mixervoice_set_level_obj, 1, audioio_mixervoi const mp_obj_property_t audioio_mixervoice_level_obj = { .base.type = &mp_type_property, - .proxy = {(mp_obj_t)&mp_const_none_obj, + .proxy = {(mp_obj_t)&audioio_mixervoice_get_level_obj, (mp_obj_t)&audioio_mixervoice_set_level_obj, (mp_obj_t)&mp_const_none_obj}, }; diff --git a/shared-bindings/audioio/MixerVoice.h b/shared-bindings/audioio/MixerVoice.h index a67a5ec90..641a0faf2 100644 --- a/shared-bindings/audioio/MixerVoice.h +++ b/shared-bindings/audioio/MixerVoice.h @@ -21,6 +21,7 @@ void common_hal_audioio_mixervoice_deinit(audioio_mixervoice_obj_t* self); void common_hal_audioio_mixervoice_set_parent(audioio_mixervoice_obj_t* self, audioio_mixer_obj_t *parent); void common_hal_audioio_mixervoice_play(audioio_mixervoice_obj_t* self, mp_obj_t sample, bool loop); void common_hal_audioio_mixervoice_stop(audioio_mixervoice_obj_t* self); +float common_hal_audioio_mixervoice_get_level(audioio_mixervoice_obj_t* self); void common_hal_audioio_mixervoice_set_level(audioio_mixervoice_obj_t* self, float gain); bool common_hal_audioio_mixervoice_get_playing(audioio_mixervoice_obj_t* self); diff --git a/shared-module/audioio/MixerVoice.c b/shared-module/audioio/MixerVoice.c index 024460af1..3e8aaff89 100644 --- a/shared-module/audioio/MixerVoice.c +++ b/shared-module/audioio/MixerVoice.c @@ -18,6 +18,10 @@ void common_hal_audioio_mixervoice_set_parent(audioio_mixervoice_obj_t* self, au self->parent = parent; } +float common_hal_audioio_mixervoice_get_level(audioio_mixervoice_obj_t* self) { + return self->level; +} + void common_hal_audioio_mixervoice_set_level(audioio_mixervoice_obj_t* self, float level) { self->level = level * ((1 << 15)-1); } -- cgit v1.2.3 From 065efb05b75e35f7e161b6828742b01d26ce5379 Mon Sep 17 00:00:00 2001 From: sommersoft Date: Sat, 3 Aug 2019 11:20:06 -0500 Subject: bring MixerVoice back to building state; update documentation --- py/circuitpy_defns.mk | 1 + shared-bindings/audiocore/Mixer.c | 82 ++++-------- shared-bindings/audiocore/MixerVoice.c | 221 +++++++++++++++++++++++++++++++++ shared-bindings/audiocore/MixerVoice.h | 47 +++++++ shared-bindings/audiocore/__init__.c | 1 + shared-bindings/audioio/MixerVoice.c | 210 ------------------------------- shared-bindings/audioio/MixerVoice.h | 29 ----- shared-module/audiocore/Mixer.c | 1 + shared-module/audiocore/MixerVoice.c | 82 ++++++++++++ shared-module/audiocore/MixerVoice.h | 46 +++++++ shared-module/audioio/MixerVoice.c | 64 ---------- shared-module/audioio/MixerVoice.h | 28 ----- 12 files changed, 424 insertions(+), 388 deletions(-) create mode 100644 shared-bindings/audiocore/MixerVoice.c create mode 100644 shared-bindings/audiocore/MixerVoice.h delete mode 100644 shared-bindings/audioio/MixerVoice.c delete mode 100644 shared-bindings/audioio/MixerVoice.h create mode 100644 shared-module/audiocore/MixerVoice.c create mode 100644 shared-module/audiocore/MixerVoice.h delete mode 100644 shared-module/audioio/MixerVoice.c delete mode 100644 shared-module/audioio/MixerVoice.h (limited to 'shared-module') diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index bdefc18cc..16a2474f3 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -306,6 +306,7 @@ $(filter $(SRC_PATTERNS), \ audioio/__init__.c \ audiocore/__init__.c \ audiocore/Mixer.c \ + audiocore/MixerVoice.c \ audiocore/RawSample.c \ audiocore/WaveFile.c \ bitbangio/I2C.c \ diff --git a/shared-bindings/audiocore/Mixer.c b/shared-bindings/audiocore/Mixer.c index ee4d8548c..66babdd08 100644 --- a/shared-bindings/audiocore/Mixer.c +++ b/shared-bindings/audiocore/Mixer.c @@ -24,6 +24,8 @@ * THE SOFTWARE. */ #include "shared-bindings/audiocore/Mixer.h" +#include "shared-bindings/audiocore/MixerVoice.h" +#include "shared-module/audiocore/MixerVoice.h" #include @@ -43,33 +45,43 @@ //| //| Mixer mixes multiple samples into one sample. //| -//| .. class:: Mixer(channel_count=2, buffer_size=1024) +//| .. class:: Mixer(voice_count=2, buffer_size=1024, channel_count=2, bits_per_sample=16, samples_signed=True, sample_rate=8000) //| //| Create a Mixer object that can mix multiple channels with the same sample rate. +//| Samples are accessed and controlled with the mixer's `audioio.MixerVoice` objects. //| -//| :param int channel_count: The maximum number of samples to mix at once +//| :param int voice_count: The maximum number of voices to mix //| :param int buffer_size: The total size in bytes of the buffers to mix into +//| :param int channel_count: The maximum number of samples to mix at once +//| :param int bits_per_sample: The bits per sample of the samples being played +//| :param bool samples_signed: Samples are signed (True) or unsigned (False) +//| :param int sample_rate: The sample rate to be used for all samples //| //| Playing a wave file from flash:: //| //| import board //| import audioio +//| import audiocore //| import digitalio //| //| # Required for CircuitPlayground Express //| speaker_enable = digitalio.DigitalInOut(board.SPEAKER_ENABLE) //| speaker_enable.switch_to_output(value=True) //| -//| music = audioio.WaveFile(open("cplay-5.1-16bit-16khz.wav", "rb")) -//| drum = audioio.WaveFile(open("drum.wav", "rb")) -//| mixer = audioio.Mixer(voice_count=2, sample_rate=16000, channel_count=1, bits_per_sample=16, samples_signed=True) +//| music = audiocore.WaveFile(open("cplay-5.1-16bit-16khz.wav", "rb")) +//| drum = audiocore.WaveFile(open("drum.wav", "rb")) +//| mixer = audiocore.Mixer(voice_count=2, sample_rate=16000, channel_count=1, +//| bits_per_sample=16, samples_signed=True) //| a = audioio.AudioOut(board.A0) //| //| print("playing") +//| # Have AudioOut play our Mixer source //| a.play(mixer) -//| mixer.play(music, voice=0) +//| # Play the first sample voice +//| mixer.voice[0].play(music) //| while mixer.playing: -//| mixer.play(drum, voice=1) +//| # Play the second sample voice +//| mixer.voice[1].play(drum) //| time.sleep(1) //| print("stopped") //| @@ -151,54 +163,6 @@ STATIC mp_obj_t audioio_mixer_obj___exit__(size_t n_args, const mp_obj_t *args) } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(audioio_mixer___exit___obj, 4, 4, audioio_mixer_obj___exit__); - -//| .. method:: play(sample, *, voice=0, 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_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_sample, ARG_voice, ARG_loop }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_sample, MP_ARG_OBJ | MP_ARG_REQUIRED }, - { MP_QSTR_voice, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, - { MP_QSTR_loop, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, - }; - audioio_mixer_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); - check_for_deinit(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, args[ARG_voice].u_int, args[ARG_loop].u_bool); - - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_KW(audioio_mixer_play_obj, 1, audioio_mixer_obj_play); - -//| .. method:: stop_voice(voice=0) -//| -//| Stops playback of the sample on the given voice. -//| -STATIC mp_obj_t audioio_mixer_obj_stop_voice(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_voice }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_voice, MP_ARG_INT, {.u_int = 0} }, - }; - audioio_mixer_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); - check_for_deinit(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); - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_KW(audioio_mixer_stop_voice_obj, 1, audioio_mixer_obj_stop_voice); - //| .. attribute:: playing //| //| True when any voice is being output. (read-only) @@ -237,11 +201,15 @@ const mp_obj_property_t audioio_mixer_sample_rate_obj = { //| .. attribute:: voice //| -//| tuple of voice objects +//| A tuple of the mixer's `audioio.MixerVoice` object(s). +//| +//| .. code-block:: python //| +//| >>> mixer.voice +//| (,) STATIC mp_obj_t audioio_mixer_obj_get_voice(mp_obj_t self_in) { audioio_mixer_obj_t *self = MP_OBJ_TO_PTR(self_in); - raise_error_if_deinited(common_hal_audioio_mixer_deinited(self)); + check_for_deinit(self); return self->voice_tuple; } MP_DEFINE_CONST_FUN_OBJ_1(audioio_mixer_get_voice_obj, audioio_mixer_obj_get_voice); diff --git a/shared-bindings/audiocore/MixerVoice.c b/shared-bindings/audiocore/MixerVoice.c new file mode 100644 index 000000000..50f14c758 --- /dev/null +++ b/shared-bindings/audiocore/MixerVoice.c @@ -0,0 +1,221 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 DeanM for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "shared-bindings/audiocore/Mixer.h" +#include "shared-bindings/audiocore/MixerVoice.h" + +#include + +#include "lib/utils/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/audiocore/RawSample.h" +#include "shared-bindings/util.h" +#include "supervisor/shared/translate.h" + +//| .. currentmodule:: audioio +//| +//| :class:`MixerVoice` -- Voice objects used with Mixer +//| ===================================================== +//| +//| Used to access and control samples with `audioio.Mixer`. +//| +//| .. class:: MixerVoice() +//| +//| MixerVoice instance object(s) created by `audioio.Mixer`. +//| +// TODO: support mono or stereo voices +STATIC mp_obj_t audioio_mixervoice_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + audioio_mixervoice_obj_t *self = m_new_obj(audioio_mixervoice_obj_t); + self->base.type = &audioio_mixervoice_type; + self->sample = NULL; + self->level = ((1 << 15)-1); + return MP_OBJ_FROM_PTR(self); +} + +//| .. method:: deinit() +//| +//| Deinitialises the Voice and releases any hardware resources for reuse. +//| +STATIC mp_obj_t audioio_mixervoice_deinit(mp_obj_t self_in) { + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(audioio_mixervoice_deinit_obj, audioio_mixervoice_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_mixervoice_obj___exit__(size_t n_args, const mp_obj_t *args) { + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(audioio_mixervoice___exit___obj, 4, 4, audioio_mixervoice_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 `audioio.Mixer`'s encoding settings given in the constructor. +//| +STATIC mp_obj_t audioio_mixervoice_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + 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_mixervoice_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + //raise_error_if_deinited(common_hal_audioio_mixervoice_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_mixervoice_play(self, sample, args[ARG_loop].u_bool); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(audioio_mixervoice_play_obj, 1, audioio_mixervoice_obj_play); + +//| .. method:: stop() +//| +//| Stops playback of the sample on this voice. +//| +STATIC mp_obj_t audioio_mixervoice_obj_stop(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_voice }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_voice, MP_ARG_INT, {.u_int = 0} }, + }; + audioio_mixervoice_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_mixervoice_stop(self); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(audioio_mixervoice_stop_obj, 1, audioio_mixervoice_obj_stop); + +//| .. method:: level() +//| +//| The volume level of a voice, as a floating point number between 0 and 1. +//| +STATIC mp_obj_t audioio_mixervoice_obj_get_level(mp_obj_t self_in) { + + #if !MICROPY_PY_BUILTINS_FLOAT + #error "floating point not supported" + #endif + + return mp_obj_new_float(common_hal_audioio_mixervoice_get_level(self_in)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audioio_mixervoice_get_level_obj, audioio_mixervoice_obj_get_level); + +STATIC mp_obj_t audioio_mixervoice_obj_set_level(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_level }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_level, MP_ARG_OBJ | MP_ARG_REQUIRED }, + }; + audioio_mixervoice_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + 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 level = mp_obj_get_float(args[ARG_level].u_obj); + #else + #error "floating point not supported" + #endif + + if (level > 1 || level < 0) { + mp_raise_ValueError(translate("level must be between 0 and 1")); + } + + common_hal_audioio_mixervoice_set_level(self, level); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(audioio_mixervoice_set_level_obj, 1, audioio_mixervoice_obj_set_level); + +const mp_obj_property_t audioio_mixervoice_level_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&audioio_mixervoice_get_level_obj, + (mp_obj_t)&audioio_mixervoice_set_level_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + +//TODO: @sommersoft - enable voice.playing tracking? will need an additional field +// in the struct, with supporting logic. disabled documentation for now. + +// .. attribute:: playing +// +// True when any voice is being output. (read-only) +// +STATIC mp_obj_t audioio_mixervoice_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_mixervoice_get_playing_obj, audioio_mixervoice_obj_get_playing); + +const mp_obj_property_t audioio_mixervoice_playing_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&audioio_mixervoice_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_mixervoice_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&audioio_mixervoice_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&audioio_mixervoice___exit___obj) }, + { MP_ROM_QSTR(MP_QSTR_play), MP_ROM_PTR(&audioio_mixervoice_play_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&audioio_mixervoice_stop_obj) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&audioio_mixervoice_playing_obj) }, + { MP_ROM_QSTR(MP_QSTR_level), MP_ROM_PTR(&audioio_mixervoice_level_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(audioio_mixervoice_locals_dict, audioio_mixervoice_locals_dict_table); + +const mp_obj_type_t audioio_mixervoice_type = { + { &mp_type_type }, + .name = MP_QSTR_MixerVoice, + .make_new = audioio_mixervoice_make_new, + .locals_dict = (mp_obj_dict_t*)&audioio_mixervoice_locals_dict, +}; diff --git a/shared-bindings/audiocore/MixerVoice.h b/shared-bindings/audiocore/MixerVoice.h new file mode 100644 index 000000000..4e7ea47e7 --- /dev/null +++ b/shared-bindings/audiocore/MixerVoice.h @@ -0,0 +1,47 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 DeanM for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef SHARED_BINDINGS_AUDIOIO_MIXERVOICE_H_ +#define SHARED_BINDINGS_AUDIOIO_MIXERVOICE_H_ + +#include "common-hal/microcontroller/Pin.h" +#include "shared-module/audiocore/Mixer.h" +#include "shared-bindings/audiocore/RawSample.h" +#include "shared-module/audiocore/MixerVoice.h" +#include "shared-module/audiocore/Mixer.h" + +extern const mp_obj_type_t audioio_mixer_type; +extern const mp_obj_type_t audioio_mixervoice_type; + +void common_hal_audioio_mixervoice_deinit(audioio_mixervoice_obj_t* self); +void common_hal_audioio_mixervoice_set_parent(audioio_mixervoice_obj_t* self, audioio_mixer_obj_t *parent); +void common_hal_audioio_mixervoice_play(audioio_mixervoice_obj_t* self, mp_obj_t sample, bool loop); +void common_hal_audioio_mixervoice_stop(audioio_mixervoice_obj_t* self); +float common_hal_audioio_mixervoice_get_level(audioio_mixervoice_obj_t* self); +void common_hal_audioio_mixervoice_set_level(audioio_mixervoice_obj_t* self, float gain); + +bool common_hal_audioio_mixervoice_get_playing(audioio_mixervoice_obj_t* self); + +#endif /* SHARED_BINDINGS_AUDIOIO_MIXERVOICE_H_ */ diff --git a/shared-bindings/audiocore/__init__.c b/shared-bindings/audiocore/__init__.c index c64759d4f..9d82b1f99 100644 --- a/shared-bindings/audiocore/__init__.c +++ b/shared-bindings/audiocore/__init__.c @@ -50,6 +50,7 @@ //| :maxdepth: 3 //| //| Mixer +//| MixerVoice //| RawSample //| WaveFile //| diff --git a/shared-bindings/audioio/MixerVoice.c b/shared-bindings/audioio/MixerVoice.c deleted file mode 100644 index cba82feb2..000000000 --- a/shared-bindings/audioio/MixerVoice.c +++ /dev/null @@ -1,210 +0,0 @@ -/* - * MixerVoice.c - * - * Created on: Nov 15, 2018 - * Author: dean - */ -#include "shared-bindings/audioio/Mixer.h" -#include "shared-bindings/audioio/MixerVoice.h" - -#include - -#include "lib/utils/context_manager_helpers.h" -#include "py/binary.h" -#include "py/objproperty.h" -#include "py/runtime.h" -#include "shared-bindings/microcontroller/Pin.h" -#include "shared-bindings/audioio/RawSample.h" -#include "shared-bindings/util.h" -#include "supervisor/shared/translate.h" - -//| .. 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 -//| -// TODO: support mono or stereo voices -STATIC mp_obj_t audioio_mixervoice_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - audioio_mixervoice_obj_t *self = m_new_obj(audioio_mixervoice_obj_t); - self->base.type = &audioio_mixervoice_type; - self->sample = NULL; - self->level = ((1 << 15)-1); - return MP_OBJ_FROM_PTR(self); -} - -//| .. method:: deinit() -//| -//| Deinitialises the Voice and releases any hardware resources for reuse. -//| -STATIC mp_obj_t audioio_mixervoice_deinit(mp_obj_t self_in) { - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(audioio_mixervoice_deinit_obj, audioio_mixervoice_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_mixervoice_obj___exit__(size_t n_args, const mp_obj_t *args) { - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(audioio_mixervoice___exit___obj, 4, 4, audioio_mixervoice_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_mixervoice_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - 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_mixervoice_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); - //raise_error_if_deinited(common_hal_audioio_mixervoice_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_mixervoice_play(self, sample, args[ARG_loop].u_bool); - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_KW(audioio_mixervoice_play_obj, 1, audioio_mixervoice_obj_play); - -//| .. method:: stop() -//| -//| Stops playback of the sample on this voice. -//| -STATIC mp_obj_t audioio_mixervoice_obj_stop(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_voice }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_voice, MP_ARG_INT, {.u_int = 0} }, - }; - audioio_mixervoice_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_mixervoice_stop(self); - - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_KW(audioio_mixervoice_stop_obj, 1, audioio_mixervoice_obj_stop); - -//| .. method:: get_gain(voice, gain) -//| -//| Get the gain of a voice. -//| -//| Returns as a floating point number between 0 and 1 -//| -STATIC mp_obj_t audioio_mixervoice_obj_get_level(mp_obj_t self_in) { - - #if !MICROPY_PY_BUILTINS_FLOAT - #error "floating point not supported" - #endif - - return mp_obj_new_float(common_hal_audioio_mixervoice_get_level(self_in)); -} -MP_DEFINE_CONST_FUN_OBJ_1(audioio_mixervoice_get_level_obj, audioio_mixervoice_obj_get_level); - -//| .. 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_mixervoice_obj_set_level(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_level }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_level, MP_ARG_OBJ | MP_ARG_REQUIRED }, - }; - audioio_mixervoice_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); - 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 level = mp_obj_get_float(args[ARG_level].u_obj); - #else - #error "floating point not supported" - #endif - - if (level > 1 || level < 0) { - mp_raise_ValueError(translate("level must be between 0 and 1")); - } - - common_hal_audioio_mixervoice_set_level(self, level); - - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_KW(audioio_mixervoice_set_level_obj, 1, audioio_mixervoice_obj_set_level); - -const mp_obj_property_t audioio_mixervoice_level_obj = { - .base.type = &mp_type_property, - .proxy = {(mp_obj_t)&audioio_mixervoice_get_level_obj, - (mp_obj_t)&audioio_mixervoice_set_level_obj, - (mp_obj_t)&mp_const_none_obj}, -}; - -//| .. attribute:: playing -//| -//| True when any voice is being output. (read-only) -//| -STATIC mp_obj_t audioio_mixervoice_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_mixervoice_get_playing_obj, audioio_mixervoice_obj_get_playing); - -const mp_obj_property_t audioio_mixervoice_playing_obj = { - .base.type = &mp_type_property, - .proxy = {(mp_obj_t)&audioio_mixervoice_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_mixervoice_locals_dict_table[] = { - // Methods - { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&audioio_mixervoice_deinit_obj) }, - { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, - { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&audioio_mixervoice___exit___obj) }, - { MP_ROM_QSTR(MP_QSTR_play), MP_ROM_PTR(&audioio_mixervoice_play_obj) }, - { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&audioio_mixervoice_stop_obj) }, - - // Properties - { MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&audioio_mixervoice_playing_obj) }, - { MP_ROM_QSTR(MP_QSTR_level), MP_ROM_PTR(&audioio_mixervoice_level_obj) }, -}; -STATIC MP_DEFINE_CONST_DICT(audioio_mixervoice_locals_dict, audioio_mixervoice_locals_dict_table); - -const mp_obj_type_t audioio_mixervoice_type = { - { &mp_type_type }, - .name = MP_QSTR_MixerVoice, - .make_new = audioio_mixervoice_make_new, - .locals_dict = (mp_obj_dict_t*)&audioio_mixervoice_locals_dict, -}; diff --git a/shared-bindings/audioio/MixerVoice.h b/shared-bindings/audioio/MixerVoice.h deleted file mode 100644 index 641a0faf2..000000000 --- a/shared-bindings/audioio/MixerVoice.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * MixerVoice.h - * - * Created on: Nov 15, 2018 - * Author: dean - */ - -#ifndef SHARED_BINDINGS_AUDIOIO_MIXERVOICE_H_ -#define SHARED_BINDINGS_AUDIOIO_MIXERVOICE_H_ - -#include "common-hal/microcontroller/Pin.h" -#include "shared-module/audioio/Mixer.h" -#include "shared-bindings/audioio/RawSample.h" -#include "shared-module/audioio/MixerVoice.h" -#include "shared-module/audioio/Mixer.h" - -extern const mp_obj_type_t audioio_mixer_type; -extern const mp_obj_type_t audioio_mixervoice_type; - -void common_hal_audioio_mixervoice_deinit(audioio_mixervoice_obj_t* self); -void common_hal_audioio_mixervoice_set_parent(audioio_mixervoice_obj_t* self, audioio_mixer_obj_t *parent); -void common_hal_audioio_mixervoice_play(audioio_mixervoice_obj_t* self, mp_obj_t sample, bool loop); -void common_hal_audioio_mixervoice_stop(audioio_mixervoice_obj_t* self); -float common_hal_audioio_mixervoice_get_level(audioio_mixervoice_obj_t* self); -void common_hal_audioio_mixervoice_set_level(audioio_mixervoice_obj_t* self, float gain); - -bool common_hal_audioio_mixervoice_get_playing(audioio_mixervoice_obj_t* self); - -#endif /* SHARED_BINDINGS_AUDIOIO_MIXERVOICE_H_ */ diff --git a/shared-module/audiocore/Mixer.c b/shared-module/audiocore/Mixer.c index 8c083f419..40ed63dc4 100644 --- a/shared-module/audiocore/Mixer.c +++ b/shared-module/audiocore/Mixer.c @@ -25,6 +25,7 @@ */ #include "shared-bindings/audiocore/Mixer.h" +#include "shared-bindings/audiocore/MixerVoice.h" #include diff --git a/shared-module/audiocore/MixerVoice.c b/shared-module/audiocore/MixerVoice.c new file mode 100644 index 000000000..c16dfccb0 --- /dev/null +++ b/shared-module/audiocore/MixerVoice.c @@ -0,0 +1,82 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 DeanM for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "shared-bindings/audiocore/Mixer.h" + +#include + +#include "py/runtime.h" +#include "shared-module/audioio/__init__.h" +#include "shared-module/audiocore/RawSample.h" +#include "shared-module/audiocore/MixerVoice.h" + +void common_hal_audioio_mixervoice_set_parent(audioio_mixervoice_obj_t* self, audioio_mixer_obj_t *parent) { + self->parent = parent; +} + +float common_hal_audioio_mixervoice_get_level(audioio_mixervoice_obj_t* self) { + return self->level; +} + +void common_hal_audioio_mixervoice_set_level(audioio_mixervoice_obj_t* self, float level) { + self->level = level * ((1 << 15)-1); +} + +void common_hal_audioio_mixervoice_play(audioio_mixervoice_obj_t* self, mp_obj_t sample, bool loop) { + if (audiosample_sample_rate(sample) != self->parent->sample_rate) { + mp_raise_ValueError(translate("The sample's sample rate does not match the mixer's")); + } + if (audiosample_channel_count(sample) != self->parent->channel_count) { + mp_raise_ValueError(translate("The sample's channel count does not match the mixer's")); + } + if (audiosample_bits_per_sample(sample) != self->parent->bits_per_sample) { + mp_raise_ValueError(translate("The sample's bits_per_sample does not match the mixer's")); + } + bool single_buffer; + bool samples_signed; + uint32_t max_buffer_length; + uint8_t spacing; + audiosample_get_buffer_structure(sample, false, &single_buffer, &samples_signed, + &max_buffer_length, &spacing); + if (samples_signed != self->parent->samples_signed) { + mp_raise_ValueError(translate("The sample's signedness does not match the mixer's")); + } + self->sample = sample; + self->loop = loop; + + audiosample_reset_buffer(sample, false, 0); + audioio_get_buffer_result_t result = audiosample_get_buffer(sample, false, 0, (uint8_t**) &self->remaining_buffer, &self->buffer_length); + // Track length in terms of words. + self->buffer_length /= sizeof(uint32_t); + self->more_data = result == GET_BUFFER_MORE_DATA; +} + +bool common_hal_audioio_mixervoice_get_playing(audioio_mixervoice_obj_t* self) { + return self->sample != NULL; +} + +void common_hal_audioio_mixervoice_stop(audioio_mixervoice_obj_t* self) { + self->sample = NULL; +} diff --git a/shared-module/audiocore/MixerVoice.h b/shared-module/audiocore/MixerVoice.h new file mode 100644 index 000000000..6010313bf --- /dev/null +++ b/shared-module/audiocore/MixerVoice.h @@ -0,0 +1,46 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 DeanM for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef SHARED_MODULE_AUDIOIO_MIXERVOICE_H_ +#define SHARED_MODULE_AUDIOIO_MIXERVOICE_H_ + +#include "py/obj.h" + +#include "shared-module/audiocore/__init__.h" +#include "shared-module/audiocore/Mixer.h" + +typedef struct { + mp_obj_base_t base; + audioio_mixer_obj_t *parent; + mp_obj_t sample; + bool loop; + bool more_data; + uint32_t* remaining_buffer; + uint32_t buffer_length; + int16_t level; +} audioio_mixervoice_obj_t; + + +#endif /* SHARED_MODULE_AUDIOIO_MIXERVOICE_H_ */ diff --git a/shared-module/audioio/MixerVoice.c b/shared-module/audioio/MixerVoice.c deleted file mode 100644 index 3e8aaff89..000000000 --- a/shared-module/audioio/MixerVoice.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * MixerVoice.c - * - * Created on: Nov 15, 2018 - * Author: dean - */ - -#include "shared-bindings/audioio/Mixer.h" - -#include - -#include "py/runtime.h" -#include "shared-module/audioio/__init__.h" -#include "shared-module/audioio/RawSample.h" -#include "shared-module/audioio/MixerVoice.h" - -void common_hal_audioio_mixervoice_set_parent(audioio_mixervoice_obj_t* self, audioio_mixer_obj_t *parent) { - self->parent = parent; -} - -float common_hal_audioio_mixervoice_get_level(audioio_mixervoice_obj_t* self) { - return self->level; -} - -void common_hal_audioio_mixervoice_set_level(audioio_mixervoice_obj_t* self, float level) { - self->level = level * ((1 << 15)-1); -} - -void common_hal_audioio_mixervoice_play(audioio_mixervoice_obj_t* self, mp_obj_t sample, bool loop) { - if (audiosample_sample_rate(sample) != self->parent->sample_rate) { - mp_raise_ValueError(translate("The sample's sample rate does not match the mixer's")); - } - if (audiosample_channel_count(sample) != self->parent->channel_count) { - mp_raise_ValueError(translate("The sample's channel count does not match the mixer's")); - } - if (audiosample_bits_per_sample(sample) != self->parent->bits_per_sample) { - mp_raise_ValueError(translate("The sample's bits_per_sample does not match the mixer's")); - } - bool single_buffer; - bool samples_signed; - uint32_t max_buffer_length; - uint8_t spacing; - audiosample_get_buffer_structure(sample, false, &single_buffer, &samples_signed, - &max_buffer_length, &spacing); - if (samples_signed != self->parent->samples_signed) { - mp_raise_ValueError(translate("The sample's signedness does not match the mixer's")); - } - self->sample = sample; - self->loop = loop; - - audiosample_reset_buffer(sample, false, 0); - audioio_get_buffer_result_t result = audiosample_get_buffer(sample, false, 0, (uint8_t**) &self->remaining_buffer, &self->buffer_length); - // Track length in terms of words. - self->buffer_length /= sizeof(uint32_t); - self->more_data = result == GET_BUFFER_MORE_DATA; -} - -bool common_hal_audioio_mixervoice_get_playing(audioio_mixervoice_obj_t* self) { - return self->sample != NULL; -} - -void common_hal_audioio_mixervoice_stop(audioio_mixervoice_obj_t* self) { - self->sample = NULL; -} diff --git a/shared-module/audioio/MixerVoice.h b/shared-module/audioio/MixerVoice.h deleted file mode 100644 index 2811a741a..000000000 --- a/shared-module/audioio/MixerVoice.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * MixerVoice.h - * - * Created on: Nov 15, 2018 - * Author: dean - */ - -#ifndef SHARED_MODULE_AUDIOIO_MIXERVOICE_H_ -#define SHARED_MODULE_AUDIOIO_MIXERVOICE_H_ - -#include "py/obj.h" - -#include "shared-module/audioio/__init__.h" -#include "shared-module/audioio/Mixer.h" - -typedef struct { - mp_obj_base_t base; - audioio_mixer_obj_t *parent; - mp_obj_t sample; - bool loop; - bool more_data; - uint32_t* remaining_buffer; - uint32_t buffer_length; - int16_t level; -} audioio_mixervoice_obj_t; - - -#endif /* SHARED_MODULE_AUDIOIO_MIXERVOICE_H_ */ -- cgit v1.2.3 From 614962ad6f38285c44598d414a9f5919e0dd40e3 Mon Sep 17 00:00:00 2001 From: sommersoft Date: Tue, 13 Aug 2019 19:30:42 -0500 Subject: non-DSP 8bit & 16bit functions working. --- shared-module/audiocore/Mixer.c | 186 +++++++++++++++++++++++++++------------- 1 file changed, 125 insertions(+), 61 deletions(-) (limited to 'shared-module') diff --git a/shared-module/audiocore/Mixer.c b/shared-module/audiocore/Mixer.c index 40ed63dc4..8dd9c6522 100644 --- a/shared-module/audiocore/Mixer.c +++ b/shared-module/audiocore/Mixer.c @@ -123,13 +123,13 @@ uint32_t add8unsigned(uint32_t a, uint32_t b) { #else uint32_t result = 0; for (int8_t i = 0; i < 4; i++) { - int8_t ai = (a >> (sizeof(uint8_t) * 8 * i)) - 128; - int8_t bi = (b >> (sizeof(uint8_t) * 8 * i)) - 128; - int32_t intermediate = (int32_t) ai + bi; + uint8_t ai = (a >> (sizeof(uint8_t) * 8 * i)); + uint8_t bi = (b >> (sizeof(uint8_t) * 8 * i)); + int32_t intermediate = (int32_t) (ai + bi) / 2; if (intermediate > UCHAR_MAX) { intermediate = UCHAR_MAX; } - result |= ((uint8_t) intermediate + 128) << (sizeof(uint8_t) * 8 * i); + result |= ((uint32_t) intermediate & 0xff) << (sizeof(uint8_t) * 8 * i); } return result; #endif @@ -179,64 +179,129 @@ uint32_t add16unsigned(uint32_t a, uint32_t b) { //TODO: static inline uint32_t mult8unsigned(uint32_t val, int32_t mul) { - #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + // if mul == 0, no need in wasting cycles + if (mul == 0) { + return 0; + } + #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + int32_t hi, lo; + int32_t bits = 16; // saturate to 16 bits + int32_t shift = 0; // shift is done automatically + asm volatile("smulwb %0, %1, %2" : "=r" (lo) : "r" (mul), "r" (val)); + asm volatile("smulwt %0, %1, %2" : "=r" (hi) : "r" (mul), "r" (val)); + asm volatile("ssat %0, %1, %2, asr %3" : "=r" (lo) : "I" (bits), "r" (lo), "I" (shift)); + asm volatile("ssat %0, %1, %2, asr %3" : "=r" (hi) : "I" (bits), "r" (hi), "I" (shift)); + asm volatile("pkhbt %0, %1, %2, lsl #8" : "=r" (val) : "r" (lo), "r" (hi)); // pack return val; - val = __USUB8(val, 0x80808080); - #else - uint32_t result = 0; - for (int8_t i = 0; i < 4; i++) { - int8_t ai = (val >> (sizeof(uint8_t) * 8 * i)) - 128; - } - return result; - #endif + #else + uint32_t result = 0; + float mod_mul = (float) mul / (float) ((1<<15)-1); + for (int8_t i = 0; i < 4; i++) { + uint8_t ai = val >> (sizeof(uint8_t) * 8 * i); + int32_t intermediate = ai * mod_mul; + if (intermediate > SHRT_MAX) { + intermediate = SHRT_MAX; + } + result |= ((uint32_t) intermediate & 0xff) << (sizeof(uint8_t) * 8 * i); + } + + return result; + #endif } //TODO: static inline uint32_t mult8signed(uint32_t val, int32_t mul) { - #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) - return val; - #else - uint32_t result = 0; - for (int8_t i = 0; i < 4; i++) { - int8_t ai = val >> (sizeof(int8_t) * 8 * i); - } - return result; - #endif + // if mul == 0, no need in wasting cycles + if (mul == 0) { + return 0; + } + #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + int32_t hi, lo; + int32_t bits = 16; // saturate to 16 bits + int32_t shift = 0; // shift is done automatically + asm volatile("smulwb %0, %1, %2" : "=r" (lo) : "r" (mul), "r" (val)); + asm volatile("smulwt %0, %1, %2" : "=r" (hi) : "r" (mul), "r" (val)); + asm volatile("ssat %0, %1, %2, asr %3" : "=r" (lo) : "I" (bits), "r" (lo), "I" (shift)); + asm volatile("ssat %0, %1, %2, asr %3" : "=r" (hi) : "I" (bits), "r" (hi), "I" (shift)); + asm volatile("pkhbt %0, %1, %2, lsl #8" : "=r" (val) : "r" (lo), "r" (hi)); // pack + return val; + #else + uint32_t result = 0; + float mod_mul = (float)mul / (float)((1<<15)-1); + for (int8_t i = 0; i < 4; i++) { + int16_t ai = val >> (sizeof(int8_t) * 8 * i); + int32_t intermediate = ai * mod_mul; + if (intermediate > CHAR_MAX) { + intermediate = CHAR_MAX; + } else if (intermediate < CHAR_MIN) { + intermediate = CHAR_MIN; + } + result |= (((uint32_t) intermediate) & 0xff) << (sizeof(int16_t) * 8 * i); + } + return result; + #endif } //TODO: static inline uint32_t mult16unsigned(uint32_t val, int32_t mul) { - #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + // if mul == 0, no need in wasting cycles + if (mul == 0) { + return 0; + } + #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + int32_t hi, lo; + asm volatile("umaal %0, %1, %2, %3" : "=r" (lo), "=r" (hi) : "r" (val), "r" (mul)); // unsigned accumulation mult (res=64bit) + asm volatile("pkhbt %0, %1, %2, lsl #16" : "=r" (val) : "r" (lo), "r" (hi)); // pack return val; - val = __USUB16(val, 0x80008000); - #else - uint32_t result = 0; - for (int8_t i = 0; i < 2; i++) { - int16_t ai = (val >> (sizeof(uint16_t) * 8 * i)) - 0x8000; - } - return result; - #endif + #else + //mp_printf(&mp_plat_print, "mult16signed called:\n\tval: %u\t mul: %u\n", val, mul); + uint32_t result = 0; + float mod_mul = (float)mul / (float)((1<<15)-1); + for (int8_t i = 0; i < 2; i++) { + int16_t ai = (val >> (sizeof(uint16_t) * 8 * i)) - 0x8000; + int32_t intermediate = ai * mod_mul; + if (intermediate > SHRT_MAX) { + intermediate = SHRT_MAX; + } else if (intermediate < SHRT_MIN) { + intermediate = SHRT_MIN; + } + result |= (((uint32_t) intermediate) + 0x8000) << (sizeof(int16_t) * 8 * i); + } + //mp_printf(&mp_plat_print, "\t mod_mul: %f\t result: %u\n", (double)mod_mul, result); + return val; + #endif } static inline uint32_t mult16signed(uint32_t val, int32_t mul) { - #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) - int32_t hi, lo; - int32_t bits = 16; // saturate to 16 bits - int32_t shift = 0; // shift is done automatically - asm volatile("smulwb %0, %1, %2" : "=r" (lo) : "r" (mul), "r" (val)); - asm volatile("smulwt %0, %1, %2" : "=r" (hi) : "r" (mul), "r" (val)); - asm volatile("ssat %0, %1, %2, asr %3" : "=r" (lo) : "I" (bits), "r" (lo), "I" (shift)); - asm volatile("ssat %0, %1, %2, asr %3" : "=r" (hi) : "I" (bits), "r" (hi), "I" (shift)); - asm volatile("pkhbt %0, %1, %2, lsl #16" : "=r" (val) : "r" (lo), "r" (hi)); // pack + // if mul == 0, no need in wasting cycles + if (mul == 0) { + return 0; + } + #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + int32_t hi, lo; + int32_t bits = 16; // saturate to 16 bits + int32_t shift = 0; // shift is done automatically + asm volatile("smulwb %0, %1, %2" : "=r" (lo) : "r" (mul), "r" (val)); + asm volatile("smulwt %0, %1, %2" : "=r" (hi) : "r" (mul), "r" (val)); + asm volatile("ssat %0, %1, %2, asr %3" : "=r" (lo) : "I" (bits), "r" (lo), "I" (shift)); + asm volatile("ssat %0, %1, %2, asr %3" : "=r" (hi) : "I" (bits), "r" (hi), "I" (shift)); + asm volatile("pkhbt %0, %1, %2, lsl #16" : "=r" (val) : "r" (lo), "r" (hi)); // pack return val; - #else - uint32_t result = 0; - //TODO: - for (int8_t i = 0; i < 2; i++) { - int16_t ai = val >> (sizeof(int16_t) * 8 * i); - } - return result; - #endif + #else + uint32_t result = 0; + float mod_mul = (float)mul / (float)((1<<15)-1); + for (int8_t i = 0; i < 2; i++) { + int16_t ai = val >> (sizeof(int16_t) * 8 * i); + int32_t intermediate = ai * mod_mul; + if (intermediate > SHRT_MAX) { + intermediate = SHRT_MAX; + } else if (intermediate < SHRT_MIN) { + intermediate = SHRT_MIN; + } + result |= (((uint32_t) intermediate) & 0xffff) << (sizeof(int16_t) * 8 * i); + } + return result; + #endif } audioio_get_buffer_result_t audioio_mixer_get_buffer(audioio_mixer_obj_t* self, @@ -311,19 +376,18 @@ audioio_get_buffer_result_t audioio_mixer_get_buffer(audioio_mixer_obj_t* self, // apply the mixer level if (!self->samples_signed) { - if (self->bits_per_sample == 8) { - sample_value = mult8unsigned(sample_value, voice->level); - } else { - sample_value = mult16unsigned(sample_value, voice->level); - } - } - else{ - if (self->bits_per_sample == 8) { - sample_value = mult8signed(sample_value, voice->level); - } else { - sample_value = mult16signed(sample_value, voice->level); - } - } + if (self->bits_per_sample == 8) { + sample_value = mult8unsigned(sample_value, voice->level); + } else { + sample_value = mult16unsigned(sample_value, voice->level); + } + } else { + if (self->bits_per_sample == 8) { + sample_value = mult8signed(sample_value, voice->level); + } else { + sample_value = mult16signed(sample_value, voice->level); + } + } if (!voices_active) { word_buffer[i] = sample_value; -- cgit v1.2.3 From 17190ae63e8a5fb43c99ca7f1f91bf684bdbc16e Mon Sep 17 00:00:00 2001 From: sommersoft Date: Mon, 19 Aug 2019 21:20:52 -0500 Subject: abandon incomplete mixer asm math functions --- shared-module/audiocore/Mixer.c | 51 +++++++++++++++-------------------------- 1 file changed, 18 insertions(+), 33 deletions(-) (limited to 'shared-module') diff --git a/shared-module/audiocore/Mixer.c b/shared-module/audiocore/Mixer.c index 8dd9c6522..c372d68d0 100644 --- a/shared-module/audiocore/Mixer.c +++ b/shared-module/audiocore/Mixer.c @@ -4,6 +4,8 @@ * The MIT License (MIT) * * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries + * 2018 DeanM for Adafruit Industries + * 2019 Michael Schroeder * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -105,7 +107,7 @@ uint32_t add8signed(uint32_t a, uint32_t b) { if (intermediate > CHAR_MAX) { intermediate = CHAR_MAX; } else if (intermediate < CHAR_MIN) { - //intermediate = CHAR_MIN; + intermediate = CHAR_MIN; } result |= (((uint32_t) intermediate) & 0xff) << (sizeof(int8_t) * 8 * i); } @@ -114,13 +116,13 @@ uint32_t add8signed(uint32_t a, uint32_t b) { } uint32_t add8unsigned(uint32_t a, uint32_t b) { - #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + /*#if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) // Subtract out the DC offset, add and then shift back. a = __USUB8(a, 0x80808080); b = __USUB8(b, 0x80808080); uint32_t sum = __QADD8(a, b); return __UADD8(sum, 0x80808080); - #else + #else*/ uint32_t result = 0; for (int8_t i = 0; i < 4; i++) { uint8_t ai = (a >> (sizeof(uint8_t) * 8 * i)); @@ -132,13 +134,13 @@ uint32_t add8unsigned(uint32_t a, uint32_t b) { result |= ((uint32_t) intermediate & 0xff) << (sizeof(uint8_t) * 8 * i); } return result; - #endif + //#endif } uint32_t add16signed(uint32_t a, uint32_t b) { - #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + /*#if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) return __QADD16(a, b); - #else + #else*/ uint32_t result = 0; for (int8_t i = 0; i < 2; i++) { int16_t ai = a >> (sizeof(int16_t) * 8 * i); @@ -152,7 +154,7 @@ uint32_t add16signed(uint32_t a, uint32_t b) { result |= (((uint32_t) intermediate) & 0xffff) << (sizeof(int16_t) * 8 * i); } return result; - #endif + //#endif } uint32_t add16unsigned(uint32_t a, uint32_t b) { @@ -183,17 +185,9 @@ static inline uint32_t mult8unsigned(uint32_t val, int32_t mul) { if (mul == 0) { return 0; } - #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) - int32_t hi, lo; - int32_t bits = 16; // saturate to 16 bits - int32_t shift = 0; // shift is done automatically - asm volatile("smulwb %0, %1, %2" : "=r" (lo) : "r" (mul), "r" (val)); - asm volatile("smulwt %0, %1, %2" : "=r" (hi) : "r" (mul), "r" (val)); - asm volatile("ssat %0, %1, %2, asr %3" : "=r" (lo) : "I" (bits), "r" (lo), "I" (shift)); - asm volatile("ssat %0, %1, %2, asr %3" : "=r" (hi) : "I" (bits), "r" (hi), "I" (shift)); - asm volatile("pkhbt %0, %1, %2, lsl #8" : "=r" (val) : "r" (lo), "r" (hi)); // pack + /*#if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) return val; - #else + #else*/ uint32_t result = 0; float mod_mul = (float) mul / (float) ((1<<15)-1); for (int8_t i = 0; i < 4; i++) { @@ -206,7 +200,7 @@ static inline uint32_t mult8unsigned(uint32_t val, int32_t mul) { } return result; - #endif + //#endif } //TODO: @@ -215,17 +209,11 @@ static inline uint32_t mult8signed(uint32_t val, int32_t mul) { if (mul == 0) { return 0; } + /* #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) - int32_t hi, lo; - int32_t bits = 16; // saturate to 16 bits - int32_t shift = 0; // shift is done automatically - asm volatile("smulwb %0, %1, %2" : "=r" (lo) : "r" (mul), "r" (val)); - asm volatile("smulwt %0, %1, %2" : "=r" (hi) : "r" (mul), "r" (val)); - asm volatile("ssat %0, %1, %2, asr %3" : "=r" (lo) : "I" (bits), "r" (lo), "I" (shift)); - asm volatile("ssat %0, %1, %2, asr %3" : "=r" (hi) : "I" (bits), "r" (hi), "I" (shift)); - asm volatile("pkhbt %0, %1, %2, lsl #8" : "=r" (val) : "r" (lo), "r" (hi)); // pack return val; #else + */ uint32_t result = 0; float mod_mul = (float)mul / (float)((1<<15)-1); for (int8_t i = 0; i < 4; i++) { @@ -239,7 +227,7 @@ static inline uint32_t mult8signed(uint32_t val, int32_t mul) { result |= (((uint32_t) intermediate) & 0xff) << (sizeof(int16_t) * 8 * i); } return result; - #endif + //#endif } //TODO: @@ -248,13 +236,11 @@ static inline uint32_t mult16unsigned(uint32_t val, int32_t mul) { if (mul == 0) { return 0; } + /* #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) - int32_t hi, lo; - asm volatile("umaal %0, %1, %2, %3" : "=r" (lo), "=r" (hi) : "r" (val), "r" (mul)); // unsigned accumulation mult (res=64bit) - asm volatile("pkhbt %0, %1, %2, lsl #16" : "=r" (val) : "r" (lo), "r" (hi)); // pack return val; #else - //mp_printf(&mp_plat_print, "mult16signed called:\n\tval: %u\t mul: %u\n", val, mul); + */ uint32_t result = 0; float mod_mul = (float)mul / (float)((1<<15)-1); for (int8_t i = 0; i < 2; i++) { @@ -267,9 +253,8 @@ static inline uint32_t mult16unsigned(uint32_t val, int32_t mul) { } result |= (((uint32_t) intermediate) + 0x8000) << (sizeof(int16_t) * 8 * i); } - //mp_printf(&mp_plat_print, "\t mod_mul: %f\t result: %u\n", (double)mod_mul, result); return val; - #endif + //#endif } static inline uint32_t mult16signed(uint32_t val, int32_t mul) { -- cgit v1.2.3 From 2c55b40a53eb537c7cda8cfdfdeacb644d775fd9 Mon Sep 17 00:00:00 2001 From: sommersoft Date: Sat, 24 Aug 2019 07:56:12 -0500 Subject: use a MixerVoice constructor --- shared-bindings/audiocore/MixerVoice.c | 7 ++++--- shared-bindings/audiocore/MixerVoice.h | 1 + shared-module/audiocore/MixerVoice.c | 5 +++++ 3 files changed, 10 insertions(+), 3 deletions(-) (limited to 'shared-module') diff --git a/shared-bindings/audiocore/MixerVoice.c b/shared-bindings/audiocore/MixerVoice.c index 2711be00f..36b3c35fc 100644 --- a/shared-bindings/audiocore/MixerVoice.c +++ b/shared-bindings/audiocore/MixerVoice.c @@ -50,10 +50,11 @@ //| // TODO: support mono or stereo voices STATIC mp_obj_t audioio_mixervoice_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - audioio_mixervoice_obj_t *self = m_new_obj(audioio_mixervoice_obj_t); + audioio_mixervoice_obj_t *self = m_new_obj(audioio_mixervoice_obj_t); self->base.type = &audioio_mixervoice_type; - self->sample = NULL; - self->level = ((1 << 15)-1); + + common_hal_audioio_mixervoice_construct(self); + return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/audiocore/MixerVoice.h b/shared-bindings/audiocore/MixerVoice.h index b5e447058..5849ce252 100644 --- a/shared-bindings/audiocore/MixerVoice.h +++ b/shared-bindings/audiocore/MixerVoice.h @@ -35,6 +35,7 @@ extern const mp_obj_type_t audioio_mixer_type; extern const mp_obj_type_t audioio_mixervoice_type; +void common_hal_audioio_mixervoice_construct(audioio_mixervoice_obj_t *self); void common_hal_audioio_mixervoice_set_parent(audioio_mixervoice_obj_t* self, audioio_mixer_obj_t *parent); void common_hal_audioio_mixervoice_play(audioio_mixervoice_obj_t* self, mp_obj_t sample, bool loop); void common_hal_audioio_mixervoice_stop(audioio_mixervoice_obj_t* self); diff --git a/shared-module/audiocore/MixerVoice.c b/shared-module/audiocore/MixerVoice.c index c16dfccb0..a9543a40d 100644 --- a/shared-module/audiocore/MixerVoice.c +++ b/shared-module/audiocore/MixerVoice.c @@ -32,6 +32,11 @@ #include "shared-module/audiocore/RawSample.h" #include "shared-module/audiocore/MixerVoice.h" +void common_hal_audioio_mixervoice_construct(audioio_mixervoice_obj_t *self) { + self->sample = NULL; + self->level = ((1 << 15) - 1); +} + void common_hal_audioio_mixervoice_set_parent(audioio_mixervoice_obj_t* self, audioio_mixer_obj_t *parent) { self->parent = parent; } -- cgit v1.2.3 From 965f2bf799b31b5f1caeeb07639741522bb94b65 Mon Sep 17 00:00:00 2001 From: sommersoft Date: Sat, 24 Aug 2019 09:24:54 -0500 Subject: fix MixerVoice.get_level; now returns a float between 0-1. --- shared-module/audiocore/MixerVoice.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'shared-module') diff --git a/shared-module/audiocore/MixerVoice.c b/shared-module/audiocore/MixerVoice.c index a9543a40d..80c07511e 100644 --- a/shared-module/audiocore/MixerVoice.c +++ b/shared-module/audiocore/MixerVoice.c @@ -42,7 +42,7 @@ void common_hal_audioio_mixervoice_set_parent(audioio_mixervoice_obj_t* self, au } float common_hal_audioio_mixervoice_get_level(audioio_mixervoice_obj_t* self) { - return self->level; + return ((float) self->level / ((1 << 15) - 1)); } void common_hal_audioio_mixervoice_set_level(audioio_mixervoice_obj_t* self, float level) { -- cgit v1.2.3 From df5568d993ad10bb6ef68fc0f01f8561f3a6c78d Mon Sep 17 00:00:00 2001 From: sommersoft Date: Sat, 24 Aug 2019 23:36:18 -0500 Subject: move Mixer & MixerVoice from 'audiocore' to 'audiomixer' --- py/circuitpy_defns.mk | 8 +- py/circuitpy_mpconfig.h | 8 + py/circuitpy_mpconfig.mk | 5 + shared-bindings/audiocore/Mixer.c | 243 ------------------ shared-bindings/audiocore/Mixer.h | 51 ---- shared-bindings/audiocore/MixerVoice.c | 178 ------------- shared-bindings/audiocore/MixerVoice.h | 47 ---- shared-bindings/audiocore/__init__.c | 6 +- shared-bindings/audioio/__init__.c | 7 +- shared-bindings/audiomixer/Mixer.c | 244 ++++++++++++++++++ shared-bindings/audiomixer/Mixer.h | 51 ++++ shared-bindings/audiomixer/MixerVoice.c | 178 +++++++++++++ shared-bindings/audiomixer/MixerVoice.h | 47 ++++ shared-bindings/audiomixer/__init__.c | 63 +++++ shared-bindings/audiomixer/__init__.h | 34 +++ shared-module/audiocore/Mixer.c | 430 -------------------------------- shared-module/audiocore/Mixer.h | 69 ----- shared-module/audiocore/MixerVoice.c | 87 ------- shared-module/audiocore/MixerVoice.h | 46 ---- shared-module/audiocore/__init__.c | 35 +-- shared-module/audiomixer/Mixer.c | 430 ++++++++++++++++++++++++++++++++ shared-module/audiomixer/Mixer.h | 69 +++++ shared-module/audiomixer/MixerVoice.c | 87 +++++++ shared-module/audiomixer/MixerVoice.h | 46 ++++ shared-module/audiomixer/__init__.c | 0 shared-module/audiomixer/__init__.h | 32 +++ 26 files changed, 1325 insertions(+), 1176 deletions(-) delete mode 100644 shared-bindings/audiocore/Mixer.c delete mode 100644 shared-bindings/audiocore/Mixer.h delete mode 100644 shared-bindings/audiocore/MixerVoice.c delete mode 100644 shared-bindings/audiocore/MixerVoice.h create mode 100644 shared-bindings/audiomixer/Mixer.c create mode 100644 shared-bindings/audiomixer/Mixer.h create mode 100644 shared-bindings/audiomixer/MixerVoice.c create mode 100644 shared-bindings/audiomixer/MixerVoice.h create mode 100644 shared-bindings/audiomixer/__init__.c create mode 100644 shared-bindings/audiomixer/__init__.h delete mode 100644 shared-module/audiocore/Mixer.c delete mode 100644 shared-module/audiocore/Mixer.h delete mode 100644 shared-module/audiocore/MixerVoice.c delete mode 100644 shared-module/audiocore/MixerVoice.h create mode 100644 shared-module/audiomixer/Mixer.c create mode 100644 shared-module/audiomixer/Mixer.h create mode 100644 shared-module/audiomixer/MixerVoice.c create mode 100644 shared-module/audiomixer/MixerVoice.h create mode 100644 shared-module/audiomixer/__init__.c create mode 100644 shared-module/audiomixer/__init__.h (limited to 'shared-module') diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index 675fe1aec..7ed9457d0 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -114,6 +114,9 @@ endif ifeq ($(CIRCUITPY_AUDIOCORE),1) SRC_PATTERNS += audiocore/% endif +ifeq ($(CIRCUITPY_AUDIOMIXER),1) +SRC_PATTERNS += audiomixer/% +endif ifeq ($(CIRCUITPY_BITBANGIO),1) SRC_PATTERNS += bitbangio/% endif @@ -305,10 +308,11 @@ SRC_SHARED_MODULE_ALL = \ audiopwmio/__init__.c \ audioio/__init__.c \ audiocore/__init__.c \ - audiocore/Mixer.c \ - audiocore/MixerVoice.c \ audiocore/RawSample.c \ audiocore/WaveFile.c \ + audiomixer/__init__.c \ + audiomixer/Mixer.c \ + audiomixer/MixerVoice.c \ bitbangio/I2C.c \ bitbangio/OneWire.c \ bitbangio/SPI.c \ diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index 393b7600c..8e44ee6c8 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -244,6 +244,13 @@ extern const struct _mp_obj_module_t audioio_module; #define AUDIOIO_MODULE #endif +#if CIRCUITPY_AUDIOMIXER +#define AUDIOMIXER_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_audiomixer), (mp_obj_t)&audiomixer_module }, +extern const struct _mp_obj_module_t audiomixer_module; +#else +#define AUDIOMIXER_MODULE +#endif + #if CIRCUITPY_AUDIOPWMIO #define AUDIOPWMIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_audiopwmio), (mp_obj_t)&audiopwmio_module }, extern const struct _mp_obj_module_t audiopwmio_module; @@ -573,6 +580,7 @@ extern const struct _mp_obj_module_t ustack_module; AUDIOBUSIO_MODULE \ AUDIOCORE_MODULE \ AUDIOIO_MODULE \ + AUDIOMIXER_MODULE \ AUDIOPWMIO_MODULE \ BITBANGIO_MODULE \ BLEIO_MODULE \ diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index 949cd1d18..b217abdce 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -94,6 +94,11 @@ endif endif CFLAGS += -DCIRCUITPY_AUDIOCORE=$(CIRCUITPY_AUDIOCORE) +ifndef CIRCUITPY_AUDIOMIXER +CIRCUITPY_AUDIOMIXER = $(CIRCUITPY_AUDIOIO) +endif +CFLAGS += -DCIRCUITPY_AUDIOMIXER=$(CIRCUITPY_AUDIOMIXER) + ifndef CIRCUITPY_BITBANGIO CIRCUITPY_BITBANGIO = $(CIRCUITPY_FULL_BUILD) endif diff --git a/shared-bindings/audiocore/Mixer.c b/shared-bindings/audiocore/Mixer.c deleted file mode 100644 index 59ff5eea7..000000000 --- a/shared-bindings/audiocore/Mixer.c +++ /dev/null @@ -1,243 +0,0 @@ -/* - * This file is part of the Micro Python project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include "shared-bindings/audiocore/Mixer.h" -#include "shared-bindings/audiocore/MixerVoice.h" -#include "shared-module/audiocore/MixerVoice.h" - -#include - -#include "lib/utils/context_manager_helpers.h" -#include "py/binary.h" -#include "py/objproperty.h" -#include "py/runtime.h" -#include "shared-bindings/microcontroller/Pin.h" -#include "shared-bindings/audiocore/RawSample.h" -#include "shared-bindings/util.h" -#include "supervisor/shared/translate.h" - -//| .. currentmodule:: audiocore -//| -//| :class:`Mixer` -- Mixes one or more audio samples together -//| =========================================================== -//| -//| Mixer mixes multiple samples into one sample. -//| -//| .. class:: Mixer(voice_count=2, buffer_size=1024, channel_count=2, bits_per_sample=16, samples_signed=True, sample_rate=8000) -//| -//| Create a Mixer object that can mix multiple channels with the same sample rate. -//| Samples are accessed and controlled with the mixer's `audiocore.MixerVoice` objects. -//| -//| :param int voice_count: The maximum number of voices to mix -//| :param int buffer_size: The total size in bytes of the buffers to mix into -//| :param int channel_count: The maximum number of samples to mix at once -//| :param int bits_per_sample: The bits per sample of the samples being played -//| :param bool samples_signed: Samples are signed (True) or unsigned (False) -//| :param int sample_rate: The sample rate to be used for all samples -//| -//| Playing a wave file from flash:: -//| -//| import board -//| import audioio -//| import audiocore -//| import digitalio -//| -//| # Required for CircuitPlayground Express -//| speaker_enable = digitalio.DigitalInOut(board.SPEAKER_ENABLE) -//| speaker_enable.switch_to_output(value=True) -//| -//| music = audiocore.WaveFile(open("cplay-5.1-16bit-16khz.wav", "rb")) -//| drum = audiocore.WaveFile(open("drum.wav", "rb")) -//| mixer = audiocore.Mixer(voice_count=2, sample_rate=16000, channel_count=1, -//| bits_per_sample=16, samples_signed=True) -//| a = audioio.AudioOut(board.A0) -//| -//| print("playing") -//| # Have AudioOut play our Mixer source -//| a.play(mixer) -//| # Play the first sample voice -//| mixer.voice[0].play(music) -//| while mixer.playing: -//| # Play the second sample voice -//| mixer.voice[1].play(drum) -//| time.sleep(1) -//| print("stopped") -//| -STATIC mp_obj_t audioio_mixer_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_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, mp_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); - - for(int v=0; vvoice[v] = audioio_mixervoice_type.make_new(&audioio_mixervoice_type, 0, 0, NULL); - common_hal_audioio_mixervoice_set_parent(self->voice[v], self); - } - self->voice_tuple = mp_obj_new_tuple(self->voice_count, self->voice); - - return MP_OBJ_FROM_PTR(self); -} - -//| .. method:: deinit() -//| -//| Deinitialises the Mixer and releases any hardware resources for reuse. -//| -STATIC mp_obj_t audioio_mixer_deinit(mp_obj_t self_in) { - audioio_mixer_obj_t *self = MP_OBJ_TO_PTR(self_in); - common_hal_audioio_mixer_deinit(self); - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(audioio_mixer_deinit_obj, audioio_mixer_deinit); - -STATIC void check_for_deinit(audioio_mixer_obj_t *self) { - if (common_hal_audioio_mixer_deinited(self)) { - raise_deinited_error(); - } -} - -//| .. 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_obj___exit__(size_t n_args, const mp_obj_t *args) { - (void)n_args; - common_hal_audioio_mixer_deinit(args[0]); - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(audioio_mixer___exit___obj, 4, 4, audioio_mixer_obj___exit__); - -//| .. attribute:: playing -//| -//| True when any voice is being output. (read-only) -//| -STATIC mp_obj_t audioio_mixer_obj_get_playing(mp_obj_t self_in) { - audioio_mixer_obj_t *self = MP_OBJ_TO_PTR(self_in); - check_for_deinit(self); - return mp_obj_new_bool(common_hal_audioio_mixer_get_playing(self)); -} -MP_DEFINE_CONST_FUN_OBJ_1(audioio_mixer_get_playing_obj, audioio_mixer_obj_get_playing); - -const mp_obj_property_t audioio_mixer_playing_obj = { - .base.type = &mp_type_property, - .proxy = {(mp_obj_t)&audioio_mixer_get_playing_obj, - (mp_obj_t)&mp_const_none_obj, - (mp_obj_t)&mp_const_none_obj}, -}; - -//| .. attribute:: sample_rate -//| -//| 32 bit value that dictates how quickly samples are played in Hertz (cycles per second). -//| -STATIC mp_obj_t audioio_mixer_obj_get_sample_rate(mp_obj_t self_in) { - audioio_mixer_obj_t *self = MP_OBJ_TO_PTR(self_in); - check_for_deinit(self); - return MP_OBJ_NEW_SMALL_INT(common_hal_audioio_mixer_get_sample_rate(self)); -} -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, - (mp_obj_t)&mp_const_none_obj, - (mp_obj_t)&mp_const_none_obj}, -}; - -//| .. attribute:: voice -//| -//| A tuple of the mixer's `audioio.MixerVoice` object(s). -//| -//| .. code-block:: python -//| -//| >>> mixer.voice -//| (,) -STATIC mp_obj_t audioio_mixer_obj_get_voice(mp_obj_t self_in) { - audioio_mixer_obj_t *self = MP_OBJ_TO_PTR(self_in); - check_for_deinit(self); - return self->voice_tuple; -} -MP_DEFINE_CONST_FUN_OBJ_1(audioio_mixer_get_voice_obj, audioio_mixer_obj_get_voice); - -const mp_obj_property_t audioio_mixer_voice_obj = { - .base.type = &mp_type_property, - .proxy = {(mp_obj_t)&audioio_mixer_get_voice_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) }, - { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, - { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&audioio_mixer___exit___obj) }, - - // Properties - { MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&audioio_mixer_playing_obj) }, - { MP_ROM_QSTR(MP_QSTR_sample_rate), MP_ROM_PTR(&audioio_mixer_sample_rate_obj) }, - { MP_ROM_QSTR(MP_QSTR_voice), MP_ROM_PTR(&audioio_mixer_voice_obj) } -}; -STATIC MP_DEFINE_CONST_DICT(audioio_mixer_locals_dict, audioio_mixer_locals_dict_table); - -const mp_obj_type_t audioio_mixer_type = { - { &mp_type_type }, - .name = MP_QSTR_Mixer, - .make_new = audioio_mixer_make_new, - .locals_dict = (mp_obj_dict_t*)&audioio_mixer_locals_dict, -}; diff --git a/shared-bindings/audiocore/Mixer.h b/shared-bindings/audiocore/Mixer.h deleted file mode 100644 index 87d681ce6..000000000 --- a/shared-bindings/audiocore/Mixer.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * This file is part of the Micro Python project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOIO_MIXER_H -#define MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOIO_MIXER_H - -#include "common-hal/microcontroller/Pin.h" -#include "shared-module/audiocore/Mixer.h" -#include "shared-bindings/audiocore/RawSample.h" - -extern const mp_obj_type_t audioio_mixer_type; -extern const mp_obj_type_t audioio_mixervoice_type; - -void common_hal_audioio_mixer_construct(audioio_mixer_obj_t* self, - uint8_t voice_count, - uint32_t buffer_size, - uint8_t bits_per_sample, - bool samples_signed, - uint8_t channel_count, - uint32_t sample_rate); - -void common_hal_audioio_mixer_deinit(audioio_mixer_obj_t* self); -bool common_hal_audioio_mixer_deinited(audioio_mixer_obj_t* self); - -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); - -#endif // MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOIO_MIXER_H diff --git a/shared-bindings/audiocore/MixerVoice.c b/shared-bindings/audiocore/MixerVoice.c deleted file mode 100644 index d666c235c..000000000 --- a/shared-bindings/audiocore/MixerVoice.c +++ /dev/null @@ -1,178 +0,0 @@ -/* - * This file is part of the Micro Python project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2018 DeanM for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include "shared-bindings/audiocore/Mixer.h" -#include "shared-bindings/audiocore/MixerVoice.h" - -#include - -#include "lib/utils/context_manager_helpers.h" -#include "py/binary.h" -#include "py/objproperty.h" -#include "py/runtime.h" -#include "shared-bindings/microcontroller/Pin.h" -#include "shared-bindings/audiocore/RawSample.h" -#include "shared-bindings/util.h" -#include "supervisor/shared/translate.h" - -//| .. currentmodule:: audiocore -//| -//| :class:`MixerVoice` -- Voice objects used with Mixer -//| ===================================================== -//| -//| Used to access and control samples with `audiocore.Mixer`. -//| -//| .. class:: MixerVoice() -//| -//| MixerVoice instance object(s) created by `audiocore.Mixer`. -//| -// TODO: support mono or stereo voices -STATIC mp_obj_t audioio_mixervoice_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - audioio_mixervoice_obj_t *self = m_new_obj(audioio_mixervoice_obj_t); - self->base.type = &audioio_mixervoice_type; - - common_hal_audioio_mixervoice_construct(self); - - return MP_OBJ_FROM_PTR(self); -} - -//| .. 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 `audiocore.WaveFile`, `audiocore.Mixer` or `audiocore.RawSample`. -//| -//| The sample must match the `audiocore.Mixer`'s encoding settings given in the constructor. -//| -STATIC mp_obj_t audioio_mixervoice_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - 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_mixervoice_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); - 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_mixervoice_play(self, sample, args[ARG_loop].u_bool); - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_KW(audioio_mixervoice_play_obj, 1, audioio_mixervoice_obj_play); - -//| .. method:: stop() -//| -//| Stops playback of the sample on this voice. -//| -STATIC mp_obj_t audioio_mixervoice_obj_stop(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_voice }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_voice, MP_ARG_INT, {.u_int = 0} }, - }; - audioio_mixervoice_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); - 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_mixervoice_stop(self); - - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_KW(audioio_mixervoice_stop_obj, 1, audioio_mixervoice_obj_stop); - -//| .. attribute:: level() -//| -//| The volume level of a voice, as a floating point number between 0 and 1. -//| -STATIC mp_obj_t audioio_mixervoice_obj_get_level(mp_obj_t self_in) { - return mp_obj_new_float(common_hal_audioio_mixervoice_get_level(self_in)); -} -MP_DEFINE_CONST_FUN_OBJ_1(audioio_mixervoice_get_level_obj, audioio_mixervoice_obj_get_level); - -STATIC mp_obj_t audioio_mixervoice_obj_set_level(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_level }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_level, MP_ARG_OBJ | MP_ARG_REQUIRED }, - }; - audioio_mixervoice_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); - 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); - - float level = mp_obj_get_float(args[ARG_level].u_obj); - - if (level > 1 || level < 0) { - mp_raise_ValueError(translate("level must be between 0 and 1")); - } - - common_hal_audioio_mixervoice_set_level(self, level); - - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_KW(audioio_mixervoice_set_level_obj, 1, audioio_mixervoice_obj_set_level); - -const mp_obj_property_t audioio_mixervoice_level_obj = { - .base.type = &mp_type_property, - .proxy = {(mp_obj_t)&audioio_mixervoice_get_level_obj, - (mp_obj_t)&audioio_mixervoice_set_level_obj, - (mp_obj_t)&mp_const_none_obj}, -}; - -//| .. attribute:: playing -//| -//| True when any voice is being output. (read-only) -//| - -STATIC mp_obj_t audioio_mixervoice_obj_get_playing(mp_obj_t self_in) { - audioio_mixervoice_obj_t *self = MP_OBJ_TO_PTR(self_in); - - return mp_obj_new_bool(common_hal_audioio_mixervoice_get_playing(self)); - -} -MP_DEFINE_CONST_FUN_OBJ_1(audioio_mixervoice_get_playing_obj, audioio_mixervoice_obj_get_playing); - -const mp_obj_property_t audioio_mixervoice_playing_obj = { - .base.type = &mp_type_property, - .proxy = {(mp_obj_t)&audioio_mixervoice_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_mixervoice_locals_dict_table[] = { - // Methods - { MP_ROM_QSTR(MP_QSTR_play), MP_ROM_PTR(&audioio_mixervoice_play_obj) }, - { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&audioio_mixervoice_stop_obj) }, - - // Properties - { MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&audioio_mixervoice_playing_obj) }, - { MP_ROM_QSTR(MP_QSTR_level), MP_ROM_PTR(&audioio_mixervoice_level_obj) }, -}; -STATIC MP_DEFINE_CONST_DICT(audioio_mixervoice_locals_dict, audioio_mixervoice_locals_dict_table); - -const mp_obj_type_t audioio_mixervoice_type = { - { &mp_type_type }, - .name = MP_QSTR_MixerVoice, - .make_new = audioio_mixervoice_make_new, - .locals_dict = (mp_obj_dict_t*)&audioio_mixervoice_locals_dict, -}; diff --git a/shared-bindings/audiocore/MixerVoice.h b/shared-bindings/audiocore/MixerVoice.h deleted file mode 100644 index 5849ce252..000000000 --- a/shared-bindings/audiocore/MixerVoice.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * This file is part of the Micro Python project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2018 DeanM for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#ifndef SHARED_BINDINGS_AUDIOIO_MIXERVOICE_H_ -#define SHARED_BINDINGS_AUDIOIO_MIXERVOICE_H_ - -#include "common-hal/microcontroller/Pin.h" -#include "shared-module/audiocore/Mixer.h" -#include "shared-bindings/audiocore/RawSample.h" -#include "shared-module/audiocore/MixerVoice.h" -#include "shared-module/audiocore/Mixer.h" - -extern const mp_obj_type_t audioio_mixer_type; -extern const mp_obj_type_t audioio_mixervoice_type; - -void common_hal_audioio_mixervoice_construct(audioio_mixervoice_obj_t *self); -void common_hal_audioio_mixervoice_set_parent(audioio_mixervoice_obj_t* self, audioio_mixer_obj_t *parent); -void common_hal_audioio_mixervoice_play(audioio_mixervoice_obj_t* self, mp_obj_t sample, bool loop); -void common_hal_audioio_mixervoice_stop(audioio_mixervoice_obj_t* self); -float common_hal_audioio_mixervoice_get_level(audioio_mixervoice_obj_t* self); -void common_hal_audioio_mixervoice_set_level(audioio_mixervoice_obj_t* self, float gain); - -bool common_hal_audioio_mixervoice_get_playing(audioio_mixervoice_obj_t* self); - -#endif /* SHARED_BINDINGS_AUDIOIO_MIXERVOICE_H_ */ diff --git a/shared-bindings/audiocore/__init__.c b/shared-bindings/audiocore/__init__.c index 9d82b1f99..c9d5a1cab 100644 --- a/shared-bindings/audiocore/__init__.c +++ b/shared-bindings/audiocore/__init__.c @@ -31,9 +31,9 @@ #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/audiocore/__init__.h" -#include "shared-bindings/audiocore/Mixer.h" #include "shared-bindings/audiocore/RawSample.h" #include "shared-bindings/audiocore/WaveFile.h" +//#include "shared-bindings/audiomixer/Mixer.h" //| :mod:`audiocore` --- Support for audio samples and mixer //| ======================================================== @@ -49,15 +49,13 @@ //| .. toctree:: //| :maxdepth: 3 //| -//| Mixer -//| MixerVoice //| RawSample //| WaveFile //| STATIC const mp_rom_map_elem_t audiocore_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_audiocore) }, - { MP_ROM_QSTR(MP_QSTR_Mixer), MP_ROM_PTR(&audioio_mixer_type) }, + //{ MP_ROM_QSTR(MP_QSTR_Mixer), MP_ROM_PTR(&audioio_mixer_type) }, { MP_ROM_QSTR(MP_QSTR_RawSample), MP_ROM_PTR(&audioio_rawsample_type) }, { MP_ROM_QSTR(MP_QSTR_WaveFile), MP_ROM_PTR(&audioio_wavefile_type) }, }; diff --git a/shared-bindings/audioio/__init__.c b/shared-bindings/audioio/__init__.c index bd771dda2..0d307335f 100644 --- a/shared-bindings/audioio/__init__.c +++ b/shared-bindings/audioio/__init__.c @@ -34,10 +34,13 @@ #include "shared-bindings/audioio/AudioOut.h" #ifdef CIRCUITPY_AUDIOIO_COMPAT -#include "shared-bindings/audiocore/Mixer.h" +#include "shared-bindings/audiomixer/Mixer.h" #include "shared-bindings/audiocore/RawSample.h" #include "shared-bindings/audiocore/WaveFile.h" #endif +#ifdef CIRCUIPY_AUDIOMIXER +#include "shared-bindings/audiomixer/Mixer.h" +#endif //| :mod:`audioio` --- Support for audio input and output //| ====================================================== @@ -72,7 +75,7 @@ STATIC const mp_rom_map_elem_t audioio_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_audioio) }, { MP_ROM_QSTR(MP_QSTR_AudioOut), MP_ROM_PTR(&audioio_audioout_type) }, #ifdef CIRCUITPY_AUDIOIO_COMPAT - { MP_ROM_QSTR(MP_QSTR_Mixer), MP_ROM_PTR(&audioio_mixer_type) }, + //{ MP_ROM_QSTR(MP_QSTR_Mixer), MP_ROM_PTR(&audiomixer_mixer_type) }, { MP_ROM_QSTR(MP_QSTR_RawSample), MP_ROM_PTR(&audioio_rawsample_type) }, { MP_ROM_QSTR(MP_QSTR_WaveFile), MP_ROM_PTR(&audioio_wavefile_type) }, #endif diff --git a/shared-bindings/audiomixer/Mixer.c b/shared-bindings/audiomixer/Mixer.c new file mode 100644 index 000000000..3beb61118 --- /dev/null +++ b/shared-bindings/audiomixer/Mixer.c @@ -0,0 +1,244 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "shared-bindings/audiomixer/Mixer.h" +#include "shared-bindings/audiomixer/MixerVoice.h" +#include "shared-module/audiomixer/MixerVoice.h" + +#include + +#include "lib/utils/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/audiocore/RawSample.h" +#include "shared-bindings/util.h" +#include "supervisor/shared/translate.h" + +//| .. currentmodule:: audiomixer +//| +//| :class:`Mixer` -- Mixes one or more audio samples together +//| =========================================================== +//| +//| Mixer mixes multiple samples into one sample. +//| +//| .. class:: Mixer(voice_count=2, buffer_size=1024, channel_count=2, bits_per_sample=16, samples_signed=True, sample_rate=8000) +//| +//| Create a Mixer object that can mix multiple channels with the same sample rate. +//| Samples are accessed and controlled with the mixer's `audiocore.MixerVoice` objects. +//| +//| :param int voice_count: The maximum number of voices to mix +//| :param int buffer_size: The total size in bytes of the buffers to mix into +//| :param int channel_count: The maximum number of samples to mix at once +//| :param int bits_per_sample: The bits per sample of the samples being played +//| :param bool samples_signed: Samples are signed (True) or unsigned (False) +//| :param int sample_rate: The sample rate to be used for all samples +//| +//| Playing a wave file from flash:: +//| +//| import board +//| import audioio +//| import audiocore +//| import audiomixer +//| import digitalio +//| +//| # Required for CircuitPlayground Express +//| speaker_enable = digitalio.DigitalInOut(board.SPEAKER_ENABLE) +//| speaker_enable.switch_to_output(value=True) +//| +//| music = audiocore.WaveFile(open("cplay-5.1-16bit-16khz.wav", "rb")) +//| drum = audiocore.WaveFile(open("drum.wav", "rb")) +//| mixer = audiomixer.Mixer(voice_count=2, sample_rate=16000, channel_count=1, +//| bits_per_sample=16, samples_signed=True) +//| a = audioio.AudioOut(board.A0) +//| +//| print("playing") +//| # Have AudioOut play our Mixer source +//| a.play(mixer) +//| # Play the first sample voice +//| mixer.voice[0].play(music) +//| while mixer.playing: +//| # Play the second sample voice +//| mixer.voice[1].play(drum) +//| time.sleep(1) +//| print("stopped") +//| +STATIC mp_obj_t audiomixer_mixer_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_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")); + } + audiomixer_mixer_obj_t *self = m_new_obj_var(audiomixer_mixer_obj_t, mp_obj_t, voice_count); + self->base.type = &audiomixer_mixer_type; + common_hal_audiomixer_mixer_construct(self, voice_count, args[ARG_buffer_size].u_int, bits_per_sample, args[ARG_samples_signed].u_bool, channel_count, sample_rate); + + for(int v=0; vvoice[v] = audiomixer_mixervoice_type.make_new(&audiomixer_mixervoice_type, 0, 0, NULL); + common_hal_audiomixer_mixervoice_set_parent(self->voice[v], self); + } + self->voice_tuple = mp_obj_new_tuple(self->voice_count, self->voice); + + return MP_OBJ_FROM_PTR(self); +} + +//| .. method:: deinit() +//| +//| Deinitialises the Mixer and releases any hardware resources for reuse. +//| +STATIC mp_obj_t audiomixer_mixer_deinit(mp_obj_t self_in) { + audiomixer_mixer_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_audiomixer_mixer_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(audiomixer_mixer_deinit_obj, audiomixer_mixer_deinit); + +STATIC void check_for_deinit(audiomixer_mixer_obj_t *self) { + if (common_hal_audiomixer_mixer_deinited(self)) { + raise_deinited_error(); + } +} + +//| .. 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 audiomixer_mixer_obj___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_audiomixer_mixer_deinit(args[0]); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(audiomixer_mixer___exit___obj, 4, 4, audiomixer_mixer_obj___exit__); + +//| .. attribute:: playing +//| +//| True when any voice is being output. (read-only) +//| +STATIC mp_obj_t audiomixer_mixer_obj_get_playing(mp_obj_t self_in) { + audiomixer_mixer_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return mp_obj_new_bool(common_hal_audiomixer_mixer_get_playing(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audiomixer_mixer_get_playing_obj, audiomixer_mixer_obj_get_playing); + +const mp_obj_property_t audiomixer_mixer_playing_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&audiomixer_mixer_get_playing_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + +//| .. attribute:: sample_rate +//| +//| 32 bit value that dictates how quickly samples are played in Hertz (cycles per second). +//| +STATIC mp_obj_t audiomixer_mixer_obj_get_sample_rate(mp_obj_t self_in) { + audiomixer_mixer_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_audiomixer_mixer_get_sample_rate(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audiomixer_mixer_get_sample_rate_obj, audiomixer_mixer_obj_get_sample_rate); + +const mp_obj_property_t audiomixer_mixer_sample_rate_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&audiomixer_mixer_get_sample_rate_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + +//| .. attribute:: voice +//| +//| A tuple of the mixer's `audiomixer.MixerVoice` object(s). +//| +//| .. code-block:: python +//| +//| >>> mixer.voice +//| (,) +STATIC mp_obj_t audiomixer_mixer_obj_get_voice(mp_obj_t self_in) { + audiomixer_mixer_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return self->voice_tuple; +} +MP_DEFINE_CONST_FUN_OBJ_1(audiomixer_mixer_get_voice_obj, audiomixer_mixer_obj_get_voice); + +const mp_obj_property_t audiomixer_mixer_voice_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&audiomixer_mixer_get_voice_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + + +STATIC const mp_rom_map_elem_t audiomixer_mixer_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&audiomixer_mixer_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&audiomixer_mixer___exit___obj) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&audiomixer_mixer_playing_obj) }, + { MP_ROM_QSTR(MP_QSTR_sample_rate), MP_ROM_PTR(&audiomixer_mixer_sample_rate_obj) }, + { MP_ROM_QSTR(MP_QSTR_voice), MP_ROM_PTR(&audiomixer_mixer_voice_obj) } +}; +STATIC MP_DEFINE_CONST_DICT(audiomixer_mixer_locals_dict, audiomixer_mixer_locals_dict_table); + +const mp_obj_type_t audiomixer_mixer_type = { + { &mp_type_type }, + .name = MP_QSTR_Mixer, + .make_new = audiomixer_mixer_make_new, + .locals_dict = (mp_obj_dict_t*)&audiomixer_mixer_locals_dict, +}; diff --git a/shared-bindings/audiomixer/Mixer.h b/shared-bindings/audiomixer/Mixer.h new file mode 100644 index 000000000..9eabbf61d --- /dev/null +++ b/shared-bindings/audiomixer/Mixer.h @@ -0,0 +1,51 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOMIXER_MIXER_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOMIXER_MIXER_H + +#include "common-hal/microcontroller/Pin.h" +#include "shared-module/audiomixer/Mixer.h" +#include "shared-bindings/audiocore/RawSample.h" + +extern const mp_obj_type_t audiomixer_mixer_type; +extern const mp_obj_type_t audiomixer_mixervoice_type; + +void common_hal_audiomixer_mixer_construct(audiomixer_mixer_obj_t* self, + uint8_t voice_count, + uint32_t buffer_size, + uint8_t bits_per_sample, + bool samples_signed, + uint8_t channel_count, + uint32_t sample_rate); + +void common_hal_audiomixer_mixer_deinit(audiomixer_mixer_obj_t* self); +bool common_hal_audiomixer_mixer_deinited(audiomixer_mixer_obj_t* self); + +bool common_hal_audiomixer_mixer_get_playing(audiomixer_mixer_obj_t* self); +uint32_t common_hal_audiomixer_mixer_get_sample_rate(audiomixer_mixer_obj_t* self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOMIXER_MIXER_H diff --git a/shared-bindings/audiomixer/MixerVoice.c b/shared-bindings/audiomixer/MixerVoice.c new file mode 100644 index 000000000..5c082d931 --- /dev/null +++ b/shared-bindings/audiomixer/MixerVoice.c @@ -0,0 +1,178 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 DeanM for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "shared-bindings/audiomixer/Mixer.h" +#include "shared-bindings/audiomixer/MixerVoice.h" + +#include + +#include "lib/utils/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/audiocore/RawSample.h" +#include "shared-bindings/util.h" +#include "supervisor/shared/translate.h" + +//| .. currentmodule:: audiomixer +//| +//| :class:`MixerVoice` -- Voice objects used with Mixer +//| ===================================================== +//| +//| Used to access and control samples with `audiocore.Mixer`. +//| +//| .. class:: MixerVoice() +//| +//| MixerVoice instance object(s) created by `audiomixer.Mixer`. +//| +// TODO: support mono or stereo voices +STATIC mp_obj_t audiomixer_mixervoice_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + audiomixer_mixervoice_obj_t *self = m_new_obj(audiomixer_mixervoice_obj_t); + self->base.type = &audiomixer_mixervoice_type; + + common_hal_audiomixer_mixervoice_construct(self); + + return MP_OBJ_FROM_PTR(self); +} + +//| .. 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 `audiocore.WaveFile`, `audiocore.Mixer` or `audiocore.RawSample`. +//| +//| The sample must match the `audiocore.Mixer`'s encoding settings given in the constructor. +//| +STATIC mp_obj_t audiomixer_mixervoice_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + 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} }, + }; + audiomixer_mixervoice_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + 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_audiomixer_mixervoice_play(self, sample, args[ARG_loop].u_bool); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(audiomixer_mixervoice_play_obj, 1, audiomixer_mixervoice_obj_play); + +//| .. method:: stop() +//| +//| Stops playback of the sample on this voice. +//| +STATIC mp_obj_t audiomixer_mixervoice_obj_stop(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_voice }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_voice, MP_ARG_INT, {.u_int = 0} }, + }; + audiomixer_mixervoice_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + 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_audiomixer_mixervoice_stop(self); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(audiomixer_mixervoice_stop_obj, 1, audiomixer_mixervoice_obj_stop); + +//| .. attribute:: level() +//| +//| The volume level of a voice, as a floating point number between 0 and 1. +//| +STATIC mp_obj_t audiomixer_mixervoice_obj_get_level(mp_obj_t self_in) { + return mp_obj_new_float(common_hal_audiomixer_mixervoice_get_level(self_in)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audiomixer_mixervoice_get_level_obj, audiomixer_mixervoice_obj_get_level); + +STATIC mp_obj_t audiomixer_mixervoice_obj_set_level(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_level }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_level, MP_ARG_OBJ | MP_ARG_REQUIRED }, + }; + audiomixer_mixervoice_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + 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); + + float level = mp_obj_get_float(args[ARG_level].u_obj); + + if (level > 1 || level < 0) { + mp_raise_ValueError(translate("level must be between 0 and 1")); + } + + common_hal_audiomixer_mixervoice_set_level(self, level); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(audiomixer_mixervoice_set_level_obj, 1, audiomixer_mixervoice_obj_set_level); + +const mp_obj_property_t audiomixer_mixervoice_level_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&audiomixer_mixervoice_get_level_obj, + (mp_obj_t)&audiomixer_mixervoice_set_level_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + +//| .. attribute:: playing +//| +//| True when any voice is being output. (read-only) +//| + +STATIC mp_obj_t audiomixer_mixervoice_obj_get_playing(mp_obj_t self_in) { + audiomixer_mixervoice_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return mp_obj_new_bool(common_hal_audiomixer_mixervoice_get_playing(self)); + +} +MP_DEFINE_CONST_FUN_OBJ_1(audiomixer_mixervoice_get_playing_obj, audiomixer_mixervoice_obj_get_playing); + +const mp_obj_property_t audiomixer_mixervoice_playing_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&audiomixer_mixervoice_get_playing_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + +STATIC const mp_rom_map_elem_t audiomixer_mixervoice_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_play), MP_ROM_PTR(&audiomixer_mixervoice_play_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&audiomixer_mixervoice_stop_obj) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&audiomixer_mixervoice_playing_obj) }, + { MP_ROM_QSTR(MP_QSTR_level), MP_ROM_PTR(&audiomixer_mixervoice_level_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(audiomixer_mixervoice_locals_dict, audiomixer_mixervoice_locals_dict_table); + +const mp_obj_type_t audiomixer_mixervoice_type = { + { &mp_type_type }, + .name = MP_QSTR_MixerVoice, + .make_new = audiomixer_mixervoice_make_new, + .locals_dict = (mp_obj_dict_t*)&audiomixer_mixervoice_locals_dict, +}; diff --git a/shared-bindings/audiomixer/MixerVoice.h b/shared-bindings/audiomixer/MixerVoice.h new file mode 100644 index 000000000..83098bc9d --- /dev/null +++ b/shared-bindings/audiomixer/MixerVoice.h @@ -0,0 +1,47 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 DeanM for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef SHARED_BINDINGS_AUDIOMIXER_MIXERVOICE_H_ +#define SHARED_BINDINGS_AUDIOMIXER_MIXERVOICE_H_ + +#include "common-hal/microcontroller/Pin.h" +#include "shared-bindings/audiocore/RawSample.h" + +#include "shared-module/audiomixer/MixerVoice.h" +#include "shared-module/audiomixer/Mixer.h" + +extern const mp_obj_type_t audiomixer_mixer_type; +extern const mp_obj_type_t audiomixer_mixervoice_type; + +void common_hal_audiomixer_mixervoice_construct(audiomixer_mixervoice_obj_t *self); +void common_hal_audiomixer_mixervoice_set_parent(audiomixer_mixervoice_obj_t* self, audiomixer_mixer_obj_t *parent); +void common_hal_audiomixer_mixervoice_play(audiomixer_mixervoice_obj_t* self, mp_obj_t sample, bool loop); +void common_hal_audiomixer_mixervoice_stop(audiomixer_mixervoice_obj_t* self); +float common_hal_audiomixer_mixervoice_get_level(audiomixer_mixervoice_obj_t* self); +void common_hal_audiomixer_mixervoice_set_level(audiomixer_mixervoice_obj_t* self, float gain); + +bool common_hal_audiomixer_mixervoice_get_playing(audiomixer_mixervoice_obj_t* self); + +#endif /* SHARED_BINDINGS_AUDIOMIXER_MIXERVOICE_H_ */ diff --git a/shared-bindings/audiomixer/__init__.c b/shared-bindings/audiomixer/__init__.c new file mode 100644 index 000000000..a8dd4309f --- /dev/null +++ b/shared-bindings/audiomixer/__init__.c @@ -0,0 +1,63 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Michael Schroeder + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include + +#include "py/obj.h" +#include "py/runtime.h" + +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/audiomixer/Mixer.h" + +//| :mod:`audiomixer` --- Support for audio mixer +//| ======================================================== +//| +//| .. module:: audiomixer +//| :synopsis: Support for audio mixer +//| :platform: SAMD21 +//| +//| The `audiomixer` module contains core classes for mixing audio sources +//| +//| Libraries +//| +//| .. toctree:: +//| :maxdepth: 3 +//| +//| Mixer +//| MixerVoice +//| + +STATIC const mp_rom_map_elem_t audiomixer_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_audiomixer) }, + { MP_ROM_QSTR(MP_QSTR_Mixer), MP_ROM_PTR(&audiomixer_mixer_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(audiomixer_module_globals, audiomixer_module_globals_table); + +const mp_obj_module_t audiomixer_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&audiomixer_module_globals, +}; diff --git a/shared-bindings/audiomixer/__init__.h b/shared-bindings/audiomixer/__init__.h new file mode 100644 index 000000000..35a90441c --- /dev/null +++ b/shared-bindings/audiomixer/__init__.h @@ -0,0 +1,34 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Michael Schroeder + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOMIXER___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOMIXER___INIT___H + +#include "py/obj.h" + +// Nothing now. + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOMIXER___INIT___H diff --git a/shared-module/audiocore/Mixer.c b/shared-module/audiocore/Mixer.c deleted file mode 100644 index c372d68d0..000000000 --- a/shared-module/audiocore/Mixer.c +++ /dev/null @@ -1,430 +0,0 @@ -/* - * This file is part of the Micro Python project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries - * 2018 DeanM for Adafruit Industries - * 2019 Michael Schroeder - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "shared-bindings/audiocore/Mixer.h" -#include "shared-bindings/audiocore/MixerVoice.h" - -#include - -#include "py/runtime.h" -#include "shared-module/audiocore/__init__.h" -#include "shared-module/audiocore/RawSample.h" - -void common_hal_audioio_mixer_construct(audioio_mixer_obj_t* self, - uint8_t voice_count, - uint32_t buffer_size, - uint8_t bits_per_sample, - bool samples_signed, - uint8_t channel_count, - uint32_t sample_rate) { - self->len = buffer_size / 2 / sizeof(uint32_t) * sizeof(uint32_t); - - self->first_buffer = m_malloc(self->len, false); - if (self->first_buffer == NULL) { - common_hal_audioio_mixer_deinit(self); - mp_raise_msg(&mp_type_MemoryError, translate("Couldn't allocate first buffer")); - } - - self->second_buffer = m_malloc(self->len, false); - if (self->second_buffer == NULL) { - common_hal_audioio_mixer_deinit(self); - mp_raise_msg(&mp_type_MemoryError, translate("Couldn't allocate second buffer")); - } - - self->bits_per_sample = bits_per_sample; - self->samples_signed = samples_signed; - self->channel_count = channel_count; - self->sample_rate = sample_rate; - self->voice_count = voice_count; -} - -void common_hal_audioio_mixer_deinit(audioio_mixer_obj_t* self) { - self->first_buffer = NULL; - self->second_buffer = NULL; -} - -bool common_hal_audioio_mixer_deinited(audioio_mixer_obj_t* self) { - return self->first_buffer == NULL; -} - -uint32_t common_hal_audioio_mixer_get_sample_rate(audioio_mixer_obj_t* self) { - return self->sample_rate; -} - -bool common_hal_audioio_mixer_get_playing(audioio_mixer_obj_t* self) { - for (int32_t v = 0; v < self->voice_count; v++) { - if (common_hal_audioio_mixervoice_get_playing(MP_OBJ_TO_PTR(self->voice[v]))) { - return true; - } - } - return false; -} - -void audioio_mixer_reset_buffer(audioio_mixer_obj_t* self, - bool single_channel, - uint8_t channel) { -#if 0 - for (int32_t i = 0; i < self->voice_count; i++) { - self->voice[i].sample = NULL; - } -#endif -} - -uint32_t add8signed(uint32_t a, uint32_t b) { - #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) - return __QADD8(a, b); - #else - uint32_t result = 0; - for (int8_t i = 0; i < 4; i++) { - int8_t ai = a >> (sizeof(int8_t) * 8 * i); - int8_t bi = b >> (sizeof(int8_t) * 8 * i); - int32_t intermediate = (int32_t) ai + bi; - if (intermediate > CHAR_MAX) { - intermediate = CHAR_MAX; - } else if (intermediate < CHAR_MIN) { - intermediate = CHAR_MIN; - } - result |= (((uint32_t) intermediate) & 0xff) << (sizeof(int8_t) * 8 * i); - } - return result; - #endif -} - -uint32_t add8unsigned(uint32_t a, uint32_t b) { - /*#if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) - // Subtract out the DC offset, add and then shift back. - a = __USUB8(a, 0x80808080); - b = __USUB8(b, 0x80808080); - uint32_t sum = __QADD8(a, b); - return __UADD8(sum, 0x80808080); - #else*/ - uint32_t result = 0; - for (int8_t i = 0; i < 4; i++) { - uint8_t ai = (a >> (sizeof(uint8_t) * 8 * i)); - uint8_t bi = (b >> (sizeof(uint8_t) * 8 * i)); - int32_t intermediate = (int32_t) (ai + bi) / 2; - if (intermediate > UCHAR_MAX) { - intermediate = UCHAR_MAX; - } - result |= ((uint32_t) intermediate & 0xff) << (sizeof(uint8_t) * 8 * i); - } - return result; - //#endif -} - -uint32_t add16signed(uint32_t a, uint32_t b) { - /*#if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) - return __QADD16(a, b); - #else*/ - uint32_t result = 0; - for (int8_t i = 0; i < 2; i++) { - int16_t ai = a >> (sizeof(int16_t) * 8 * i); - int16_t bi = b >> (sizeof(int16_t) * 8 * i); - int32_t intermediate = (int32_t) ai + bi; - if (intermediate > SHRT_MAX) { - intermediate = SHRT_MAX; - } else if (intermediate < SHRT_MIN) { - intermediate = SHRT_MIN; - } - result |= (((uint32_t) intermediate) & 0xffff) << (sizeof(int16_t) * 8 * i); - } - return result; - //#endif -} - -uint32_t add16unsigned(uint32_t a, uint32_t b) { - #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) - // Subtract out the DC offset, add and then shift back. - a = __USUB16(a, 0x80008000); - b = __USUB16(b, 0x80008000); - uint32_t sum = __QADD16(a, b); - return __UADD16(sum, 0x80008000); - #else - uint32_t result = 0; - for (int8_t i = 0; i < 2; i++) { - int16_t ai = (a >> (sizeof(uint16_t) * 8 * i)) - 0x8000; - int16_t bi = (b >> (sizeof(uint16_t) * 8 * i)) - 0x8000; - int32_t intermediate = (int32_t) ai + bi; - if (intermediate > USHRT_MAX) { - intermediate = USHRT_MAX; - } - result |= ((uint16_t) intermediate + 0x8000) << (sizeof(int16_t) * 8 * i); - } - return result; - #endif -} - -//TODO: -static inline uint32_t mult8unsigned(uint32_t val, int32_t mul) { - // if mul == 0, no need in wasting cycles - if (mul == 0) { - return 0; - } - /*#if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) - return val; - #else*/ - uint32_t result = 0; - float mod_mul = (float) mul / (float) ((1<<15)-1); - for (int8_t i = 0; i < 4; i++) { - uint8_t ai = val >> (sizeof(uint8_t) * 8 * i); - int32_t intermediate = ai * mod_mul; - if (intermediate > SHRT_MAX) { - intermediate = SHRT_MAX; - } - result |= ((uint32_t) intermediate & 0xff) << (sizeof(uint8_t) * 8 * i); - } - - return result; - //#endif -} - -//TODO: -static inline uint32_t mult8signed(uint32_t val, int32_t mul) { - // if mul == 0, no need in wasting cycles - if (mul == 0) { - return 0; - } - /* - #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) - return val; - #else - */ - uint32_t result = 0; - float mod_mul = (float)mul / (float)((1<<15)-1); - for (int8_t i = 0; i < 4; i++) { - int16_t ai = val >> (sizeof(int8_t) * 8 * i); - int32_t intermediate = ai * mod_mul; - if (intermediate > CHAR_MAX) { - intermediate = CHAR_MAX; - } else if (intermediate < CHAR_MIN) { - intermediate = CHAR_MIN; - } - result |= (((uint32_t) intermediate) & 0xff) << (sizeof(int16_t) * 8 * i); - } - return result; - //#endif -} - -//TODO: -static inline uint32_t mult16unsigned(uint32_t val, int32_t mul) { - // if mul == 0, no need in wasting cycles - if (mul == 0) { - return 0; - } - /* - #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) - return val; - #else - */ - uint32_t result = 0; - float mod_mul = (float)mul / (float)((1<<15)-1); - for (int8_t i = 0; i < 2; i++) { - int16_t ai = (val >> (sizeof(uint16_t) * 8 * i)) - 0x8000; - int32_t intermediate = ai * mod_mul; - if (intermediate > SHRT_MAX) { - intermediate = SHRT_MAX; - } else if (intermediate < SHRT_MIN) { - intermediate = SHRT_MIN; - } - result |= (((uint32_t) intermediate) + 0x8000) << (sizeof(int16_t) * 8 * i); - } - return val; - //#endif -} - -static inline uint32_t mult16signed(uint32_t val, int32_t mul) { - // if mul == 0, no need in wasting cycles - if (mul == 0) { - return 0; - } - #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) - int32_t hi, lo; - int32_t bits = 16; // saturate to 16 bits - int32_t shift = 0; // shift is done automatically - asm volatile("smulwb %0, %1, %2" : "=r" (lo) : "r" (mul), "r" (val)); - asm volatile("smulwt %0, %1, %2" : "=r" (hi) : "r" (mul), "r" (val)); - asm volatile("ssat %0, %1, %2, asr %3" : "=r" (lo) : "I" (bits), "r" (lo), "I" (shift)); - asm volatile("ssat %0, %1, %2, asr %3" : "=r" (hi) : "I" (bits), "r" (hi), "I" (shift)); - asm volatile("pkhbt %0, %1, %2, lsl #16" : "=r" (val) : "r" (lo), "r" (hi)); // pack - return val; - #else - uint32_t result = 0; - float mod_mul = (float)mul / (float)((1<<15)-1); - for (int8_t i = 0; i < 2; i++) { - int16_t ai = val >> (sizeof(int16_t) * 8 * i); - int32_t intermediate = ai * mod_mul; - if (intermediate > SHRT_MAX) { - intermediate = SHRT_MAX; - } else if (intermediate < SHRT_MIN) { - intermediate = SHRT_MIN; - } - result |= (((uint32_t) intermediate) & 0xffff) << (sizeof(int16_t) * 8 * i); - } - return result; - #endif -} - -audioio_get_buffer_result_t audioio_mixer_get_buffer(audioio_mixer_obj_t* self, - bool single_channel, - uint8_t channel, - uint8_t** buffer, - uint32_t* buffer_length) { - if (!single_channel) { - channel = 0; - } - - uint32_t channel_read_count = self->left_read_count; - if (channel == 1) { - channel_read_count = self->right_read_count; - } - *buffer_length = self->len; - - bool need_more_data = self->read_count == channel_read_count; - if (need_more_data) { - uint32_t* word_buffer; - if (self->use_first_buffer) { - *buffer = (uint8_t*) self->first_buffer; - word_buffer = self->first_buffer; - } else { - *buffer = (uint8_t*) self->second_buffer; - word_buffer = self->second_buffer; - } - self->use_first_buffer = !self->use_first_buffer; - bool voices_active = false; - for (int32_t v = 0; v < self->voice_count; v++) { - audioio_mixervoice_obj_t* voice = MP_OBJ_TO_PTR(self->voice[v]); - - uint32_t j = 0; - bool voice_done = voice->sample == NULL; - for (uint32_t i = 0; i < self->len / sizeof(uint32_t); i++) { - if (!voice_done && j >= voice->buffer_length) { - if (!voice->more_data) { - if (voice->loop) { - audiosample_reset_buffer(voice->sample, false, 0); - } else { - voice->sample = NULL; - voice_done = true; - } - } - if (!voice_done) { - // Load another buffer - audioio_get_buffer_result_t result = audiosample_get_buffer(voice->sample, false, 0, (uint8_t**) &voice->remaining_buffer, &voice->buffer_length); - // Track length in terms of words. - voice->buffer_length /= sizeof(uint32_t); - voice->more_data = result == GET_BUFFER_MORE_DATA; - j = 0; - } - } - // First active voice gets copied over verbatim. - uint32_t sample_value; - if (voice_done) { - // Exit early if another voice already set all samples once. - if (voices_active) { - continue; - } - sample_value = 0; - if (!self->samples_signed) { - if (self->bits_per_sample == 8) { - sample_value = 0x7f7f7f7f; - } else { - sample_value = 0x7fff7fff; - } - } - } else { - sample_value = voice->remaining_buffer[j]; - } - - // apply the mixer level - if (!self->samples_signed) { - if (self->bits_per_sample == 8) { - sample_value = mult8unsigned(sample_value, voice->level); - } else { - sample_value = mult16unsigned(sample_value, voice->level); - } - } else { - if (self->bits_per_sample == 8) { - sample_value = mult8signed(sample_value, voice->level); - } else { - sample_value = mult16signed(sample_value, voice->level); - } - } - - if (!voices_active) { - word_buffer[i] = sample_value; - } else { - if (self->bits_per_sample == 8) { - if (self->samples_signed) { - word_buffer[i] = add8signed(word_buffer[i], sample_value); - } else { - word_buffer[i] = add8unsigned(word_buffer[i], sample_value); - } - } else { - if (self->samples_signed) { - word_buffer[i] = add16signed(word_buffer[i], sample_value); - } else { - word_buffer[i] = add16unsigned(word_buffer[i], sample_value); - } - } - } - j++; - } - voice->buffer_length -= j; - voice->remaining_buffer += j; - - voices_active = true; - } - - self->read_count += 1; - } else if (!self->use_first_buffer) { - *buffer = (uint8_t*) self->first_buffer; - } else { - *buffer = (uint8_t*) self->second_buffer; - } - - - if (channel == 0) { - self->left_read_count += 1; - } else if (channel == 1) { - self->right_read_count += 1; - *buffer = *buffer + self->bits_per_sample / 8; - } - return GET_BUFFER_MORE_DATA; -} - -void audioio_mixer_get_buffer_structure(audioio_mixer_obj_t* self, bool single_channel, - bool* single_buffer, bool* samples_signed, - uint32_t* max_buffer_length, uint8_t* spacing) { - *single_buffer = false; - *samples_signed = self->samples_signed; - *max_buffer_length = self->len; - if (single_channel) { - *spacing = self->channel_count; - } else { - *spacing = 1; - } -} diff --git a/shared-module/audiocore/Mixer.h b/shared-module/audiocore/Mixer.h deleted file mode 100644 index aaab60e84..000000000 --- a/shared-module/audiocore/Mixer.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2018 Scott Shawcroft - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef MICROPY_INCLUDED_SHARED_MODULE_AUDIOIO_MIXER_H -#define MICROPY_INCLUDED_SHARED_MODULE_AUDIOIO_MIXER_H - -#include "py/obj.h" -#include "py/objtuple.h" - -#include "shared-module/audiocore/__init__.h" - -typedef struct { - mp_obj_base_t base; - uint32_t* first_buffer; - uint32_t* second_buffer; - uint32_t len; // in words - uint8_t bits_per_sample; - bool use_first_buffer; - bool samples_signed; - uint8_t channel_count; - uint32_t sample_rate; - - uint32_t read_count; - uint32_t left_read_count; - uint32_t right_read_count; - - uint8_t voice_count; - mp_obj_tuple_t *voice_tuple; - mp_obj_t voice[]; -} audioio_mixer_obj_t; - - -// These are not available from Python because it may be called in an interrupt. -void audioio_mixer_reset_buffer(audioio_mixer_obj_t* self, - bool single_channel, - uint8_t channel); -audioio_get_buffer_result_t audioio_mixer_get_buffer(audioio_mixer_obj_t* self, - bool single_channel, - uint8_t channel, - uint8_t** buffer, - uint32_t* buffer_length); // length in bytes -void audioio_mixer_get_buffer_structure(audioio_mixer_obj_t* self, bool single_channel, - bool* single_buffer, bool* samples_signed, - uint32_t* max_buffer_length, uint8_t* spacing); - -#endif // MICROPY_INCLUDED_SHARED_MODULE_AUDIOIO_MIXER_H diff --git a/shared-module/audiocore/MixerVoice.c b/shared-module/audiocore/MixerVoice.c deleted file mode 100644 index 80c07511e..000000000 --- a/shared-module/audiocore/MixerVoice.c +++ /dev/null @@ -1,87 +0,0 @@ -/* - * This file is part of the Micro Python project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2018 DeanM for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include "shared-bindings/audiocore/Mixer.h" - -#include - -#include "py/runtime.h" -#include "shared-module/audioio/__init__.h" -#include "shared-module/audiocore/RawSample.h" -#include "shared-module/audiocore/MixerVoice.h" - -void common_hal_audioio_mixervoice_construct(audioio_mixervoice_obj_t *self) { - self->sample = NULL; - self->level = ((1 << 15) - 1); -} - -void common_hal_audioio_mixervoice_set_parent(audioio_mixervoice_obj_t* self, audioio_mixer_obj_t *parent) { - self->parent = parent; -} - -float common_hal_audioio_mixervoice_get_level(audioio_mixervoice_obj_t* self) { - return ((float) self->level / ((1 << 15) - 1)); -} - -void common_hal_audioio_mixervoice_set_level(audioio_mixervoice_obj_t* self, float level) { - self->level = level * ((1 << 15)-1); -} - -void common_hal_audioio_mixervoice_play(audioio_mixervoice_obj_t* self, mp_obj_t sample, bool loop) { - if (audiosample_sample_rate(sample) != self->parent->sample_rate) { - mp_raise_ValueError(translate("The sample's sample rate does not match the mixer's")); - } - if (audiosample_channel_count(sample) != self->parent->channel_count) { - mp_raise_ValueError(translate("The sample's channel count does not match the mixer's")); - } - if (audiosample_bits_per_sample(sample) != self->parent->bits_per_sample) { - mp_raise_ValueError(translate("The sample's bits_per_sample does not match the mixer's")); - } - bool single_buffer; - bool samples_signed; - uint32_t max_buffer_length; - uint8_t spacing; - audiosample_get_buffer_structure(sample, false, &single_buffer, &samples_signed, - &max_buffer_length, &spacing); - if (samples_signed != self->parent->samples_signed) { - mp_raise_ValueError(translate("The sample's signedness does not match the mixer's")); - } - self->sample = sample; - self->loop = loop; - - audiosample_reset_buffer(sample, false, 0); - audioio_get_buffer_result_t result = audiosample_get_buffer(sample, false, 0, (uint8_t**) &self->remaining_buffer, &self->buffer_length); - // Track length in terms of words. - self->buffer_length /= sizeof(uint32_t); - self->more_data = result == GET_BUFFER_MORE_DATA; -} - -bool common_hal_audioio_mixervoice_get_playing(audioio_mixervoice_obj_t* self) { - return self->sample != NULL; -} - -void common_hal_audioio_mixervoice_stop(audioio_mixervoice_obj_t* self) { - self->sample = NULL; -} diff --git a/shared-module/audiocore/MixerVoice.h b/shared-module/audiocore/MixerVoice.h deleted file mode 100644 index 6010313bf..000000000 --- a/shared-module/audiocore/MixerVoice.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * This file is part of the Micro Python project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2018 DeanM for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#ifndef SHARED_MODULE_AUDIOIO_MIXERVOICE_H_ -#define SHARED_MODULE_AUDIOIO_MIXERVOICE_H_ - -#include "py/obj.h" - -#include "shared-module/audiocore/__init__.h" -#include "shared-module/audiocore/Mixer.h" - -typedef struct { - mp_obj_base_t base; - audioio_mixer_obj_t *parent; - mp_obj_t sample; - bool loop; - bool more_data; - uint32_t* remaining_buffer; - uint32_t buffer_length; - int16_t level; -} audioio_mixervoice_obj_t; - - -#endif /* SHARED_MODULE_AUDIOIO_MIXERVOICE_H_ */ diff --git a/shared-module/audiocore/__init__.c b/shared-module/audiocore/__init__.c index 067cae5dd..8433a1770 100644 --- a/shared-module/audiocore/__init__.c +++ b/shared-module/audiocore/__init__.c @@ -27,13 +27,14 @@ #include "shared-module/audioio/__init__.h" #include "py/obj.h" -#include "shared-bindings/audiocore/Mixer.h" #include "shared-bindings/audiocore/RawSample.h" #include "shared-bindings/audiocore/WaveFile.h" -#include "shared-module/audiocore/Mixer.h" #include "shared-module/audiocore/RawSample.h" #include "shared-module/audiocore/WaveFile.h" +#include "shared-bindings/audiomixer/Mixer.h" +#include "shared-module/audiomixer/Mixer.h" + uint32_t audiosample_sample_rate(mp_obj_t sample_obj) { if (MP_OBJ_IS_TYPE(sample_obj, &audioio_rawsample_type)) { audioio_rawsample_obj_t* sample = MP_OBJ_TO_PTR(sample_obj); @@ -41,8 +42,8 @@ uint32_t audiosample_sample_rate(mp_obj_t sample_obj) { } else if (MP_OBJ_IS_TYPE(sample_obj, &audioio_wavefile_type)) { audioio_wavefile_obj_t* file = MP_OBJ_TO_PTR(sample_obj); return file->sample_rate; - } else if (MP_OBJ_IS_TYPE(sample_obj, &audioio_mixer_type)) { - audioio_mixer_obj_t* mixer = MP_OBJ_TO_PTR(sample_obj); + } else if (MP_OBJ_IS_TYPE(sample_obj, &audiomixer_mixer_type)) { + audiomixer_mixer_obj_t* mixer = MP_OBJ_TO_PTR(sample_obj); return mixer->sample_rate; } return 16000; @@ -55,8 +56,8 @@ uint8_t audiosample_bits_per_sample(mp_obj_t sample_obj) { } else if (MP_OBJ_IS_TYPE(sample_obj, &audioio_wavefile_type)) { audioio_wavefile_obj_t* file = MP_OBJ_TO_PTR(sample_obj); return file->bits_per_sample; - } else if (MP_OBJ_IS_TYPE(sample_obj, &audioio_mixer_type)) { - audioio_mixer_obj_t* mixer = MP_OBJ_TO_PTR(sample_obj); + } else if (MP_OBJ_IS_TYPE(sample_obj, &audiomixer_mixer_type)) { + audiomixer_mixer_obj_t* mixer = MP_OBJ_TO_PTR(sample_obj); return mixer->bits_per_sample; } return 8; @@ -69,8 +70,8 @@ uint8_t audiosample_channel_count(mp_obj_t sample_obj) { } else if (MP_OBJ_IS_TYPE(sample_obj, &audioio_wavefile_type)) { audioio_wavefile_obj_t* file = MP_OBJ_TO_PTR(sample_obj); return file->channel_count; - } else if (MP_OBJ_IS_TYPE(sample_obj, &audioio_mixer_type)) { - audioio_mixer_obj_t* mixer = MP_OBJ_TO_PTR(sample_obj); + } else if (MP_OBJ_IS_TYPE(sample_obj, &audiomixer_mixer_type)) { + audiomixer_mixer_obj_t* mixer = MP_OBJ_TO_PTR(sample_obj); return mixer->channel_count; } return 1; @@ -83,9 +84,9 @@ void audiosample_reset_buffer(mp_obj_t sample_obj, bool single_channel, uint8_t } else if (MP_OBJ_IS_TYPE(sample_obj, &audioio_wavefile_type)) { audioio_wavefile_obj_t* file = MP_OBJ_TO_PTR(sample_obj); audioio_wavefile_reset_buffer(file, single_channel, audio_channel); - } else if (MP_OBJ_IS_TYPE(sample_obj, &audioio_mixer_type)) { - audioio_mixer_obj_t* file = MP_OBJ_TO_PTR(sample_obj); - audioio_mixer_reset_buffer(file, single_channel, audio_channel); + } else if (MP_OBJ_IS_TYPE(sample_obj, &audiomixer_mixer_type)) { + audiomixer_mixer_obj_t* file = MP_OBJ_TO_PTR(sample_obj); + audiomixer_mixer_reset_buffer(file, single_channel, audio_channel); } } @@ -99,9 +100,9 @@ audioio_get_buffer_result_t audiosample_get_buffer(mp_obj_t sample_obj, } else if (MP_OBJ_IS_TYPE(sample_obj, &audioio_wavefile_type)) { audioio_wavefile_obj_t* file = MP_OBJ_TO_PTR(sample_obj); return audioio_wavefile_get_buffer(file, single_channel, channel, buffer, buffer_length); - } else if (MP_OBJ_IS_TYPE(sample_obj, &audioio_mixer_type)) { - audioio_mixer_obj_t* file = MP_OBJ_TO_PTR(sample_obj); - return audioio_mixer_get_buffer(file, single_channel, channel, buffer, buffer_length); + } else if (MP_OBJ_IS_TYPE(sample_obj, &audiomixer_mixer_type)) { + audiomixer_mixer_obj_t* file = MP_OBJ_TO_PTR(sample_obj); + return audiomixer_mixer_get_buffer(file, single_channel, channel, buffer, buffer_length); } return GET_BUFFER_DONE; } @@ -117,9 +118,9 @@ void audiosample_get_buffer_structure(mp_obj_t sample_obj, bool single_channel, audioio_wavefile_obj_t* file = MP_OBJ_TO_PTR(sample_obj); audioio_wavefile_get_buffer_structure(file, single_channel, single_buffer, samples_signed, max_buffer_length, spacing); - } else if (MP_OBJ_IS_TYPE(sample_obj, &audioio_mixer_type)) { - audioio_mixer_obj_t* file = MP_OBJ_TO_PTR(sample_obj); - audioio_mixer_get_buffer_structure(file, single_channel, single_buffer, samples_signed, + } else if (MP_OBJ_IS_TYPE(sample_obj, &audiomixer_mixer_type)) { + audiomixer_mixer_obj_t* file = MP_OBJ_TO_PTR(sample_obj); + audiomixer_mixer_get_buffer_structure(file, single_channel, single_buffer, samples_signed, max_buffer_length, spacing); } } diff --git a/shared-module/audiomixer/Mixer.c b/shared-module/audiomixer/Mixer.c new file mode 100644 index 000000000..0ae7c7b09 --- /dev/null +++ b/shared-module/audiomixer/Mixer.c @@ -0,0 +1,430 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries + * 2018 DeanM for Adafruit Industries + * 2019 Michael Schroeder + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "shared-bindings/audiomixer/Mixer.h" +#include "shared-bindings/audiomixer/MixerVoice.h" + +#include + +#include "py/runtime.h" +#include "shared-module/audiocore/__init__.h" +#include "shared-module/audiocore/RawSample.h" + +void common_hal_audiomixer_mixer_construct(audiomixer_mixer_obj_t* self, + uint8_t voice_count, + uint32_t buffer_size, + uint8_t bits_per_sample, + bool samples_signed, + uint8_t channel_count, + uint32_t sample_rate) { + self->len = buffer_size / 2 / sizeof(uint32_t) * sizeof(uint32_t); + + self->first_buffer = m_malloc(self->len, false); + if (self->first_buffer == NULL) { + common_hal_audiomixer_mixer_deinit(self); + mp_raise_msg(&mp_type_MemoryError, translate("Couldn't allocate first buffer")); + } + + self->second_buffer = m_malloc(self->len, false); + if (self->second_buffer == NULL) { + common_hal_audiomixer_mixer_deinit(self); + mp_raise_msg(&mp_type_MemoryError, translate("Couldn't allocate second buffer")); + } + + self->bits_per_sample = bits_per_sample; + self->samples_signed = samples_signed; + self->channel_count = channel_count; + self->sample_rate = sample_rate; + self->voice_count = voice_count; +} + +void common_hal_audiomixer_mixer_deinit(audiomixer_mixer_obj_t* self) { + self->first_buffer = NULL; + self->second_buffer = NULL; +} + +bool common_hal_audiomixer_mixer_deinited(audiomixer_mixer_obj_t* self) { + return self->first_buffer == NULL; +} + +uint32_t common_hal_audiomixer_mixer_get_sample_rate(audiomixer_mixer_obj_t* self) { + return self->sample_rate; +} + +bool common_hal_audiomixer_mixer_get_playing(audiomixer_mixer_obj_t* self) { + for (int32_t v = 0; v < self->voice_count; v++) { + if (common_hal_audiomixer_mixervoice_get_playing(MP_OBJ_TO_PTR(self->voice[v]))) { + return true; + } + } + return false; +} + +void audiomixer_mixer_reset_buffer(audiomixer_mixer_obj_t* self, + bool single_channel, + uint8_t channel) { +#if 0 + for (int32_t i = 0; i < self->voice_count; i++) { + self->voice[i].sample = NULL; + } +#endif +} + +uint32_t add8signed(uint32_t a, uint32_t b) { + #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + return __QADD8(a, b); + #else + uint32_t result = 0; + for (int8_t i = 0; i < 4; i++) { + int8_t ai = a >> (sizeof(int8_t) * 8 * i); + int8_t bi = b >> (sizeof(int8_t) * 8 * i); + int32_t intermediate = (int32_t) ai + bi; + if (intermediate > CHAR_MAX) { + intermediate = CHAR_MAX; + } else if (intermediate < CHAR_MIN) { + intermediate = CHAR_MIN; + } + result |= (((uint32_t) intermediate) & 0xff) << (sizeof(int8_t) * 8 * i); + } + return result; + #endif +} + +uint32_t add8unsigned(uint32_t a, uint32_t b) { + /*#if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + // Subtract out the DC offset, add and then shift back. + a = __USUB8(a, 0x80808080); + b = __USUB8(b, 0x80808080); + uint32_t sum = __QADD8(a, b); + return __UADD8(sum, 0x80808080); + #else*/ + uint32_t result = 0; + for (int8_t i = 0; i < 4; i++) { + uint8_t ai = (a >> (sizeof(uint8_t) * 8 * i)); + uint8_t bi = (b >> (sizeof(uint8_t) * 8 * i)); + int32_t intermediate = (int32_t) (ai + bi) / 2; + if (intermediate > UCHAR_MAX) { + intermediate = UCHAR_MAX; + } + result |= ((uint32_t) intermediate & 0xff) << (sizeof(uint8_t) * 8 * i); + } + return result; + //#endif +} + +uint32_t add16signed(uint32_t a, uint32_t b) { + /*#if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + return __QADD16(a, b); + #else*/ + uint32_t result = 0; + for (int8_t i = 0; i < 2; i++) { + int16_t ai = a >> (sizeof(int16_t) * 8 * i); + int16_t bi = b >> (sizeof(int16_t) * 8 * i); + int32_t intermediate = (int32_t) ai + bi; + if (intermediate > SHRT_MAX) { + intermediate = SHRT_MAX; + } else if (intermediate < SHRT_MIN) { + intermediate = SHRT_MIN; + } + result |= (((uint32_t) intermediate) & 0xffff) << (sizeof(int16_t) * 8 * i); + } + return result; + //#endif +} + +uint32_t add16unsigned(uint32_t a, uint32_t b) { + #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + // Subtract out the DC offset, add and then shift back. + a = __USUB16(a, 0x80008000); + b = __USUB16(b, 0x80008000); + uint32_t sum = __QADD16(a, b); + return __UADD16(sum, 0x80008000); + #else + uint32_t result = 0; + for (int8_t i = 0; i < 2; i++) { + int16_t ai = (a >> (sizeof(uint16_t) * 8 * i)) - 0x8000; + int16_t bi = (b >> (sizeof(uint16_t) * 8 * i)) - 0x8000; + int32_t intermediate = (int32_t) ai + bi; + if (intermediate > USHRT_MAX) { + intermediate = USHRT_MAX; + } + result |= ((uint16_t) intermediate + 0x8000) << (sizeof(int16_t) * 8 * i); + } + return result; + #endif +} + +//TODO: +static inline uint32_t mult8unsigned(uint32_t val, int32_t mul) { + // if mul == 0, no need in wasting cycles + if (mul == 0) { + return 0; + } + /*#if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + return val; + #else*/ + uint32_t result = 0; + float mod_mul = (float) mul / (float) ((1<<15)-1); + for (int8_t i = 0; i < 4; i++) { + uint8_t ai = val >> (sizeof(uint8_t) * 8 * i); + int32_t intermediate = ai * mod_mul; + if (intermediate > SHRT_MAX) { + intermediate = SHRT_MAX; + } + result |= ((uint32_t) intermediate & 0xff) << (sizeof(uint8_t) * 8 * i); + } + + return result; + //#endif +} + +//TODO: +static inline uint32_t mult8signed(uint32_t val, int32_t mul) { + // if mul == 0, no need in wasting cycles + if (mul == 0) { + return 0; + } + /* + #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + return val; + #else + */ + uint32_t result = 0; + float mod_mul = (float)mul / (float)((1<<15)-1); + for (int8_t i = 0; i < 4; i++) { + int16_t ai = val >> (sizeof(int8_t) * 8 * i); + int32_t intermediate = ai * mod_mul; + if (intermediate > CHAR_MAX) { + intermediate = CHAR_MAX; + } else if (intermediate < CHAR_MIN) { + intermediate = CHAR_MIN; + } + result |= (((uint32_t) intermediate) & 0xff) << (sizeof(int16_t) * 8 * i); + } + return result; + //#endif +} + +//TODO: +static inline uint32_t mult16unsigned(uint32_t val, int32_t mul) { + // if mul == 0, no need in wasting cycles + if (mul == 0) { + return 0; + } + /* + #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + return val; + #else + */ + uint32_t result = 0; + float mod_mul = (float)mul / (float)((1<<15)-1); + for (int8_t i = 0; i < 2; i++) { + int16_t ai = (val >> (sizeof(uint16_t) * 8 * i)) - 0x8000; + int32_t intermediate = ai * mod_mul; + if (intermediate > SHRT_MAX) { + intermediate = SHRT_MAX; + } else if (intermediate < SHRT_MIN) { + intermediate = SHRT_MIN; + } + result |= (((uint32_t) intermediate) + 0x8000) << (sizeof(int16_t) * 8 * i); + } + return val; + //#endif +} + +static inline uint32_t mult16signed(uint32_t val, int32_t mul) { + // if mul == 0, no need in wasting cycles + if (mul == 0) { + return 0; + } + #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + int32_t hi, lo; + int32_t bits = 16; // saturate to 16 bits + int32_t shift = 0; // shift is done automatically + asm volatile("smulwb %0, %1, %2" : "=r" (lo) : "r" (mul), "r" (val)); + asm volatile("smulwt %0, %1, %2" : "=r" (hi) : "r" (mul), "r" (val)); + asm volatile("ssat %0, %1, %2, asr %3" : "=r" (lo) : "I" (bits), "r" (lo), "I" (shift)); + asm volatile("ssat %0, %1, %2, asr %3" : "=r" (hi) : "I" (bits), "r" (hi), "I" (shift)); + asm volatile("pkhbt %0, %1, %2, lsl #16" : "=r" (val) : "r" (lo), "r" (hi)); // pack + return val; + #else + uint32_t result = 0; + float mod_mul = (float)mul / (float)((1<<15)-1); + for (int8_t i = 0; i < 2; i++) { + int16_t ai = val >> (sizeof(int16_t) * 8 * i); + int32_t intermediate = ai * mod_mul; + if (intermediate > SHRT_MAX) { + intermediate = SHRT_MAX; + } else if (intermediate < SHRT_MIN) { + intermediate = SHRT_MIN; + } + result |= (((uint32_t) intermediate) & 0xffff) << (sizeof(int16_t) * 8 * i); + } + return result; + #endif +} + +audioio_get_buffer_result_t audiomixer_mixer_get_buffer(audiomixer_mixer_obj_t* self, + bool single_channel, + uint8_t channel, + uint8_t** buffer, + uint32_t* buffer_length) { + if (!single_channel) { + channel = 0; + } + + uint32_t channel_read_count = self->left_read_count; + if (channel == 1) { + channel_read_count = self->right_read_count; + } + *buffer_length = self->len; + + bool need_more_data = self->read_count == channel_read_count; + if (need_more_data) { + uint32_t* word_buffer; + if (self->use_first_buffer) { + *buffer = (uint8_t*) self->first_buffer; + word_buffer = self->first_buffer; + } else { + *buffer = (uint8_t*) self->second_buffer; + word_buffer = self->second_buffer; + } + self->use_first_buffer = !self->use_first_buffer; + bool voices_active = false; + for (int32_t v = 0; v < self->voice_count; v++) { + audiomixer_mixervoice_obj_t* voice = MP_OBJ_TO_PTR(self->voice[v]); + + uint32_t j = 0; + bool voice_done = voice->sample == NULL; + for (uint32_t i = 0; i < self->len / sizeof(uint32_t); i++) { + if (!voice_done && j >= voice->buffer_length) { + if (!voice->more_data) { + if (voice->loop) { + audiosample_reset_buffer(voice->sample, false, 0); + } else { + voice->sample = NULL; + voice_done = true; + } + } + if (!voice_done) { + // Load another buffer + audioio_get_buffer_result_t result = audiosample_get_buffer(voice->sample, false, 0, (uint8_t**) &voice->remaining_buffer, &voice->buffer_length); + // Track length in terms of words. + voice->buffer_length /= sizeof(uint32_t); + voice->more_data = result == GET_BUFFER_MORE_DATA; + j = 0; + } + } + // First active voice gets copied over verbatim. + uint32_t sample_value; + if (voice_done) { + // Exit early if another voice already set all samples once. + if (voices_active) { + continue; + } + sample_value = 0; + if (!self->samples_signed) { + if (self->bits_per_sample == 8) { + sample_value = 0x7f7f7f7f; + } else { + sample_value = 0x7fff7fff; + } + } + } else { + sample_value = voice->remaining_buffer[j]; + } + + // apply the mixer level + if (!self->samples_signed) { + if (self->bits_per_sample == 8) { + sample_value = mult8unsigned(sample_value, voice->level); + } else { + sample_value = mult16unsigned(sample_value, voice->level); + } + } else { + if (self->bits_per_sample == 8) { + sample_value = mult8signed(sample_value, voice->level); + } else { + sample_value = mult16signed(sample_value, voice->level); + } + } + + if (!voices_active) { + word_buffer[i] = sample_value; + } else { + if (self->bits_per_sample == 8) { + if (self->samples_signed) { + word_buffer[i] = add8signed(word_buffer[i], sample_value); + } else { + word_buffer[i] = add8unsigned(word_buffer[i], sample_value); + } + } else { + if (self->samples_signed) { + word_buffer[i] = add16signed(word_buffer[i], sample_value); + } else { + word_buffer[i] = add16unsigned(word_buffer[i], sample_value); + } + } + } + j++; + } + voice->buffer_length -= j; + voice->remaining_buffer += j; + + voices_active = true; + } + + self->read_count += 1; + } else if (!self->use_first_buffer) { + *buffer = (uint8_t*) self->first_buffer; + } else { + *buffer = (uint8_t*) self->second_buffer; + } + + + if (channel == 0) { + self->left_read_count += 1; + } else if (channel == 1) { + self->right_read_count += 1; + *buffer = *buffer + self->bits_per_sample / 8; + } + return GET_BUFFER_MORE_DATA; +} + +void audiomixer_mixer_get_buffer_structure(audiomixer_mixer_obj_t* self, bool single_channel, + bool* single_buffer, bool* samples_signed, + uint32_t* max_buffer_length, uint8_t* spacing) { + *single_buffer = false; + *samples_signed = self->samples_signed; + *max_buffer_length = self->len; + if (single_channel) { + *spacing = self->channel_count; + } else { + *spacing = 1; + } +} diff --git a/shared-module/audiomixer/Mixer.h b/shared-module/audiomixer/Mixer.h new file mode 100644 index 000000000..ab4780efc --- /dev/null +++ b/shared-module/audiomixer/Mixer.h @@ -0,0 +1,69 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_SHARED_MODULE_AUDIOMIXER_MIXER_H +#define MICROPY_INCLUDED_SHARED_MODULE_AUDIOMIXER_MIXER_H + +#include "py/obj.h" +#include "py/objtuple.h" + +#include "shared-module/audiocore/__init__.h" + +typedef struct { + mp_obj_base_t base; + uint32_t* first_buffer; + uint32_t* second_buffer; + uint32_t len; // in words + uint8_t bits_per_sample; + bool use_first_buffer; + bool samples_signed; + uint8_t channel_count; + uint32_t sample_rate; + + uint32_t read_count; + uint32_t left_read_count; + uint32_t right_read_count; + + uint8_t voice_count; + mp_obj_tuple_t *voice_tuple; + mp_obj_t voice[]; +} audiomixer_mixer_obj_t; + + +// These are not available from Python because it may be called in an interrupt. +void audiomixer_mixer_reset_buffer(audiomixer_mixer_obj_t* self, + bool single_channel, + uint8_t channel); +audioio_get_buffer_result_t audiomixer_mixer_get_buffer(audiomixer_mixer_obj_t* self, + bool single_channel, + uint8_t channel, + uint8_t** buffer, + uint32_t* buffer_length); // length in bytes +void audiomixer_mixer_get_buffer_structure(audiomixer_mixer_obj_t* self, bool single_channel, + bool* single_buffer, bool* samples_signed, + uint32_t* max_buffer_length, uint8_t* spacing); + +#endif // MICROPY_INCLUDED_SHARED_MODULE_AUDIOMIXER_MIXER_H diff --git a/shared-module/audiomixer/MixerVoice.c b/shared-module/audiomixer/MixerVoice.c new file mode 100644 index 000000000..ff05dc93e --- /dev/null +++ b/shared-module/audiomixer/MixerVoice.c @@ -0,0 +1,87 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 DeanM for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "shared-bindings/audiomixer/Mixer.h" +#include "shared-module/audiomixer/MixerVoice.h" + +#include + +#include "py/runtime.h" +#include "shared-module/audiomixer/__init__.h" +#include "shared-module/audiocore/RawSample.h" + +void common_hal_audiomixer_mixervoice_construct(audiomixer_mixervoice_obj_t *self) { + self->sample = NULL; + self->level = ((1 << 15) - 1); +} + +void common_hal_audiomixer_mixervoice_set_parent(audiomixer_mixervoice_obj_t* self, audiomixer_mixer_obj_t *parent) { + self->parent = parent; +} + +float common_hal_audiomixer_mixervoice_get_level(audiomixer_mixervoice_obj_t* self) { + return ((float) self->level / ((1 << 15) - 1)); +} + +void common_hal_audiomixer_mixervoice_set_level(audiomixer_mixervoice_obj_t* self, float level) { + self->level = level * ((1 << 15)-1); +} + +void common_hal_audiomixer_mixervoice_play(audiomixer_mixervoice_obj_t* self, mp_obj_t sample, bool loop) { + if (audiosample_sample_rate(sample) != self->parent->sample_rate) { + mp_raise_ValueError(translate("The sample's sample rate does not match the mixer's")); + } + if (audiosample_channel_count(sample) != self->parent->channel_count) { + mp_raise_ValueError(translate("The sample's channel count does not match the mixer's")); + } + if (audiosample_bits_per_sample(sample) != self->parent->bits_per_sample) { + mp_raise_ValueError(translate("The sample's bits_per_sample does not match the mixer's")); + } + bool single_buffer; + bool samples_signed; + uint32_t max_buffer_length; + uint8_t spacing; + audiosample_get_buffer_structure(sample, false, &single_buffer, &samples_signed, + &max_buffer_length, &spacing); + if (samples_signed != self->parent->samples_signed) { + mp_raise_ValueError(translate("The sample's signedness does not match the mixer's")); + } + self->sample = sample; + self->loop = loop; + + audiosample_reset_buffer(sample, false, 0); + audioio_get_buffer_result_t result = audiosample_get_buffer(sample, false, 0, (uint8_t**) &self->remaining_buffer, &self->buffer_length); + // Track length in terms of words. + self->buffer_length /= sizeof(uint32_t); + self->more_data = result == GET_BUFFER_MORE_DATA; +} + +bool common_hal_audiomixer_mixervoice_get_playing(audiomixer_mixervoice_obj_t* self) { + return self->sample != NULL; +} + +void common_hal_audiomixer_mixervoice_stop(audiomixer_mixervoice_obj_t* self) { + self->sample = NULL; +} diff --git a/shared-module/audiomixer/MixerVoice.h b/shared-module/audiomixer/MixerVoice.h new file mode 100644 index 000000000..efac19156 --- /dev/null +++ b/shared-module/audiomixer/MixerVoice.h @@ -0,0 +1,46 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 DeanM for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef SHARED_MODULE_AUDIOMIXER_MIXERVOICE_H_ +#define SHARED_MODULE_AUDIOMIXER_MIXERVOICE_H_ + +#include "py/obj.h" + +#include "shared-module/audiomixer/__init__.h" +#include "shared-module/audiomixer/Mixer.h" + +typedef struct { + mp_obj_base_t base; + audiomixer_mixer_obj_t *parent; + mp_obj_t sample; + bool loop; + bool more_data; + uint32_t* remaining_buffer; + uint32_t buffer_length; + int16_t level; +} audiomixer_mixervoice_obj_t; + + +#endif /* SHARED_MODULE_AUDIOMIXER_MIXERVOICE_H_ */ diff --git a/shared-module/audiomixer/__init__.c b/shared-module/audiomixer/__init__.c new file mode 100644 index 000000000..e69de29bb diff --git a/shared-module/audiomixer/__init__.h b/shared-module/audiomixer/__init__.h new file mode 100644 index 000000000..35b731558 --- /dev/null +++ b/shared-module/audiomixer/__init__.h @@ -0,0 +1,32 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Dan Halbert for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_SHARED_MODULE_AUDIOMIXER__INIT__H +#define MICROPY_INCLUDED_SHARED_MODULE_AUDIOMIXER__INIT__H + +#include "shared-module/audiocore/__init__.h" + +#endif // MICROPY_INCLUDED_SHARED_MODULE_AUDIOMIXER__INIT__H -- cgit v1.2.3 From 7f64af38cbd74b417b28e28e706dceb00e42b01b Mon Sep 17 00:00:00 2001 From: sommersoft Date: Sun, 25 Aug 2019 09:33:23 -0500 Subject: fix 'audiomixer_mixer_reset_buffer' --- shared-module/audiomixer/Mixer.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'shared-module') diff --git a/shared-module/audiomixer/Mixer.c b/shared-module/audiomixer/Mixer.c index 0ae7c7b09..da0f7dedb 100644 --- a/shared-module/audiomixer/Mixer.c +++ b/shared-module/audiomixer/Mixer.c @@ -77,7 +77,7 @@ uint32_t common_hal_audiomixer_mixer_get_sample_rate(audiomixer_mixer_obj_t* sel } bool common_hal_audiomixer_mixer_get_playing(audiomixer_mixer_obj_t* self) { - for (int32_t v = 0; v < self->voice_count; v++) { + for (uint8_t v = 0; v < self->voice_count; v++) { if (common_hal_audiomixer_mixervoice_get_playing(MP_OBJ_TO_PTR(self->voice[v]))) { return true; } @@ -88,11 +88,9 @@ bool common_hal_audiomixer_mixer_get_playing(audiomixer_mixer_obj_t* self) { void audiomixer_mixer_reset_buffer(audiomixer_mixer_obj_t* self, bool single_channel, uint8_t channel) { -#if 0 - for (int32_t i = 0; i < self->voice_count; i++) { - self->voice[i].sample = NULL; + for (uint8_t i = 0; i < self->voice_count; i++) { + common_hal_audiomixer_mixervoice_stop(self->voice[i]); } -#endif } uint32_t add8signed(uint32_t a, uint32_t b) { -- cgit v1.2.3 From 696117b048bd39d9480cfa5a2ee0871942eba816 Mon Sep 17 00:00:00 2001 From: sommersoft Date: Sun, 25 Aug 2019 14:53:34 -0500 Subject: disable audiomixer on boards it doesn't fit on --- ports/atmel-samd/boards/feather_m0_express/mpconfigboard.mk | 2 ++ .../atmel-samd/boards/feather_m0_supersized/mpconfigboard.mk | 2 ++ ports/atmel-samd/boards/metro_m0_express/mpconfigboard.mk | 2 ++ ports/atmel-samd/boards/snekboard/mpconfigboard.mk | 2 ++ .../boards/sparkfun_redboard_turbo/mpconfigboard.mk | 2 ++ shared-module/audiocore/__init__.c | 12 ++++++++++++ 6 files changed, 22 insertions(+) (limited to 'shared-module') diff --git a/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.mk b/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.mk index 756c293ad..001d1e984 100644 --- a/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.mk +++ b/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.mk @@ -14,3 +14,5 @@ LONGINT_IMPL = MPZ CFLAGS_INLINE_LIMIT = 60 SUPEROPT_GC = 0 + +CIRCUITPY_AUDIOMIXER = 0 diff --git a/ports/atmel-samd/boards/feather_m0_supersized/mpconfigboard.mk b/ports/atmel-samd/boards/feather_m0_supersized/mpconfigboard.mk index e34d06962..fb8d014b9 100644 --- a/ports/atmel-samd/boards/feather_m0_supersized/mpconfigboard.mk +++ b/ports/atmel-samd/boards/feather_m0_supersized/mpconfigboard.mk @@ -14,3 +14,5 @@ LONGINT_IMPL = MPZ CFLAGS_INLINE_LIMIT = 60 SUPEROPT_GC = 0 + +CIRCUITPY_AUDIOMIXER = 0 diff --git a/ports/atmel-samd/boards/metro_m0_express/mpconfigboard.mk b/ports/atmel-samd/boards/metro_m0_express/mpconfigboard.mk index 93a8ffc11..7543f6efa 100644 --- a/ports/atmel-samd/boards/metro_m0_express/mpconfigboard.mk +++ b/ports/atmel-samd/boards/metro_m0_express/mpconfigboard.mk @@ -14,3 +14,5 @@ LONGINT_IMPL = MPZ CFLAGS_INLINE_LIMIT = 60 SUPEROPT_GC = 0 + +CIRCUITPY_AUDIOMIXER = 0 diff --git a/ports/atmel-samd/boards/snekboard/mpconfigboard.mk b/ports/atmel-samd/boards/snekboard/mpconfigboard.mk index 9892ade8f..d96487a6f 100644 --- a/ports/atmel-samd/boards/snekboard/mpconfigboard.mk +++ b/ports/atmel-samd/boards/snekboard/mpconfigboard.mk @@ -14,3 +14,5 @@ LONGINT_IMPL = MPZ CFLAGS_INLINE_LIMIT = 60 SUPEROPT_GC = 0 + +CIRCUITPY_AUDIOMIXER = 0 diff --git a/ports/atmel-samd/boards/sparkfun_redboard_turbo/mpconfigboard.mk b/ports/atmel-samd/boards/sparkfun_redboard_turbo/mpconfigboard.mk index 3be1a17d3..2ed809c24 100755 --- a/ports/atmel-samd/boards/sparkfun_redboard_turbo/mpconfigboard.mk +++ b/ports/atmel-samd/boards/sparkfun_redboard_turbo/mpconfigboard.mk @@ -14,3 +14,5 @@ LONGINT_IMPL = MPZ CFLAGS_INLINE_LIMIT = 60 SUPEROPT_GC = 0 + +CIRCUITPY_AUDIOMIXER = 0 diff --git a/shared-module/audiocore/__init__.c b/shared-module/audiocore/__init__.c index 8433a1770..f9762676f 100644 --- a/shared-module/audiocore/__init__.c +++ b/shared-module/audiocore/__init__.c @@ -42,9 +42,11 @@ uint32_t audiosample_sample_rate(mp_obj_t sample_obj) { } else if (MP_OBJ_IS_TYPE(sample_obj, &audioio_wavefile_type)) { audioio_wavefile_obj_t* file = MP_OBJ_TO_PTR(sample_obj); return file->sample_rate; + #if CIRCUITPY_AUDIOMIXER } else if (MP_OBJ_IS_TYPE(sample_obj, &audiomixer_mixer_type)) { audiomixer_mixer_obj_t* mixer = MP_OBJ_TO_PTR(sample_obj); return mixer->sample_rate; + #endif } return 16000; } @@ -56,9 +58,11 @@ uint8_t audiosample_bits_per_sample(mp_obj_t sample_obj) { } else if (MP_OBJ_IS_TYPE(sample_obj, &audioio_wavefile_type)) { audioio_wavefile_obj_t* file = MP_OBJ_TO_PTR(sample_obj); return file->bits_per_sample; + #if CIRCUITPY_AUDIOMIXER } else if (MP_OBJ_IS_TYPE(sample_obj, &audiomixer_mixer_type)) { audiomixer_mixer_obj_t* mixer = MP_OBJ_TO_PTR(sample_obj); return mixer->bits_per_sample; + #endif } return 8; } @@ -70,9 +74,11 @@ uint8_t audiosample_channel_count(mp_obj_t sample_obj) { } else if (MP_OBJ_IS_TYPE(sample_obj, &audioio_wavefile_type)) { audioio_wavefile_obj_t* file = MP_OBJ_TO_PTR(sample_obj); return file->channel_count; + #if CIRCUITPY_AUDIOMIXER } else if (MP_OBJ_IS_TYPE(sample_obj, &audiomixer_mixer_type)) { audiomixer_mixer_obj_t* mixer = MP_OBJ_TO_PTR(sample_obj); return mixer->channel_count; + #endif } return 1; } @@ -84,9 +90,11 @@ void audiosample_reset_buffer(mp_obj_t sample_obj, bool single_channel, uint8_t } else if (MP_OBJ_IS_TYPE(sample_obj, &audioio_wavefile_type)) { audioio_wavefile_obj_t* file = MP_OBJ_TO_PTR(sample_obj); audioio_wavefile_reset_buffer(file, single_channel, audio_channel); + #if CIRCUITPY_AUDIOMIXER } else if (MP_OBJ_IS_TYPE(sample_obj, &audiomixer_mixer_type)) { audiomixer_mixer_obj_t* file = MP_OBJ_TO_PTR(sample_obj); audiomixer_mixer_reset_buffer(file, single_channel, audio_channel); + #endif } } @@ -100,9 +108,11 @@ audioio_get_buffer_result_t audiosample_get_buffer(mp_obj_t sample_obj, } else if (MP_OBJ_IS_TYPE(sample_obj, &audioio_wavefile_type)) { audioio_wavefile_obj_t* file = MP_OBJ_TO_PTR(sample_obj); return audioio_wavefile_get_buffer(file, single_channel, channel, buffer, buffer_length); + #if CIRCUITPY_AUDIOMIXER } else if (MP_OBJ_IS_TYPE(sample_obj, &audiomixer_mixer_type)) { audiomixer_mixer_obj_t* file = MP_OBJ_TO_PTR(sample_obj); return audiomixer_mixer_get_buffer(file, single_channel, channel, buffer, buffer_length); + #endif } return GET_BUFFER_DONE; } @@ -118,9 +128,11 @@ void audiosample_get_buffer_structure(mp_obj_t sample_obj, bool single_channel, audioio_wavefile_obj_t* file = MP_OBJ_TO_PTR(sample_obj); audioio_wavefile_get_buffer_structure(file, single_channel, single_buffer, samples_signed, max_buffer_length, spacing); + #if CIRCUITPY_AUDIOMIXER } else if (MP_OBJ_IS_TYPE(sample_obj, &audiomixer_mixer_type)) { audiomixer_mixer_obj_t* file = MP_OBJ_TO_PTR(sample_obj); audiomixer_mixer_get_buffer_structure(file, single_channel, single_buffer, samples_signed, max_buffer_length, spacing); + #endif } } -- cgit v1.2.3 From 3c7c3c98d7f497df33f0503162fb7b1721ef7ecd Mon Sep 17 00:00:00 2001 From: sommersoft Date: Sat, 31 Aug 2019 17:36:54 -0500 Subject: include CMSIS instrinsic addition functions for M4; cleanup C math funcs --- shared-module/audiomixer/Mixer.c | 38 +++++++++++++++----------------------- 1 file changed, 15 insertions(+), 23 deletions(-) (limited to 'shared-module') diff --git a/shared-module/audiomixer/Mixer.c b/shared-module/audiomixer/Mixer.c index da0f7dedb..b61ba1eb0 100644 --- a/shared-module/audiomixer/Mixer.c +++ b/shared-module/audiomixer/Mixer.c @@ -95,32 +95,28 @@ void audiomixer_mixer_reset_buffer(audiomixer_mixer_obj_t* self, uint32_t add8signed(uint32_t a, uint32_t b) { #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) - return __QADD8(a, b); + return __SHADD8(a, b); #else uint32_t result = 0; for (int8_t i = 0; i < 4; i++) { int8_t ai = a >> (sizeof(int8_t) * 8 * i); int8_t bi = b >> (sizeof(int8_t) * 8 * i); - int32_t intermediate = (int32_t) ai + bi; + int32_t intermediate = (int32_t) ai + bi / 2; if (intermediate > CHAR_MAX) { intermediate = CHAR_MAX; } else if (intermediate < CHAR_MIN) { intermediate = CHAR_MIN; } - result |= (((uint32_t) intermediate) & 0xff) << (sizeof(int8_t) * 8 * i); + result |= ((uint32_t) intermediate & 0xff) << (sizeof(int8_t) * 8 * i); } return result; #endif } uint32_t add8unsigned(uint32_t a, uint32_t b) { - /*#if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) - // Subtract out the DC offset, add and then shift back. - a = __USUB8(a, 0x80808080); - b = __USUB8(b, 0x80808080); - uint32_t sum = __QADD8(a, b); - return __UADD8(sum, 0x80808080); - #else*/ + #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + return __UHADD8(a, b); + #else uint32_t result = 0; for (int8_t i = 0; i < 4; i++) { uint8_t ai = (a >> (sizeof(uint8_t) * 8 * i)); @@ -132,18 +128,18 @@ uint32_t add8unsigned(uint32_t a, uint32_t b) { result |= ((uint32_t) intermediate & 0xff) << (sizeof(uint8_t) * 8 * i); } return result; - //#endif + #endif } uint32_t add16signed(uint32_t a, uint32_t b) { - /*#if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) - return __QADD16(a, b); - #else*/ + #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + return __SHADD16(a, b); + #else uint32_t result = 0; for (int8_t i = 0; i < 2; i++) { int16_t ai = a >> (sizeof(int16_t) * 8 * i); int16_t bi = b >> (sizeof(int16_t) * 8 * i); - int32_t intermediate = (int32_t) ai + bi; + int32_t intermediate = (int32_t) ai + bi / 2; if (intermediate > SHRT_MAX) { intermediate = SHRT_MAX; } else if (intermediate < SHRT_MIN) { @@ -152,26 +148,22 @@ uint32_t add16signed(uint32_t a, uint32_t b) { result |= (((uint32_t) intermediate) & 0xffff) << (sizeof(int16_t) * 8 * i); } return result; - //#endif + #endif } uint32_t add16unsigned(uint32_t a, uint32_t b) { #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) - // Subtract out the DC offset, add and then shift back. - a = __USUB16(a, 0x80008000); - b = __USUB16(b, 0x80008000); - uint32_t sum = __QADD16(a, b); - return __UADD16(sum, 0x80008000); + return __UHADD16(a, b); #else uint32_t result = 0; for (int8_t i = 0; i < 2; i++) { int16_t ai = (a >> (sizeof(uint16_t) * 8 * i)) - 0x8000; int16_t bi = (b >> (sizeof(uint16_t) * 8 * i)) - 0x8000; - int32_t intermediate = (int32_t) ai + bi; + int32_t intermediate = (int32_t) ai + bi / 2; if (intermediate > USHRT_MAX) { intermediate = USHRT_MAX; } - result |= ((uint16_t) intermediate + 0x8000) << (sizeof(int16_t) * 8 * i); + result |= ((uint32_t) intermediate & 0xffff) << (sizeof(int16_t) * 8 * i); } return result; #endif -- cgit v1.2.3 From 362c1664ae5979f288e8cb12d528f8cd44100410 Mon Sep 17 00:00:00 2001 From: sommersoft Date: Sun, 1 Sep 2019 17:36:29 -0500 Subject: use more accurate ARMv7 prepocessor flags; TODOs for asm instructions --- shared-module/audiomixer/Mixer.c | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) (limited to 'shared-module') diff --git a/shared-module/audiomixer/Mixer.c b/shared-module/audiomixer/Mixer.c index b61ba1eb0..9f30bd47f 100644 --- a/shared-module/audiomixer/Mixer.c +++ b/shared-module/audiomixer/Mixer.c @@ -94,7 +94,7 @@ void audiomixer_mixer_reset_buffer(audiomixer_mixer_obj_t* self, } uint32_t add8signed(uint32_t a, uint32_t b) { - #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + #if (defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) //Cortex-M4 w/FPU return __SHADD8(a, b); #else uint32_t result = 0; @@ -114,7 +114,7 @@ uint32_t add8signed(uint32_t a, uint32_t b) { } uint32_t add8unsigned(uint32_t a, uint32_t b) { - #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + #if (defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) //Cortex-M4 w/FPU return __UHADD8(a, b); #else uint32_t result = 0; @@ -132,7 +132,7 @@ uint32_t add8unsigned(uint32_t a, uint32_t b) { } uint32_t add16signed(uint32_t a, uint32_t b) { - #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + #if (defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) //Cortex-M4 w/FPU return __SHADD16(a, b); #else uint32_t result = 0; @@ -152,7 +152,7 @@ uint32_t add16signed(uint32_t a, uint32_t b) { } uint32_t add16unsigned(uint32_t a, uint32_t b) { - #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + #if (defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) //Cortex-M4 w/FPU return __UHADD16(a, b); #else uint32_t result = 0; @@ -169,15 +169,16 @@ uint32_t add16unsigned(uint32_t a, uint32_t b) { #endif } -//TODO: static inline uint32_t mult8unsigned(uint32_t val, int32_t mul) { // if mul == 0, no need in wasting cycles if (mul == 0) { return 0; } - /*#if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + /* TODO: workout ARMv7 instructions + #if (defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) //Cortex-M4 w/FPU return val; #else*/ + mp_printf(&mp_plat_print, "mult8unsigned"); uint32_t result = 0; float mod_mul = (float) mul / (float) ((1<<15)-1); for (int8_t i = 0; i < 4; i++) { @@ -193,14 +194,13 @@ static inline uint32_t mult8unsigned(uint32_t val, int32_t mul) { //#endif } -//TODO: static inline uint32_t mult8signed(uint32_t val, int32_t mul) { // if mul == 0, no need in wasting cycles if (mul == 0) { return 0; } - /* - #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + /* TODO: workout ARMv7 instructions + #if (defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) //Cortex-M4 w/FPU return val; #else */ @@ -226,8 +226,17 @@ static inline uint32_t mult16unsigned(uint32_t val, int32_t mul) { if (mul == 0) { return 0; } - /* - #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + /* TODO: the below ARMv7m instructions "work", but the amplitude is much higher/louder + #if (defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) //Cortex-M4 w/FPU + // there is no unsigned equivalent to the 'SMULWx' ARMv7 Thumb function, + // so we have to do it by hand. + uint32_t lo = val & 0xffff; + uint32_t hi = val >> 16; + //mp_printf(&mp_plat_print, "pre-asm: (mul: %d)\n\tval: %x\tlo: %x\thi: %x\n", mul, val, lo, hi); + uint32_t val_lo; + asm volatile("mul %0, %1, %2" : "=r" (val_lo) : "r" (mul), "r" (lo)); + asm volatile("mla %0, %1, %2, %3" : "=r" (val) : "r" (mul), "r" (hi), "r" (val_lo)); + //mp_printf(&mp_plat_print, "post-asm:\n\tval: %x\tlo: %x\n\n", val, val_lo); return val; #else */ @@ -243,7 +252,7 @@ static inline uint32_t mult16unsigned(uint32_t val, int32_t mul) { } result |= (((uint32_t) intermediate) + 0x8000) << (sizeof(int16_t) * 8 * i); } - return val; + return result; //#endif } @@ -252,7 +261,7 @@ static inline uint32_t mult16signed(uint32_t val, int32_t mul) { if (mul == 0) { return 0; } - #if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + #if (defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) //Cortex-M4 w/FPU int32_t hi, lo; int32_t bits = 16; // saturate to 16 bits int32_t shift = 0; // shift is done automatically -- cgit v1.2.3 From edfcee29ff88faa35a75a979cb74ad64553289a6 Mon Sep 17 00:00:00 2001 From: sommersoft Date: Tue, 3 Sep 2019 22:25:27 -0500 Subject: remove leftover debug print --- shared-module/audiomixer/Mixer.c | 1 - 1 file changed, 1 deletion(-) (limited to 'shared-module') diff --git a/shared-module/audiomixer/Mixer.c b/shared-module/audiomixer/Mixer.c index 9f30bd47f..e0fb4fab8 100644 --- a/shared-module/audiomixer/Mixer.c +++ b/shared-module/audiomixer/Mixer.c @@ -178,7 +178,6 @@ static inline uint32_t mult8unsigned(uint32_t val, int32_t mul) { #if (defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) //Cortex-M4 w/FPU return val; #else*/ - mp_printf(&mp_plat_print, "mult8unsigned"); uint32_t result = 0; float mod_mul = (float) mul / (float) ((1<<15)-1); for (int8_t i = 0; i < 4; i++) { -- cgit v1.2.3