diff options
| author | Dan Halbert <halbert@halwitz.org> | 2018-04-13 14:59:10 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-04-13 14:59:10 -0400 |
| commit | 10eabf6bc22391292c7bec42f611a876698d587f (patch) | |
| tree | 4517268e0c44e0ff4f614fdfe4400b53c6e80ff8 /shared-bindings | |
| parent | 0f4a2032b73104bb3715cbea6fad64ba31ef2fac (diff) | |
| parent | 22194d5977e5fe0ce9254043d6aefd1469982a97 (diff) | |
Merge pull request #756 from tannewt/audio3
Add audio output support!
Diffstat (limited to 'shared-bindings')
| -rw-r--r-- | shared-bindings/audiobusio/I2SOut.c | 233 | ||||
| -rw-r--r-- | shared-bindings/audiobusio/I2SOut.h | 45 | ||||
| -rw-r--r-- | shared-bindings/audiobusio/__init__.c | 5 | ||||
| -rw-r--r-- | shared-bindings/audioio/AudioOut.c | 123 | ||||
| -rw-r--r-- | shared-bindings/audioio/AudioOut.h | 13 | ||||
| -rw-r--r-- | shared-bindings/audioio/RawSample.c | 183 | ||||
| -rw-r--r-- | shared-bindings/audioio/RawSample.h | 45 | ||||
| -rw-r--r-- | shared-bindings/audioio/WaveFile.c | 158 | ||||
| -rw-r--r-- | shared-bindings/audioio/WaveFile.h | 44 | ||||
| -rw-r--r-- | shared-bindings/audioio/__init__.c | 5 |
10 files changed, 772 insertions, 82 deletions
diff --git a/shared-bindings/audiobusio/I2SOut.c b/shared-bindings/audiobusio/I2SOut.c new file mode 100644 index 000000000..6233bb51a --- /dev/null +++ b/shared-bindings/audiobusio/I2SOut.c @@ -0,0 +1,233 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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 <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/audiobusio/I2SOut.h" +#include "shared-bindings/util.h" + +//| .. currentmodule:: audiobusio +//| +//| :class:`I2SOut` -- Output an I2S audio signal +//| ======================================================== +//| +//| I2S is used to output an audio signal on an I2S bus. +//| +//| .. class:: I2SOut(bit_clock, word_select, data, *, left_justified) +//| +//| Create a I2SOut object associated with the given pins. +//| +//| :param ~microcontroller.Pin bit_clock: The bit clock (or serial clock) pin +//| :param ~microcontroller.Pin word_select: The word select (or left/right clock) pin +//| :param ~microcontroller.Pin data: The data pin +//| :param bool left_justified: True when data bits are aligned with the word select clock. False +//| when they are shifted by one to match classic I2S protocol. +//| +//| Simple 8ksps 440 Hz sine wave on `Metro M0 Express <https://www.adafruit.com/product/3505>`_ +//| using `UDA1334 Breakout <https://www.adafruit.com/product/3678>`_:: +//| +//| import audiobusio +//| import audioio +//| import board +//| import array +//| import time +//| import math +//| +//| # Generate one period of sine wav. +//| length = 8000 // 440 +//| sine_wave = array.array("H", [0] * length) +//| for i in range(length): +//| sine_wave[i] = int(math.sin(math.pi * 2 * i / 18) * (2 ** 15) + 2 ** 15) +//| +//| sine_wave = audiobusio.RawSample(sine_wave, sample_rate=8000) +//| i2s = audiobusio.I2SOut(board.D1, board.D0, board.D9) +//| i2s.play(sine_wave, loop=True) +//| time.sleep(1) +//| i2s.stop() +//| +//| Playing a wave file from flash:: +//| +//| import board +//| import audioio +//| import audiobusio +//| import digitalio +//| +//| +//| f = open("cplay-5.1-16bit-16khz.wav", "rb") +//| wav = audioio.WaveFile(f) +//| +//| a = audiobusio.I2SOut(board.D1, board.D0, board.D9) +//| +//| print("playing") +//| a.play(wav) +//| while a.playing: +//| pass +//| print("stopped") +//| +STATIC mp_obj_t audiobusio_i2sout_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *pos_args) { + mp_arg_check_num(n_args, n_kw, 3, 4, true); + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, pos_args + n_args); + enum { ARG_bit_clock, ARG_word_select, ARG_data, ARG_left_justified }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_bit_clock, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_word_select, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_data, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_left_justified, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_bool = false} }, + }; + 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_obj_t bit_clock_obj = args[ARG_bit_clock].u_obj; + assert_pin(bit_clock_obj, false); + const mcu_pin_obj_t *bit_clock = MP_OBJ_TO_PTR(bit_clock_obj); + + mp_obj_t word_select_obj = args[ARG_word_select].u_obj; + assert_pin(word_select_obj, false); + const mcu_pin_obj_t *word_select = MP_OBJ_TO_PTR(word_select_obj); + + mp_obj_t data_obj = args[ARG_data].u_obj; + assert_pin(data_obj, false); + const mcu_pin_obj_t *data = MP_OBJ_TO_PTR(data_obj); + + audiobusio_i2sout_obj_t *self = m_new_obj(audiobusio_i2sout_obj_t); + self->base.type = &audiobusio_i2sout_type; + common_hal_audiobusio_i2sout_construct(self, bit_clock, word_select, data, args[ARG_left_justified].u_bool); + + return MP_OBJ_FROM_PTR(self); +} + +//| .. method:: deinit() +//| +//| Deinitialises the I2SOut and releases any hardware resources for reuse. +//| +STATIC mp_obj_t audiobusio_i2sout_deinit(mp_obj_t self_in) { + audiobusio_i2sout_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_audiobusio_i2sout_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(audiobusio_i2sout_deinit_obj, audiobusio_i2sout_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 audiobusio_i2sout_obj___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_audiobusio_i2sout_deinit(args[0]); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(audiobusio_i2sout___exit___obj, 4, 4, audiobusio_i2sout_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` or `audioio.RawSample`. +//| +//| The sample itself should consist of 8 bit or 16 bit samples. +//| +STATIC mp_obj_t audiobusio_i2sout_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} }, + }; + audiobusio_i2sout_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + raise_error_if_deinited(common_hal_audiobusio_i2sout_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_audiobusio_i2sout_play(self, sample, args[ARG_loop].u_bool); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(audiobusio_i2sout_play_obj, 1, audiobusio_i2sout_obj_play); + +//| .. method:: stop() +//| +//| Stops playback. +//| +STATIC mp_obj_t audiobusio_i2sout_obj_stop(mp_obj_t self_in) { + audiobusio_i2sout_obj_t *self = MP_OBJ_TO_PTR(self_in); + raise_error_if_deinited(common_hal_audiobusio_i2sout_deinited(self)); + common_hal_audiobusio_i2sout_stop(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(audiobusio_i2sout_stop_obj, audiobusio_i2sout_obj_stop); + +//| .. attribute:: playing +//| +//| True when the audio sample is being output. (read-only) +//| +STATIC mp_obj_t audiobusio_i2sout_obj_get_playing(mp_obj_t self_in) { + audiobusio_i2sout_obj_t *self = MP_OBJ_TO_PTR(self_in); + raise_error_if_deinited(common_hal_audiobusio_i2sout_deinited(self)); + return mp_obj_new_bool(common_hal_audiobusio_i2sout_get_playing(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audiobusio_i2sout_get_playing_obj, audiobusio_i2sout_obj_get_playing); + +const mp_obj_property_t audiobusio_i2sout_playing_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&audiobusio_i2sout_get_playing_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + +STATIC const mp_rom_map_elem_t audiobusio_i2sout_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&audiobusio_i2sout_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&audiobusio_i2sout___exit___obj) }, + { MP_ROM_QSTR(MP_QSTR_play), MP_ROM_PTR(&audiobusio_i2sout_play_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&audiobusio_i2sout_stop_obj) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&audiobusio_i2sout_playing_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(audiobusio_i2sout_locals_dict, audiobusio_i2sout_locals_dict_table); + +const mp_obj_type_t audiobusio_i2sout_type = { + { &mp_type_type }, + .name = MP_QSTR_I2SOut, + .make_new = audiobusio_i2sout_make_new, + .locals_dict = (mp_obj_dict_t*)&audiobusio_i2sout_locals_dict, +}; diff --git a/shared-bindings/audiobusio/I2SOut.h b/shared-bindings/audiobusio/I2SOut.h new file mode 100644 index 000000000..930044054 --- /dev/null +++ b/shared-bindings/audiobusio/I2SOut.h @@ -0,0 +1,45 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017, 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_AUDIOBUSIO_I2SOUT_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOBUSIO_I2SOUT_H + +#include "common-hal/audiobusio/I2SOut.h" +#include "common-hal/microcontroller/Pin.h" + +extern const mp_obj_type_t audiobusio_i2sout_type; + +void common_hal_audiobusio_i2sout_construct(audiobusio_i2sout_obj_t* self, + const mcu_pin_obj_t* bit_clock, const mcu_pin_obj_t* word_select, const mcu_pin_obj_t* data, + bool left_justified); + +void common_hal_audiobusio_i2sout_deinit(audiobusio_i2sout_obj_t* self); +bool common_hal_audiobusio_i2sout_deinited(audiobusio_i2sout_obj_t* self); +void common_hal_audiobusio_i2sout_play(audiobusio_i2sout_obj_t* self, mp_obj_t sample, bool loop); +void common_hal_audiobusio_i2sout_stop(audiobusio_i2sout_obj_t* self); +bool common_hal_audiobusio_i2sout_get_playing(audiobusio_i2sout_obj_t* self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOBUSIO_I2SOUT_H diff --git a/shared-bindings/audiobusio/__init__.c b/shared-bindings/audiobusio/__init__.c index bdd999b04..528a8fb31 100644 --- a/shared-bindings/audiobusio/__init__.c +++ b/shared-bindings/audiobusio/__init__.c @@ -31,6 +31,7 @@ #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/audiobusio/__init__.h" +#include "shared-bindings/audiobusio/I2SOut.h" #include "shared-bindings/audiobusio/PDMIn.h" //| :mod:`audiobusio` --- Support for audio input and output over digital bus @@ -50,6 +51,7 @@ //| .. toctree:: //| :maxdepth: 3 //| +//| I2SOut //| PDMIn //| //| All libraries change hardware state and should be deinitialized when they @@ -59,7 +61,8 @@ STATIC const mp_rom_map_elem_t audiobusio_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_audiobusio) }, - { MP_ROM_QSTR(MP_QSTR_PDMIn), MP_ROM_PTR(&audiobusio_pdmin_type) }, + { MP_ROM_QSTR(MP_QSTR_I2SOut), MP_ROM_PTR(&audiobusio_i2sout_type) }, + //{ MP_ROM_QSTR(MP_QSTR_PDMIn), MP_ROM_PTR(&audiobusio_pdmin_type) }, }; STATIC MP_DEFINE_CONST_DICT(audiobusio_module_globals, audiobusio_module_globals_table); diff --git a/shared-bindings/audioio/AudioOut.c b/shared-bindings/audioio/AudioOut.c index 5bfd7e2a3..189a217d4 100644 --- a/shared-bindings/audioio/AudioOut.c +++ b/shared-bindings/audioio/AudioOut.c @@ -32,6 +32,7 @@ #include "py/runtime.h" #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/audioio/AudioOut.h" +#include "shared-bindings/audioio/RawSample.h" #include "shared-bindings/util.h" //| .. currentmodule:: audioio @@ -41,18 +42,13 @@ //| //| AudioOut can be used to output an analog audio signal on a given pin. //| -//| .. class:: AudioOut(pin, sample_source) +//| .. class:: AudioOut(left_channel, right_channel=None) //| -//| Create a AudioOut object associated with the given pin. This allows you to -//| play audio signals out on the given pin. Sample_source must be a `bytes-like object <https://docs.python.org/3/glossary.html#term-bytes-like-object>`_. +//| Create a AudioOut object associated with the given pin(s). This allows you to +//| play audio signals out on the given pin(s). //| -//| The sample itself should consist of 16 bit samples and be mono. -//| Microcontrollers with a lower output resolution will use the highest order -//| bits to output. For example, the SAMD21 has a 10 bit DAC that ignores the -//| lowest 6 bits when playing 16 bit samples. -//| -//| :param ~microcontroller.Pin pin: The pin to output to -//| :param bytes-like sample_source: The source of the sample +//| :param ~microcontroller.Pin left_channel: The pin to output the left channel to +//| :param ~microcontroller.Pin right_channel: The pin to output the right channel to //| //| Simple 8ksps 440 Hz sin wave:: //| @@ -68,8 +64,9 @@ //| for i in range(length): //| sine_wave[i] = int(math.sin(math.pi * 2 * i / 18) * (2 ** 15) + 2 ** 15) //| -//| sample = audioio.AudioOut(board.SPEAKER, sine_wave) -//| sample.play(loop=True) +//| dac = audioio.AudioOut(board.SPEAKER) +//| sine_wave = audioio.RawSample(sine_wave, mono=True, sample_rate=8000) +//| dac.play(sine_wave, loop=True) //| time.sleep(1) //| sample.stop() //| @@ -83,41 +80,42 @@ //| speaker_enable = digitalio.DigitalInOut(board.SPEAKER_ENABLE) //| speaker_enable.switch_to_output(value=True) //| -//| f = open("cplay-5.1-16bit-16khz.wav", "rb") -// -//| a = audioio.AudioOut(board.A0, f) +//| wav = audioio.WaveFile("cplay-5.1-16bit-16khz.wav") +//| a = audioio.AudioOut(board.A0) //| //| print("playing") -//| a.play() +//| a.play(wav) //| while a.playing: //| pass //| print("stopped") //| -STATIC mp_obj_t audioio_audioout_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { - mp_arg_check_num(n_args, n_kw, 2, 2, true); - mp_obj_t pin_obj = args[0]; - assert_pin(pin_obj, false); - const mcu_pin_obj_t *pin = MP_OBJ_TO_PTR(pin_obj); - // We explicitly don't check whether the pin is free because multiple - // AudioOuts may share it. +STATIC mp_obj_t audioio_audioout_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *pos_args) { + mp_arg_check_num(n_args, n_kw, 1, 2, true); + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, pos_args + n_args); + enum { ARG_left_channel, ARG_right_channel }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_left_channel, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_right_channel, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_rom_obj = mp_const_none} }, + }; + 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_obj_t left_channel_obj = args[ARG_left_channel].u_obj; + assert_pin(left_channel_obj, false); + const mcu_pin_obj_t *left_channel_pin = MP_OBJ_TO_PTR(left_channel_obj); + + mp_obj_t right_channel_obj = args[ARG_right_channel].u_obj; + const mcu_pin_obj_t *right_channel_pin = NULL; + if (right_channel_obj != mp_const_none) { + assert_pin(right_channel_obj, false); + right_channel_pin = MP_OBJ_TO_PTR(right_channel_obj); + } // create AudioOut object from the given pin audioio_audioout_obj_t *self = m_new_obj(audioio_audioout_obj_t); self->base.type = &audioio_audioout_type; - mp_buffer_info_t bufinfo; - if (MP_OBJ_IS_TYPE(args[1], &fatfs_type_fileio)) { - common_hal_audioio_audioout_construct_from_file(self, pin, MP_OBJ_TO_PTR(args[1])); - } else if (mp_get_buffer(args[1], &bufinfo, MP_BUFFER_READ)) { - uint8_t bytes_per_sample = 1; - if (bufinfo.typecode == 'H') { - bytes_per_sample = 2; - } else if (bufinfo.typecode != 'B' && bufinfo.typecode != BYTEARRAY_TYPECODE) { - mp_raise_ValueError("sample_source buffer must be a bytearray or array of type 'H' or 'B'"); - } - common_hal_audioio_audioout_construct_from_buffer(self, pin, ((uint16_t*)bufinfo.buf), bufinfo.len, bytes_per_sample); - } else { - mp_raise_TypeError("sample_source must be a file or bytes-like object"); - } + common_hal_audioio_audioout_construct(self, left_channel_pin, right_channel_pin); return MP_OBJ_FROM_PTR(self); } @@ -152,30 +150,38 @@ STATIC mp_obj_t audioio_audioout_obj___exit__(size_t n_args, const mp_obj_t *arg STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(audioio_audioout___exit___obj, 4, 4, audioio_audioout_obj___exit__); -//| .. method:: play(loop=False) +//| .. 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` or `audioio.RawSample`. +//| +//| The sample itself should consist of 16 bit samples. Microcontrollers with a lower output +//| resolution will use the highest order bits to output. For example, the SAMD21 has a 10 bit +//| DAC that ignores the lowest 6 bits when playing 16 bit samples. +//| STATIC mp_obj_t audioio_audioout_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_loop }; + enum { ARG_sample, ARG_loop }; static const mp_arg_t allowed_args[] = { - { MP_QSTR_loop, MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_sample, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_loop, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, }; audioio_audioout_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); raise_error_if_deinited(common_hal_audioio_audioout_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_audioout_play(self, args[ARG_loop].u_bool); + mp_obj_t sample = args[ARG_sample].u_obj; + common_hal_audioio_audioout_play(self, sample, args[ARG_loop].u_bool); + return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_KW(audioio_audioout_play_obj, 1, audioio_audioout_obj_play); //| .. method:: stop() //| -//| Stops playback of this sample. If another sample is playing instead, it -//| won't be stopped. +//| Stops playback. //| STATIC mp_obj_t audioio_audioout_obj_stop(mp_obj_t self_in) { audioio_audioout_obj_t *self = MP_OBJ_TO_PTR(self_in); @@ -187,7 +193,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(audioio_audioout_stop_obj, audioio_audioout_obj_stop); //| .. attribute:: playing //| -//| True when the audio sample is being output. (read-only) +//| True when an audio sample is being output. (read-only) //| STATIC mp_obj_t audioio_audioout_obj_get_playing(mp_obj_t self_in) { audioio_audioout_obj_t *self = MP_OBJ_TO_PTR(self_in); @@ -203,34 +209,6 @@ const mp_obj_property_t audioio_audioout_playing_obj = { (mp_obj_t)&mp_const_none_obj}, }; -//| .. attribute:: frequency -//| -//| 32 bit value that dictates how quickly samples are loaded into the DAC -//| in Hertz (cycles per second). When the sample is looped, this can change -//| the pitch output without changing the underlying sample. -//| -STATIC mp_obj_t audioio_audioout_obj_get_frequency(mp_obj_t self_in) { - audioio_audioout_obj_t *self = MP_OBJ_TO_PTR(self_in); - raise_error_if_deinited(common_hal_audioio_audioout_deinited(self)); - return MP_OBJ_NEW_SMALL_INT(common_hal_audioio_audioout_get_frequency(self)); -} -MP_DEFINE_CONST_FUN_OBJ_1(audioio_audioout_get_frequency_obj, audioio_audioout_obj_get_frequency); - -STATIC mp_obj_t audioio_audioout_obj_set_frequency(mp_obj_t self_in, mp_obj_t frequency) { - audioio_audioout_obj_t *self = MP_OBJ_TO_PTR(self_in); - raise_error_if_deinited(common_hal_audioio_audioout_deinited(self)); - common_hal_audioio_audioout_set_frequency(self, mp_obj_get_int(frequency)); - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_2(audioio_audioout_set_frequency_obj, audioio_audioout_obj_set_frequency); - -const mp_obj_property_t audioio_audioout_frequency_obj = { - .base.type = &mp_type_property, - .proxy = {(mp_obj_t)&audioio_audioout_get_frequency_obj, - (mp_obj_t)&audioio_audioout_set_frequency_obj, - (mp_obj_t)&mp_const_none_obj}, -}; - STATIC const mp_rom_map_elem_t audioio_audioout_locals_dict_table[] = { // Methods { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&audioio_audioout_deinit_obj) }, @@ -241,7 +219,6 @@ STATIC const mp_rom_map_elem_t audioio_audioout_locals_dict_table[] = { // Properties { MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&audioio_audioout_playing_obj) }, - { MP_ROM_QSTR(MP_QSTR_frequency), MP_ROM_PTR(&audioio_audioout_frequency_obj) }, }; STATIC MP_DEFINE_CONST_DICT(audioio_audioout_locals_dict, audioio_audioout_locals_dict_table); diff --git a/shared-bindings/audioio/AudioOut.h b/shared-bindings/audioio/AudioOut.h index befd4d35e..55958af79 100644 --- a/shared-bindings/audioio/AudioOut.h +++ b/shared-bindings/audioio/AudioOut.h @@ -29,21 +29,18 @@ #include "common-hal/audioio/AudioOut.h" #include "common-hal/microcontroller/Pin.h" -#include "extmod/vfs_fat_file.h" +#include "shared-bindings/audioio/RawSample.h" extern const mp_obj_type_t audioio_audioout_type; -void common_hal_audioio_audioout_construct_from_buffer(audioio_audioout_obj_t* self, - const mcu_pin_obj_t* pin, uint16_t* buffer, uint32_t len, uint8_t bytes_per_sample); -void common_hal_audioio_audioout_construct_from_file(audioio_audioout_obj_t* self, - const mcu_pin_obj_t* pin, pyb_file_obj_t* file); +// left_channel will always be non-NULL but right_channel may be for mono output. +void common_hal_audioio_audioout_construct(audioio_audioout_obj_t* self, + const mcu_pin_obj_t* left_channel, const mcu_pin_obj_t* right_channel); void common_hal_audioio_audioout_deinit(audioio_audioout_obj_t* self); bool common_hal_audioio_audioout_deinited(audioio_audioout_obj_t* self); -void common_hal_audioio_audioout_play(audioio_audioout_obj_t* self, bool loop); +void common_hal_audioio_audioout_play(audioio_audioout_obj_t* self, mp_obj_t sample, bool loop); void common_hal_audioio_audioout_stop(audioio_audioout_obj_t* self); bool common_hal_audioio_audioout_get_playing(audioio_audioout_obj_t* self); -uint32_t common_hal_audioio_audioout_get_frequency(audioio_audioout_obj_t* self); -void common_hal_audioio_audioout_set_frequency(audioio_audioout_obj_t* self, uint32_t frequency); #endif // MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOIO_AUDIOOUT_H diff --git a/shared-bindings/audioio/RawSample.c b/shared-bindings/audioio/RawSample.c new file mode 100644 index 000000000..3f8f71a1f --- /dev/null +++ b/shared-bindings/audioio/RawSample.c @@ -0,0 +1,183 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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 <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/audioio/AudioOut.h" +#include "shared-bindings/util.h" + +//| .. currentmodule:: audioio +//| +//| :class:`RawSample` -- A raw audio sample buffer +//| ======================================================== +//| +//| An in-memory sound sample +//| +//| .. class:: RawSample(buffer, *, channel_count=1, sample_rate=8000) +//| +//| Create a RawSample based on the given buffer of signed values. If channel_count is more than +//| 1 then each channel's samples should alternate. In other words, for a two channel buffer, the +//| first sample will be for channel 1, the second sample will be for channel two, the third for +//| channel 1 and so on. +//| +//| :param array buffer: An `array.array` with samples +//| :param int channel_count: The number of channels in the buffer +//| :param int sample_rate: The desired playback sample rate +//| +//| Simple 8ksps 440 Hz sin wave:: +//| +//| import audioio +//| import board +//| import array +//| import time +//| import math +//| +//| # Generate one period of sine wav. +//| length = 8000 // 440 +//| sine_wave = array.array("h", [0] * length) +//| for i in range(length): +//| sine_wave[i] = int(math.sin(math.pi * 2 * i / 18) * (2 ** 15)) +//| +//| dac = audioio.AudioOut(board.SPEAKER) +//| sine_wave = audioio.RawSample(sine_wave) +//| dac.play(sine_wave, loop=True) +//| time.sleep(1) +//| sample.stop() +//| +STATIC mp_obj_t audioio_rawsample_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *pos_args) { + mp_arg_check_num(n_args, n_kw, 1, 2, true); + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, pos_args + n_args); + enum { ARG_buffer, ARG_channel_count, ARG_sample_rate }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_buffer, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_channel_count, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 1 } }, + { 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); + + audioio_rawsample_obj_t *self = m_new_obj(audioio_rawsample_obj_t); + self->base.type = &audioio_rawsample_type; + mp_buffer_info_t bufinfo; + if (mp_get_buffer(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_READ)) { + uint8_t bytes_per_sample = 1; + bool signed_samples = bufinfo.typecode == 'b' || bufinfo.typecode == 'h'; + if (bufinfo.typecode == 'h' || bufinfo.typecode == 'H') { + bytes_per_sample = 2; + } else if (bufinfo.typecode != 'b' && bufinfo.typecode != 'B' && bufinfo.typecode != BYTEARRAY_TYPECODE) { + mp_raise_ValueError("sample_source buffer must be a bytearray or array of type 'h', 'H', 'b' or 'B'"); + } + common_hal_audioio_rawsample_construct(self, ((uint8_t*)bufinfo.buf), bufinfo.len, + bytes_per_sample, signed_samples, args[ARG_channel_count].u_int, + args[ARG_sample_rate].u_int); + } else { + mp_raise_TypeError("buffer must be a bytes-like object"); + } + + return MP_OBJ_FROM_PTR(self); +} + +//| .. method:: deinit() +//| +//| Deinitialises the AudioOut and releases any hardware resources for reuse. +//| +STATIC mp_obj_t audioio_rawsample_deinit(mp_obj_t self_in) { + audioio_rawsample_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_audioio_rawsample_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(audioio_rawsample_deinit_obj, audioio_rawsample_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_rawsample_obj___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_audioio_rawsample_deinit(args[0]); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(audioio_rawsample___exit___obj, 4, 4, audioio_rawsample_obj___exit__); + +//| .. attribute:: sample_rate +//| +//| 32 bit value that dictates how quickly samples are played in Hertz (cycles per second). +//| When the sample is looped, this can change the pitch output without changing the underlying +//| sample. This will not change the sample rate of any active playback. Call ``play`` again to +//| change it. +//| +STATIC mp_obj_t audioio_rawsample_obj_get_sample_rate(mp_obj_t self_in) { + audioio_rawsample_obj_t *self = MP_OBJ_TO_PTR(self_in); + raise_error_if_deinited(common_hal_audioio_rawsample_deinited(self)); + return MP_OBJ_NEW_SMALL_INT(common_hal_audioio_rawsample_get_sample_rate(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audioio_rawsample_get_sample_rate_obj, audioio_rawsample_obj_get_sample_rate); + +STATIC mp_obj_t audioio_rawsample_obj_set_sample_rate(mp_obj_t self_in, mp_obj_t sample_rate) { + audioio_rawsample_obj_t *self = MP_OBJ_TO_PTR(self_in); + raise_error_if_deinited(common_hal_audioio_rawsample_deinited(self)); + common_hal_audioio_rawsample_set_sample_rate(self, mp_obj_get_int(sample_rate)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(audioio_rawsample_set_sample_rate_obj, audioio_rawsample_obj_set_sample_rate); + +const mp_obj_property_t audioio_rawsample_sample_rate_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&audioio_rawsample_get_sample_rate_obj, + (mp_obj_t)&audioio_rawsample_set_sample_rate_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + +STATIC const mp_rom_map_elem_t audioio_rawsample_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&audioio_rawsample_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&audioio_rawsample___exit___obj) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_sample_rate), MP_ROM_PTR(&audioio_rawsample_sample_rate_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(audioio_rawsample_locals_dict, audioio_rawsample_locals_dict_table); + +const mp_obj_type_t audioio_rawsample_type = { + { &mp_type_type }, + .name = MP_QSTR_RawSample, + .make_new = audioio_rawsample_make_new, + .locals_dict = (mp_obj_dict_t*)&audioio_rawsample_locals_dict, +}; diff --git a/shared-bindings/audioio/RawSample.h b/shared-bindings/audioio/RawSample.h new file mode 100644 index 000000000..0d764b77c --- /dev/null +++ b/shared-bindings/audioio/RawSample.h @@ -0,0 +1,45 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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_RAWSAMPLE_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOIO_RAWSAMPLE_H + +#include "common-hal/audioio/AudioOut.h" +#include "common-hal/microcontroller/Pin.h" +#include "shared-module/audioio/RawSample.h" + +extern const mp_obj_type_t audioio_rawsample_type; + +void common_hal_audioio_rawsample_construct(audioio_rawsample_obj_t* self, + uint8_t* buffer, uint32_t len, uint8_t bytes_per_sample, bool samples_signed, + uint8_t channel_count, uint32_t sample_rate); + +void common_hal_audioio_rawsample_deinit(audioio_rawsample_obj_t* self); +bool common_hal_audioio_rawsample_deinited(audioio_rawsample_obj_t* self); +uint32_t common_hal_audioio_rawsample_get_sample_rate(audioio_rawsample_obj_t* self); +void common_hal_audioio_rawsample_set_sample_rate(audioio_rawsample_obj_t* self, uint32_t sample_rate); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOIO_RAWSAMPLE_H diff --git a/shared-bindings/audioio/WaveFile.c b/shared-bindings/audioio/WaveFile.c new file mode 100644 index 000000000..13c4d6b8f --- /dev/null +++ b/shared-bindings/audioio/WaveFile.c @@ -0,0 +1,158 @@ +/* + * 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 <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/audioio/WaveFile.h" +#include "shared-bindings/util.h" + +//| .. currentmodule:: audioio +//| +//| :class:`WaveFile` -- Load a wave file for audio playback +//| ======================================================== +//| +//| A .wav file prepped for audio playback. Only mono and stereo files are supported. Samples must +//| be 8 bit unsigned or 16 bit signed. +//| +//| .. class:: WaveFile(filename) +//| +//| Load a .wav file for playback with `audioio.AudioOut` or `audiobusio.I2SOut`. +//| +//| :param bytes-like file: Already opened wave file +//| +//| Playing a wave file from flash:: +//| +//| import board +//| import audioio +//| import digitalio +//| +//| # Required for CircuitPlayground Express +//| speaker_enable = digitalio.DigitalInOut(board.SPEAKER_ENABLE) +//| speaker_enable.switch_to_output(value=True) +//| +//| data = open("cplay-5.1-16bit-16khz.wav", "rb") +//| wav = audioio.WaveFile(data) +//| a = audioio.AudioOut(board.A0) +//| +//| print("playing") +//| a.play(wav) +//| while a.playing: +//| pass +//| print("stopped") +//| +STATIC mp_obj_t audioio_wavefile_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 1, 1, true); + + audioio_wavefile_obj_t *self = m_new_obj(audioio_wavefile_obj_t); + self->base.type = &audioio_wavefile_type; + if (MP_OBJ_IS_TYPE(args[0], &fatfs_type_fileio)) { + common_hal_audioio_wavefile_construct(self, MP_OBJ_TO_PTR(args[0])); + } else { + mp_raise_TypeError("file must be a file opened in byte mode"); + } + + return MP_OBJ_FROM_PTR(self); +} + +//| .. method:: deinit() +//| +//| Deinitialises the WaveFile and releases all memory resources for reuse. +//| +STATIC mp_obj_t audioio_wavefile_deinit(mp_obj_t self_in) { + audioio_wavefile_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_audioio_wavefile_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(audioio_wavefile_deinit_obj, audioio_wavefile_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_wavefile_obj___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_audioio_wavefile_deinit(args[0]); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(audioio_wavefile___exit___obj, 4, 4, audioio_wavefile_obj___exit__); + +//| .. attribute:: sample_rate +//| +//| 32 bit value that dictates how quickly samples are loaded into the DAC +//| in Hertz (cycles per second). When the sample is looped, this can change +//| the pitch output without changing the underlying sample. +//| +STATIC mp_obj_t audioio_wavefile_obj_get_sample_rate(mp_obj_t self_in) { + audioio_wavefile_obj_t *self = MP_OBJ_TO_PTR(self_in); + raise_error_if_deinited(common_hal_audioio_wavefile_deinited(self)); + return MP_OBJ_NEW_SMALL_INT(common_hal_audioio_wavefile_get_sample_rate(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audioio_wavefile_get_sample_rate_obj, audioio_wavefile_obj_get_sample_rate); + +STATIC mp_obj_t audioio_wavefile_obj_set_sample_rate(mp_obj_t self_in, mp_obj_t sample_rate) { + audioio_wavefile_obj_t *self = MP_OBJ_TO_PTR(self_in); + raise_error_if_deinited(common_hal_audioio_wavefile_deinited(self)); + common_hal_audioio_wavefile_set_sample_rate(self, mp_obj_get_int(sample_rate)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(audioio_wavefile_set_sample_rate_obj, audioio_wavefile_obj_set_sample_rate); + +const mp_obj_property_t audioio_wavefile_sample_rate_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&audioio_wavefile_get_sample_rate_obj, + (mp_obj_t)&audioio_wavefile_set_sample_rate_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + +STATIC const mp_rom_map_elem_t audioio_wavefile_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&audioio_wavefile_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&audioio_wavefile___exit___obj) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_sample_rate), MP_ROM_PTR(&audioio_wavefile_sample_rate_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(audioio_wavefile_locals_dict, audioio_wavefile_locals_dict_table); + +const mp_obj_type_t audioio_wavefile_type = { + { &mp_type_type }, + .name = MP_QSTR_WaveFile, + .make_new = audioio_wavefile_make_new, + .locals_dict = (mp_obj_dict_t*)&audioio_wavefile_locals_dict, +}; diff --git a/shared-bindings/audioio/WaveFile.h b/shared-bindings/audioio/WaveFile.h new file mode 100644 index 000000000..b6838ce19 --- /dev/null +++ b/shared-bindings/audioio/WaveFile.h @@ -0,0 +1,44 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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_WAVEFILE_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOIO_WAVEFILE_H + +#include "common-hal/audioio/AudioOut.h" +#include "common-hal/microcontroller/Pin.h" +#include "extmod/vfs_fat_file.h" + +extern const mp_obj_type_t audioio_wavefile_type; + +void common_hal_audioio_wavefile_construct(audioio_wavefile_obj_t* self, + pyb_file_obj_t* file); + +void common_hal_audioio_wavefile_deinit(audioio_wavefile_obj_t* self); +bool common_hal_audioio_wavefile_deinited(audioio_wavefile_obj_t* self); +uint32_t common_hal_audioio_wavefile_get_sample_rate(audioio_wavefile_obj_t* self); +void common_hal_audioio_wavefile_set_sample_rate(audioio_wavefile_obj_t* self, uint32_t sample_rate); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOIO_WAVEFILE_H diff --git a/shared-bindings/audioio/__init__.c b/shared-bindings/audioio/__init__.c index 07305a45a..8a00b43d8 100644 --- a/shared-bindings/audioio/__init__.c +++ b/shared-bindings/audioio/__init__.c @@ -32,6 +32,7 @@ #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/audioio/__init__.h" #include "shared-bindings/audioio/AudioOut.h" +#include "shared-bindings/audioio/WaveFile.h" //| :mod:`audioio` --- Support for audio input and output //| ====================================================== @@ -48,6 +49,8 @@ //| :maxdepth: 3 //| //| AudioOut +//| RawSample +//| WaveFile //| //| All classes change hardware state and should be deinitialized when they //| are no longer needed if the program continues after use. To do so, either @@ -58,6 +61,8 @@ 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) }, + { MP_ROM_QSTR(MP_QSTR_RawSample), MP_ROM_PTR(&audioio_rawsample_type) }, + { MP_ROM_QSTR(MP_QSTR_WaveFile), MP_ROM_PTR(&audioio_wavefile_type) }, }; STATIC MP_DEFINE_CONST_DICT(audioio_module_globals, audioio_module_globals_table); |
