diff options
| author | sommersoft <sommersoft@gmail.com> | 2019-08-03 11:20:06 -0500 |
|---|---|---|
| committer | sommersoft <sommersoft@gmail.com> | 2019-08-03 11:20:06 -0500 |
| commit | 065efb05b75e35f7e161b6828742b01d26ce5379 (patch) | |
| tree | d08dfbb71bd5375db4f964604c6d5a887ca4dd7d /shared-bindings/audiocore | |
| parent | 9939d0c4f49db084f52a12b6ea5f6ce97b614146 (diff) | |
bring MixerVoice back to building state; update documentation
Diffstat (limited to 'shared-bindings/audiocore')
| -rw-r--r-- | shared-bindings/audiocore/Mixer.c | 82 | ||||
| -rw-r--r-- | shared-bindings/audiocore/MixerVoice.c | 221 | ||||
| -rw-r--r-- | shared-bindings/audiocore/MixerVoice.h | 47 | ||||
| -rw-r--r-- | shared-bindings/audiocore/__init__.c | 1 |
4 files changed, 294 insertions, 57 deletions
diff --git a/shared-bindings/audiocore/Mixer.c b/shared-bindings/audiocore/Mixer.c index ee4d8548c..66babdd08 100644 --- a/shared-bindings/audiocore/Mixer.c +++ b/shared-bindings/audiocore/Mixer.c @@ -24,6 +24,8 @@ * THE SOFTWARE. */ #include "shared-bindings/audiocore/Mixer.h" +#include "shared-bindings/audiocore/MixerVoice.h" +#include "shared-module/audiocore/MixerVoice.h" #include <stdint.h> @@ -43,33 +45,43 @@ //| //| Mixer mixes multiple samples into one sample. //| -//| .. class:: Mixer(channel_count=2, buffer_size=1024) +//| .. class:: Mixer(voice_count=2, buffer_size=1024, channel_count=2, bits_per_sample=16, samples_signed=True, sample_rate=8000) //| //| Create a Mixer object that can mix multiple channels with the same sample rate. +//| Samples are accessed and controlled with the mixer's `audioio.MixerVoice` objects. //| -//| :param int channel_count: The maximum number of samples to mix at once +//| :param int voice_count: The maximum number of voices to mix //| :param int buffer_size: The total size in bytes of the buffers to mix into +//| :param int channel_count: The maximum number of samples to mix at once +//| :param int bits_per_sample: The bits per sample of the samples being played +//| :param bool samples_signed: Samples are signed (True) or unsigned (False) +//| :param int sample_rate: The sample rate to be used for all samples //| //| Playing a wave file from flash:: //| //| import board //| import audioio +//| import audiocore //| import digitalio //| //| # Required for CircuitPlayground Express //| speaker_enable = digitalio.DigitalInOut(board.SPEAKER_ENABLE) //| speaker_enable.switch_to_output(value=True) //| -//| music = audioio.WaveFile(open("cplay-5.1-16bit-16khz.wav", "rb")) -//| drum = audioio.WaveFile(open("drum.wav", "rb")) -//| mixer = audioio.Mixer(voice_count=2, sample_rate=16000, channel_count=1, bits_per_sample=16, samples_signed=True) +//| music = audiocore.WaveFile(open("cplay-5.1-16bit-16khz.wav", "rb")) +//| drum = audiocore.WaveFile(open("drum.wav", "rb")) +//| mixer = audiocore.Mixer(voice_count=2, sample_rate=16000, channel_count=1, +//| bits_per_sample=16, samples_signed=True) //| a = audioio.AudioOut(board.A0) //| //| print("playing") +//| # Have AudioOut play our Mixer source //| a.play(mixer) -//| mixer.play(music, voice=0) +//| # Play the first sample voice +//| mixer.voice[0].play(music) //| while mixer.playing: -//| mixer.play(drum, voice=1) +//| # Play the second sample voice +//| mixer.voice[1].play(drum) //| time.sleep(1) //| print("stopped") //| @@ -151,54 +163,6 @@ STATIC mp_obj_t audioio_mixer_obj___exit__(size_t n_args, const mp_obj_t *args) } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(audioio_mixer___exit___obj, 4, 4, audioio_mixer_obj___exit__); - -//| .. method:: play(sample, *, voice=0, loop=False) -//| -//| Plays the sample once when loop=False and continuously when loop=True. -//| Does not block. Use `playing` to block. -//| -//| Sample must be an `audioio.WaveFile`, `audioio.Mixer` or `audioio.RawSample`. -//| -//| The sample must match the Mixer's encoding settings given in the constructor. -//| -STATIC mp_obj_t audioio_mixer_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_sample, ARG_voice, ARG_loop }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_sample, MP_ARG_OBJ | MP_ARG_REQUIRED }, - { MP_QSTR_voice, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, - { MP_QSTR_loop, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, - }; - audioio_mixer_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); - check_for_deinit(self); - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - mp_obj_t sample = args[ARG_sample].u_obj; - common_hal_audioio_mixer_play(self, sample, args[ARG_voice].u_int, args[ARG_loop].u_bool); - - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_KW(audioio_mixer_play_obj, 1, audioio_mixer_obj_play); - -//| .. method:: stop_voice(voice=0) -//| -//| Stops playback of the sample on the given voice. -//| -STATIC mp_obj_t audioio_mixer_obj_stop_voice(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_voice }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_voice, MP_ARG_INT, {.u_int = 0} }, - }; - audioio_mixer_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); - check_for_deinit(self); - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - common_hal_audioio_mixer_stop_voice(self, args[ARG_voice].u_int); - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_KW(audioio_mixer_stop_voice_obj, 1, audioio_mixer_obj_stop_voice); - //| .. attribute:: playing //| //| True when any voice is being output. (read-only) @@ -237,11 +201,15 @@ const mp_obj_property_t audioio_mixer_sample_rate_obj = { //| .. attribute:: voice //| -//| tuple of voice objects +//| A tuple of the mixer's `audioio.MixerVoice` object(s). +//| +//| .. code-block:: python //| +//| >>> mixer.voice +//| (<MixerVoice>,) STATIC mp_obj_t audioio_mixer_obj_get_voice(mp_obj_t self_in) { audioio_mixer_obj_t *self = MP_OBJ_TO_PTR(self_in); - raise_error_if_deinited(common_hal_audioio_mixer_deinited(self)); + check_for_deinit(self); return self->voice_tuple; } MP_DEFINE_CONST_FUN_OBJ_1(audioio_mixer_get_voice_obj, audioio_mixer_obj_get_voice); diff --git a/shared-bindings/audiocore/MixerVoice.c b/shared-bindings/audiocore/MixerVoice.c new file mode 100644 index 000000000..50f14c758 --- /dev/null +++ b/shared-bindings/audiocore/MixerVoice.c @@ -0,0 +1,221 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 DeanM for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "shared-bindings/audiocore/Mixer.h" +#include "shared-bindings/audiocore/MixerVoice.h" + +#include <stdint.h> + +#include "lib/utils/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/audiocore/RawSample.h" +#include "shared-bindings/util.h" +#include "supervisor/shared/translate.h" + +//| .. currentmodule:: audioio +//| +//| :class:`MixerVoice` -- Voice objects used with Mixer +//| ===================================================== +//| +//| Used to access and control samples with `audioio.Mixer`. +//| +//| .. class:: MixerVoice() +//| +//| MixerVoice instance object(s) created by `audioio.Mixer`. +//| +// TODO: support mono or stereo voices +STATIC mp_obj_t audioio_mixervoice_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + audioio_mixervoice_obj_t *self = m_new_obj(audioio_mixervoice_obj_t); + self->base.type = &audioio_mixervoice_type; + self->sample = NULL; + self->level = ((1 << 15)-1); + return MP_OBJ_FROM_PTR(self); +} + +//| .. method:: deinit() +//| +//| Deinitialises the Voice and releases any hardware resources for reuse. +//| +STATIC mp_obj_t audioio_mixervoice_deinit(mp_obj_t self_in) { + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(audioio_mixervoice_deinit_obj, audioio_mixervoice_deinit); + +//| .. method:: __enter__() +//| +//| No-op used by Context Managers. +//| +// Provided by context manager helper. + +//| .. method:: __exit__() +//| +//| Automatically deinitializes the hardware when exiting a context. See +//| :ref:`lifetime-and-contextmanagers` for more info. +//| +STATIC mp_obj_t audioio_mixervoice_obj___exit__(size_t n_args, const mp_obj_t *args) { + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(audioio_mixervoice___exit___obj, 4, 4, audioio_mixervoice_obj___exit__); + + +//| .. method:: play(sample, *, loop=False) +//| +//| Plays the sample once when ``loop=False``, and continuously when ``loop=True``. +//| Does not block. Use `playing` to block. +//| +//| Sample must be an `audioio.WaveFile`, `audioio.Mixer` or `audioio.RawSample`. +//| +//| The sample must match the `audioio.Mixer`'s encoding settings given in the constructor. +//| +STATIC mp_obj_t audioio_mixervoice_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_sample, ARG_loop }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_sample, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_loop, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, + }; + audioio_mixervoice_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + //raise_error_if_deinited(common_hal_audioio_mixervoice_deinited(self)); + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_obj_t sample = args[ARG_sample].u_obj; + common_hal_audioio_mixervoice_play(self, sample, args[ARG_loop].u_bool); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(audioio_mixervoice_play_obj, 1, audioio_mixervoice_obj_play); + +//| .. method:: stop() +//| +//| Stops playback of the sample on this voice. +//| +STATIC mp_obj_t audioio_mixervoice_obj_stop(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_voice }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_voice, MP_ARG_INT, {.u_int = 0} }, + }; + audioio_mixervoice_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + //raise_error_if_deinited(common_hal_audioio_mixer_deinited(self)); + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + common_hal_audioio_mixervoice_stop(self); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(audioio_mixervoice_stop_obj, 1, audioio_mixervoice_obj_stop); + +//| .. method:: level() +//| +//| The volume level of a voice, as a floating point number between 0 and 1. +//| +STATIC mp_obj_t audioio_mixervoice_obj_get_level(mp_obj_t self_in) { + + #if !MICROPY_PY_BUILTINS_FLOAT + #error "floating point not supported" + #endif + + return mp_obj_new_float(common_hal_audioio_mixervoice_get_level(self_in)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audioio_mixervoice_get_level_obj, audioio_mixervoice_obj_get_level); + +STATIC mp_obj_t audioio_mixervoice_obj_set_level(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_level }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_level, MP_ARG_OBJ | MP_ARG_REQUIRED }, + }; + audioio_mixervoice_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + #if MICROPY_PY_BUILTINS_FLOAT + float level = mp_obj_get_float(args[ARG_level].u_obj); + #else + #error "floating point not supported" + #endif + + if (level > 1 || level < 0) { + mp_raise_ValueError(translate("level must be between 0 and 1")); + } + + common_hal_audioio_mixervoice_set_level(self, level); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(audioio_mixervoice_set_level_obj, 1, audioio_mixervoice_obj_set_level); + +const mp_obj_property_t audioio_mixervoice_level_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&audioio_mixervoice_get_level_obj, + (mp_obj_t)&audioio_mixervoice_set_level_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + +//TODO: @sommersoft - enable voice.playing tracking? will need an additional field +// in the struct, with supporting logic. disabled documentation for now. + +// .. attribute:: playing +// +// True when any voice is being output. (read-only) +// +STATIC mp_obj_t audioio_mixervoice_obj_get_playing(mp_obj_t self_in) { +#if 0 + audioio_mixer_obj_t *self = MP_OBJ_TO_PTR(self_in); + raise_error_if_deinited(common_hal_audioio_mixer_deinited(self)); + return mp_obj_new_bool(common_hal_audioio_mixer_get_playing(self)); +#endif + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(audioio_mixervoice_get_playing_obj, audioio_mixervoice_obj_get_playing); + +const mp_obj_property_t audioio_mixervoice_playing_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&audioio_mixervoice_get_playing_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + +STATIC const mp_rom_map_elem_t audioio_mixervoice_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&audioio_mixervoice_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&audioio_mixervoice___exit___obj) }, + { MP_ROM_QSTR(MP_QSTR_play), MP_ROM_PTR(&audioio_mixervoice_play_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&audioio_mixervoice_stop_obj) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&audioio_mixervoice_playing_obj) }, + { MP_ROM_QSTR(MP_QSTR_level), MP_ROM_PTR(&audioio_mixervoice_level_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(audioio_mixervoice_locals_dict, audioio_mixervoice_locals_dict_table); + +const mp_obj_type_t audioio_mixervoice_type = { + { &mp_type_type }, + .name = MP_QSTR_MixerVoice, + .make_new = audioio_mixervoice_make_new, + .locals_dict = (mp_obj_dict_t*)&audioio_mixervoice_locals_dict, +}; diff --git a/shared-bindings/audiocore/MixerVoice.h b/shared-bindings/audiocore/MixerVoice.h new file mode 100644 index 000000000..4e7ea47e7 --- /dev/null +++ b/shared-bindings/audiocore/MixerVoice.h @@ -0,0 +1,47 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 DeanM for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef SHARED_BINDINGS_AUDIOIO_MIXERVOICE_H_ +#define SHARED_BINDINGS_AUDIOIO_MIXERVOICE_H_ + +#include "common-hal/microcontroller/Pin.h" +#include "shared-module/audiocore/Mixer.h" +#include "shared-bindings/audiocore/RawSample.h" +#include "shared-module/audiocore/MixerVoice.h" +#include "shared-module/audiocore/Mixer.h" + +extern const mp_obj_type_t audioio_mixer_type; +extern const mp_obj_type_t audioio_mixervoice_type; + +void common_hal_audioio_mixervoice_deinit(audioio_mixervoice_obj_t* self); +void common_hal_audioio_mixervoice_set_parent(audioio_mixervoice_obj_t* self, audioio_mixer_obj_t *parent); +void common_hal_audioio_mixervoice_play(audioio_mixervoice_obj_t* self, mp_obj_t sample, bool loop); +void common_hal_audioio_mixervoice_stop(audioio_mixervoice_obj_t* self); +float common_hal_audioio_mixervoice_get_level(audioio_mixervoice_obj_t* self); +void common_hal_audioio_mixervoice_set_level(audioio_mixervoice_obj_t* self, float gain); + +bool common_hal_audioio_mixervoice_get_playing(audioio_mixervoice_obj_t* self); + +#endif /* SHARED_BINDINGS_AUDIOIO_MIXERVOICE_H_ */ diff --git a/shared-bindings/audiocore/__init__.c b/shared-bindings/audiocore/__init__.c index c64759d4f..9d82b1f99 100644 --- a/shared-bindings/audiocore/__init__.c +++ b/shared-bindings/audiocore/__init__.c @@ -50,6 +50,7 @@ //| :maxdepth: 3 //| //| Mixer +//| MixerVoice //| RawSample //| WaveFile //| |
