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 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