Introduce audioio.Mixer which can mix multiple audio samples
to produce a single sample. Only works with 16 bit samples on the M4. Fixes #987
This commit is contained in:
parent
7b95818c4f
commit
76008ce304
|
@ -407,6 +407,8 @@ ifneq ($(CHIP_VARIANT),SAMR21G18A)
|
|||
audioio/__init__.c \
|
||||
audioio/AudioOut.c
|
||||
SRC_SHARED_MODULE += \
|
||||
audioio/__init__.c \
|
||||
audioio/Mixer.c \
|
||||
audioio/RawSample.c \
|
||||
audioio/WaveFile.c
|
||||
endif
|
||||
|
|
|
@ -40,82 +40,6 @@ static audio_dma_t* audio_dma_state[AUDIO_DMA_CHANNEL_COUNT];
|
|||
// This cannot be in audio_dma_state because it's volatile.
|
||||
static volatile bool audio_dma_pending[AUDIO_DMA_CHANNEL_COUNT];
|
||||
|
||||
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);
|
||||
return sample->sample_rate;
|
||||
}
|
||||
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;
|
||||
}
|
||||
return 16000;
|
||||
}
|
||||
|
||||
uint8_t audiosample_bits_per_sample(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);
|
||||
return sample->bits_per_sample;
|
||||
}
|
||||
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;
|
||||
}
|
||||
return 8;
|
||||
}
|
||||
|
||||
uint8_t audiosample_channel_count(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);
|
||||
return sample->channel_count;
|
||||
}
|
||||
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;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void audiosample_reset_buffer(mp_obj_t sample_obj, bool single_channel, uint8_t audio_channel) {
|
||||
if (MP_OBJ_IS_TYPE(sample_obj, &audioio_rawsample_type)) {
|
||||
audioio_rawsample_obj_t* sample = MP_OBJ_TO_PTR(sample_obj);
|
||||
audioio_rawsample_reset_buffer(sample, single_channel, audio_channel);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
static audioio_get_buffer_result_t audiosample_get_buffer(mp_obj_t sample_obj,
|
||||
bool single_channel,
|
||||
uint8_t channel,
|
||||
uint8_t** buffer, uint32_t* buffer_length) {
|
||||
if (MP_OBJ_IS_TYPE(sample_obj, &audioio_rawsample_type)) {
|
||||
audioio_rawsample_obj_t* sample = MP_OBJ_TO_PTR(sample_obj);
|
||||
return audioio_rawsample_get_buffer(sample, single_channel, channel, buffer, buffer_length);
|
||||
}
|
||||
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);
|
||||
}
|
||||
return GET_BUFFER_DONE;
|
||||
}
|
||||
|
||||
static void audiosample_get_buffer_structure(mp_obj_t sample_obj, bool single_channel,
|
||||
bool* single_buffer, bool* samples_signed,
|
||||
uint32_t* max_buffer_length, uint8_t* spacing) {
|
||||
if (MP_OBJ_IS_TYPE(sample_obj, &audioio_rawsample_type)) {
|
||||
audioio_rawsample_obj_t* sample = MP_OBJ_TO_PTR(sample_obj);
|
||||
audioio_rawsample_get_buffer_structure(sample, single_channel, single_buffer,
|
||||
samples_signed, max_buffer_length, spacing);
|
||||
} else if (MP_OBJ_IS_TYPE(sample_obj, &audioio_wavefile_type)) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t find_free_audio_dma_channel(void) {
|
||||
uint8_t channel;
|
||||
for (channel = 0; channel < AUDIO_DMA_CHANNEL_COUNT; channel++) {
|
||||
|
|
|
@ -0,0 +1,249 @@
|
|||
/*
|
||||
* 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/audioio/Mixer.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/audioio/RawSample.h"
|
||||
#include "shared-bindings/util.h"
|
||||
#include "supervisor/shared/translate.h"
|
||||
|
||||
//| .. currentmodule:: audioio
|
||||
//|
|
||||
//| :class:`Mixer` -- Mixes one or more audio samples together
|
||||
//| ===========================================================
|
||||
//|
|
||||
//| Mixer mixes multiple samples into one sample.
|
||||
//|
|
||||
//| .. class:: Mixer(channel_count=2, buffer_size=1024)
|
||||
//|
|
||||
//| Create a Mixer object that can mix multiple channels with the same sample rate.
|
||||
//|
|
||||
//| :param int channel_count: The maximum number of samples to mix at once
|
||||
//| :param int buffer_size: The total size in bytes of the buffers to mix into
|
||||
//|
|
||||
//| 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)
|
||||
//|
|
||||
//| 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)
|
||||
//| a = audioio.AudioOut(board.A0)
|
||||
//|
|
||||
//| print("playing")
|
||||
//| a.play(mixer)
|
||||
//| mixer.play(music, voice=0)
|
||||
//| while mixer.playing:
|
||||
//| mixer.play(drum, voice=1)
|
||||
//| time.sleep(1)
|
||||
//| print("stopped")
|
||||
//|
|
||||
STATIC mp_obj_t audioio_mixer_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, 0, 2, true);
|
||||
mp_map_t kw_args;
|
||||
mp_map_init_fixed_table(&kw_args, n_kw, pos_args + n_args);
|
||||
enum { ARG_voice_count, ARG_buffer_size, ARG_channel_count, ARG_bits_per_sample, ARG_samples_signed, ARG_sample_rate };
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_voice_count, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 2} },
|
||||
{ MP_QSTR_buffer_size, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 1024} },
|
||||
{ MP_QSTR_channel_count, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 2} },
|
||||
{ MP_QSTR_bits_per_sample, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 16} },
|
||||
{ MP_QSTR_samples_signed, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = true} },
|
||||
{ MP_QSTR_sample_rate, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 8000} },
|
||||
};
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, &kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
mp_int_t voice_count = args[ARG_voice_count].u_int;
|
||||
if (voice_count < 1 || voice_count > 255) {
|
||||
mp_raise_ValueError(translate("Invalid voice count"));
|
||||
}
|
||||
|
||||
mp_int_t channel_count = args[ARG_channel_count].u_int;
|
||||
if (channel_count < 1 || channel_count > 2) {
|
||||
mp_raise_ValueError(translate("Invalid channel count"));
|
||||
}
|
||||
mp_int_t sample_rate = args[ARG_sample_rate].u_int;
|
||||
if (sample_rate < 1) {
|
||||
mp_raise_ValueError(translate("Sample rate must be positive"));
|
||||
}
|
||||
mp_int_t bits_per_sample = args[ARG_bits_per_sample].u_int;
|
||||
if (bits_per_sample != 8 && bits_per_sample != 16) {
|
||||
mp_raise_ValueError(translate("bits_per_sample must be 8 or 16"));
|
||||
}
|
||||
audioio_mixer_obj_t *self = m_new_obj_var(audioio_mixer_obj_t, audioio_mixer_voice_t, voice_count);
|
||||
self->base.type = &audioio_mixer_type;
|
||||
common_hal_audioio_mixer_construct(self, voice_count, args[ARG_buffer_size].u_int, bits_per_sample, args[ARG_samples_signed].u_bool, channel_count, sample_rate);
|
||||
|
||||
return MP_OBJ_FROM_PTR(self);
|
||||
}
|
||||
|
||||
//| .. 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);
|
||||
|
||||
//| .. 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__);
|
||||
|
||||
|
||||
//| .. 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`.
|
||||
//|
|
||||
//| If other samples are already playing, the encodings must match.
|
||||
//|
|
||||
STATIC mp_obj_t audioio_mixer_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
enum { ARG_sample, ARG_voice, ARG_loop };
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_sample, MP_ARG_OBJ | MP_ARG_REQUIRED },
|
||||
{ MP_QSTR_voice, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} },
|
||||
{ MP_QSTR_loop, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} },
|
||||
};
|
||||
audioio_mixer_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
|
||||
raise_error_if_deinited(common_hal_audioio_mixer_deinited(self));
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
mp_obj_t sample = args[ARG_sample].u_obj;
|
||||
common_hal_audioio_mixer_play(self, sample, args[ARG_voice].u_int, args[ARG_loop].u_bool);
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(audioio_mixer_play_obj, 1, audioio_mixer_obj_play);
|
||||
|
||||
//| .. method:: stop(voice=0)
|
||||
//|
|
||||
//| Stops playback and resets to the start of the sample on the given channel.
|
||||
//|
|
||||
STATIC mp_obj_t audioio_mixer_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 | MP_ARG_KW_ONLY, {.u_int = 0} },
|
||||
};
|
||||
audioio_mixer_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
|
||||
raise_error_if_deinited(common_hal_audioio_mixer_deinited(self));
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
common_hal_audioio_mixer_stop(self, args[ARG_voice].u_int);
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(audioio_mixer_stop_obj, 1, audioio_mixer_obj_stop);
|
||||
|
||||
//| .. attribute:: playing
|
||||
//|
|
||||
//| True when an audio sample is being output even if `paused`. (read-only)
|
||||
//|
|
||||
STATIC mp_obj_t audioio_mixer_obj_get_playing(mp_obj_t self_in) {
|
||||
audioio_mixer_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
raise_error_if_deinited(common_hal_audioio_mixer_deinited(self));
|
||||
return mp_obj_new_bool(common_hal_audioio_mixer_get_playing(self));
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(audioio_mixer_get_playing_obj, audioio_mixer_obj_get_playing);
|
||||
|
||||
const mp_obj_property_t audioio_mixer_playing_obj = {
|
||||
.base.type = &mp_type_property,
|
||||
.proxy = {(mp_obj_t)&audioio_mixer_get_playing_obj,
|
||||
(mp_obj_t)&mp_const_none_obj,
|
||||
(mp_obj_t)&mp_const_none_obj},
|
||||
};
|
||||
|
||||
//| .. attribute:: sample_rate
|
||||
//|
|
||||
//| 32 bit value that dictates how quickly samples are played in Hertz (cycles per second).
|
||||
//|
|
||||
STATIC mp_obj_t audioio_mixer_obj_get_sample_rate(mp_obj_t self_in) {
|
||||
audioio_mixer_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
raise_error_if_deinited(common_hal_audioio_mixer_deinited(self));
|
||||
return MP_OBJ_NEW_SMALL_INT(common_hal_audioio_mixer_get_sample_rate(self));
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(audioio_mixer_get_sample_rate_obj, audioio_mixer_obj_get_sample_rate);
|
||||
|
||||
|
||||
const mp_obj_property_t audioio_mixer_sample_rate_obj = {
|
||||
.base.type = &mp_type_property,
|
||||
.proxy = {(mp_obj_t)&audioio_mixer_get_sample_rate_obj,
|
||||
(mp_obj_t)&mp_const_none_obj,
|
||||
(mp_obj_t)&mp_const_none_obj},
|
||||
};
|
||||
|
||||
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) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_play), MP_ROM_PTR(&audioio_mixer_play_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&audioio_mixer_stop_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) },
|
||||
};
|
||||
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,
|
||||
};
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* 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/audioio/Mixer.h"
|
||||
#include "shared-bindings/audioio/RawSample.h"
|
||||
|
||||
extern const mp_obj_type_t audioio_mixer_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);
|
||||
void common_hal_audioio_mixer_play(audioio_mixer_obj_t* self, mp_obj_t sample, uint8_t voice, bool loop);
|
||||
void common_hal_audioio_mixer_stop(audioio_mixer_obj_t* self, uint8_t voice);
|
||||
|
||||
bool common_hal_audioio_mixer_get_playing(audioio_mixer_obj_t* self);
|
||||
uint32_t common_hal_audioio_mixer_get_sample_rate(audioio_mixer_obj_t* self);
|
||||
|
||||
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOIO_MIXER_H
|
|
@ -32,6 +32,8 @@
|
|||
#include "shared-bindings/microcontroller/Pin.h"
|
||||
#include "shared-bindings/audioio/__init__.h"
|
||||
#include "shared-bindings/audioio/AudioOut.h"
|
||||
#include "shared-bindings/audioio/Mixer.h"
|
||||
#include "shared-bindings/audioio/RawSample.h"
|
||||
#include "shared-bindings/audioio/WaveFile.h"
|
||||
|
||||
//| :mod:`audioio` --- Support for audio input and output
|
||||
|
@ -49,6 +51,7 @@
|
|||
//| :maxdepth: 3
|
||||
//|
|
||||
//| AudioOut
|
||||
//| Mixer
|
||||
//| RawSample
|
||||
//| WaveFile
|
||||
//|
|
||||
|
@ -61,6 +64,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) },
|
||||
{ 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) },
|
||||
};
|
||||
|
|
|
@ -0,0 +1,338 @@
|
|||
/*
|
||||
* 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/audioio/Mixer.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "py/runtime.h"
|
||||
#include "shared-module/audioio/__init__.h"
|
||||
#include "shared-module/audioio/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;
|
||||
|
||||
for (uint8_t i = 0; i < self->voice_count; i++) {
|
||||
self->voice[i].sample = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void common_hal_audioio_mixer_play(audioio_mixer_obj_t* self, mp_obj_t sample, uint8_t v, bool loop) {
|
||||
if (v >= self->voice_count) {
|
||||
mp_raise_ValueError(translate("Voice index too high"));
|
||||
}
|
||||
if (audiosample_sample_rate(sample) != self->sample_rate) {
|
||||
mp_raise_ValueError(translate("The sample's sample rate does not match the mixer's"));
|
||||
}
|
||||
if (audiosample_channel_count(sample) != self->channel_count) {
|
||||
mp_raise_ValueError(translate("The sample's channel count does not match the mixer's"));
|
||||
}
|
||||
if (audiosample_bits_per_sample(sample) != self->bits_per_sample) {
|
||||
mp_raise_ValueError(translate("The sample's bits_per_sample does not match the mixer's"));
|
||||
}
|
||||
bool single_buffer;
|
||||
bool samples_signed;
|
||||
uint32_t max_buffer_length;
|
||||
uint8_t spacing;
|
||||
audiosample_get_buffer_structure(sample, false, &single_buffer, &samples_signed,
|
||||
&max_buffer_length, &spacing);
|
||||
if (samples_signed != self->samples_signed) {
|
||||
mp_raise_ValueError(translate("The sample's signedness does not match the mixer's"));
|
||||
}
|
||||
audioio_mixer_voice_t* voice = &self->voice[v];
|
||||
voice->sample = sample;
|
||||
voice->loop = loop;
|
||||
|
||||
audiosample_reset_buffer(sample, false, 0);
|
||||
audioio_get_buffer_result_t result = audiosample_get_buffer(sample, false, 0, (uint8_t**) &voice->remaining_buffer, &voice->buffer_length);
|
||||
// Track length in terms of words.
|
||||
voice->buffer_length /= sizeof(uint32_t);
|
||||
voice->more_data = result == GET_BUFFER_MORE_DATA;
|
||||
}
|
||||
|
||||
void common_hal_audioio_mixer_stop(audioio_mixer_obj_t* self, uint8_t voice) {
|
||||
self->voice[voice].sample = NULL;
|
||||
}
|
||||
|
||||
bool common_hal_audioio_mixer_get_playing(audioio_mixer_obj_t* self) {
|
||||
for (int32_t v = 0; v < self->voice_count; v++) {
|
||||
if (self->voice[v].sample != NULL) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void audioio_mixer_reset_buffer(audioio_mixer_obj_t* self,
|
||||
bool single_channel,
|
||||
uint8_t channel) {
|
||||
for (int32_t i = 0; i < self->voice_count; i++) {
|
||||
self->voice[i].sample = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
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) * i);
|
||||
int8_t bi = b >> (sizeof(int8_t) * i);
|
||||
int32_t intermediate = (int32_t) ai + bi;
|
||||
if (intermediate > CHAR_MAX) {
|
||||
intermediate = CHAR_MAX;
|
||||
} else if (intermediate < CHAR_MIN) {
|
||||
intermediate = CHAR_MIN;
|
||||
}
|
||||
result |= ((int8_t) intermediate) >> (sizeof(int8_t) * 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) * i);
|
||||
uint8_t bi = b >> (sizeof(uint8_t) * i);
|
||||
int32_t intermediate = (int32_t) ai + bi;
|
||||
if (intermediate > UCHAR_MAX) {
|
||||
intermediate = UCHAR_MAX;
|
||||
}
|
||||
result |= ((uint8_t) intermediate) >> (sizeof(uint8_t) * 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) * i);
|
||||
int16_t bi = b >> (sizeof(int16_t) * i);
|
||||
int32_t intermediate = (int32_t) ai + bi;
|
||||
if (intermediate > SHRT_MAX) {
|
||||
intermediate = SHRT_MAX;
|
||||
} else if (intermediate < SHRT_MIN) {
|
||||
intermediate = SHRT_MIN;
|
||||
}
|
||||
result |= ((int16_t) intermediate) >> (sizeof(int16_t) * 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++) {
|
||||
uint16_t ai = a >> (sizeof(uint16_t) * i);
|
||||
uint16_t bi = b >> (sizeof(uint16_t) * i);
|
||||
uint32_t intermediate = (uint32_t) ai + bi;
|
||||
if (intermediate > USHRT_MAX) {
|
||||
intermediate = USHRT_MAX;
|
||||
}
|
||||
result |= ((uint16_t) intermediate) >> (sizeof(int16_t) * 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_mixer_voice_t* voice = &self->voice[v];
|
||||
if (voice->sample == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t j = 0;
|
||||
for (uint32_t i = 0; i < self->len / sizeof(uint32_t); i++) {
|
||||
if (j >= voice->buffer_length) {
|
||||
if (!voice->more_data) {
|
||||
if (voice->loop) {
|
||||
audiosample_reset_buffer(voice->sample, false, 0);
|
||||
} else {
|
||||
voice->sample = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 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 = voice->remaining_buffer[j];
|
||||
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;
|
||||
}
|
||||
|
||||
// No voice is active so zero out the signal.
|
||||
if (!voices_active) {
|
||||
uint32_t zero = 0;
|
||||
if (!self->samples_signed) {
|
||||
if (self->bits_per_sample == 8) {
|
||||
zero = 0x7f7f7f7f;
|
||||
} else {
|
||||
zero = 0x7fff7fff;
|
||||
}
|
||||
}
|
||||
for (uint32_t i = 0; i < self->len / sizeof(uint32_t); i++) {
|
||||
word_buffer[i] = zero;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* 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 "shared-module/audioio/__init__.h"
|
||||
|
||||
typedef struct {
|
||||
mp_obj_t sample;
|
||||
bool loop;
|
||||
bool more_data;
|
||||
uint32_t* remaining_buffer;
|
||||
uint32_t buffer_length;
|
||||
} audioio_mixer_voice_t;
|
||||
|
||||
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;
|
||||
audioio_mixer_voice_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
|
|
@ -200,13 +200,29 @@ audioio_get_buffer_result_t audioio_wavefile_get_buffer(audioio_wavefile_obj_t*
|
|||
if (f_read(&self->file->fp, *buffer, num_bytes_to_load, &length_read) != FR_OK) {
|
||||
return GET_BUFFER_ERROR;
|
||||
}
|
||||
self->bytes_remaining -= length_read;
|
||||
// Pad the last buffer to word align it.
|
||||
if (self->bytes_remaining == 0 && length_read % sizeof(uint32_t) != 0) {
|
||||
uint32_t pad = length_read % sizeof(uint32_t);
|
||||
length_read += pad;
|
||||
if (self->bits_per_sample == 8) {
|
||||
for (uint32_t i = 0; i < pad; i++) {
|
||||
((uint8_t*) (*buffer))[length_read / sizeof(uint8_t) - i - 1] = 0x80;
|
||||
}
|
||||
} else if (self->bits_per_sample == 16) {
|
||||
// We know the buffer is aligned because we allocated it onto the heap ourselves.
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wcast-align"
|
||||
((int16_t*) (*buffer))[length_read / sizeof(int16_t) - 1] = 0;
|
||||
#pragma GCC diagnostic pop
|
||||
}
|
||||
}
|
||||
*buffer_length = length_read;
|
||||
if (self->buffer_index % 2 == 1) {
|
||||
self->second_buffer_length = length_read;
|
||||
} else {
|
||||
self->buffer_length = length_read;
|
||||
}
|
||||
self->bytes_remaining -= length_read;
|
||||
self->buffer_index += 1;
|
||||
self->read_count += 1;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* This file is part of the MicroPython 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-module/audioio/__init__.h"
|
||||
|
||||
#include "py/obj.h"
|
||||
#include "shared-bindings/audioio/Mixer.h"
|
||||
#include "shared-bindings/audioio/RawSample.h"
|
||||
#include "shared-bindings/audioio/WaveFile.h"
|
||||
#include "shared-module/audioio/Mixer.h"
|
||||
#include "shared-module/audioio/RawSample.h"
|
||||
#include "shared-module/audioio/WaveFile.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);
|
||||
return sample->sample_rate;
|
||||
} 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);
|
||||
return mixer->sample_rate;
|
||||
}
|
||||
return 16000;
|
||||
}
|
||||
|
||||
uint8_t audiosample_bits_per_sample(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);
|
||||
return sample->bits_per_sample;
|
||||
} 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);
|
||||
return mixer->bits_per_sample;
|
||||
}
|
||||
return 8;
|
||||
}
|
||||
|
||||
uint8_t audiosample_channel_count(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);
|
||||
return sample->channel_count;
|
||||
} 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);
|
||||
return mixer->channel_count;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void audiosample_reset_buffer(mp_obj_t sample_obj, bool single_channel, uint8_t audio_channel) {
|
||||
if (MP_OBJ_IS_TYPE(sample_obj, &audioio_rawsample_type)) {
|
||||
audioio_rawsample_obj_t* sample = MP_OBJ_TO_PTR(sample_obj);
|
||||
audioio_rawsample_reset_buffer(sample, single_channel, audio_channel);
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
||||
audioio_get_buffer_result_t audiosample_get_buffer(mp_obj_t sample_obj,
|
||||
bool single_channel,
|
||||
uint8_t channel,
|
||||
uint8_t** buffer, uint32_t* buffer_length) {
|
||||
if (MP_OBJ_IS_TYPE(sample_obj, &audioio_rawsample_type)) {
|
||||
audioio_rawsample_obj_t* sample = MP_OBJ_TO_PTR(sample_obj);
|
||||
return audioio_rawsample_get_buffer(sample, single_channel, channel, buffer, buffer_length);
|
||||
} 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);
|
||||
}
|
||||
return GET_BUFFER_DONE;
|
||||
}
|
||||
|
||||
void audiosample_get_buffer_structure(mp_obj_t sample_obj, bool single_channel,
|
||||
bool* single_buffer, bool* samples_signed,
|
||||
uint32_t* max_buffer_length, uint8_t* spacing) {
|
||||
if (MP_OBJ_IS_TYPE(sample_obj, &audioio_rawsample_type)) {
|
||||
audioio_rawsample_obj_t* sample = MP_OBJ_TO_PTR(sample_obj);
|
||||
audioio_rawsample_get_buffer_structure(sample, single_channel, single_buffer,
|
||||
samples_signed, max_buffer_length, spacing);
|
||||
} else if (MP_OBJ_IS_TYPE(sample_obj, &audioio_wavefile_type)) {
|
||||
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,
|
||||
max_buffer_length, spacing);
|
||||
}
|
||||
}
|
|
@ -27,10 +27,27 @@
|
|||
#ifndef MICROPY_INCLUDED_SHARED_MODULE_AUDIOIO__INIT__H
|
||||
#define MICROPY_INCLUDED_SHARED_MODULE_AUDIOIO__INIT__H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "py/obj.h"
|
||||
|
||||
typedef enum {
|
||||
GET_BUFFER_DONE, // No more data to read
|
||||
GET_BUFFER_MORE_DATA, // More data to read.
|
||||
GET_BUFFER_ERROR, // Error while reading data.
|
||||
} audioio_get_buffer_result_t;
|
||||
|
||||
uint32_t audiosample_sample_rate(mp_obj_t sample_obj);
|
||||
uint8_t audiosample_bits_per_sample(mp_obj_t sample_obj);
|
||||
uint8_t audiosample_channel_count(mp_obj_t sample_obj);
|
||||
void audiosample_reset_buffer(mp_obj_t sample_obj, bool single_channel, uint8_t audio_channel);
|
||||
audioio_get_buffer_result_t audiosample_get_buffer(mp_obj_t sample_obj,
|
||||
bool single_channel,
|
||||
uint8_t channel,
|
||||
uint8_t** buffer, uint32_t* buffer_length);
|
||||
void audiosample_get_buffer_structure(mp_obj_t sample_obj, bool single_channel,
|
||||
bool* single_buffer, bool* samples_signed,
|
||||
uint32_t* max_buffer_length, uint8_t* spacing);
|
||||
|
||||
#endif // MICROPY_INCLUDED_SHARED_MODULE_AUDIOIO__INIT__H
|
||||
|
|
|
@ -68,7 +68,7 @@ bool displayio_sprite_get_pixel(displayio_sprite_t *self, int16_t x, int16_t y,
|
|||
if (y < 0 || y >= self->height || x >= self->width || x < 0) {
|
||||
return false;
|
||||
}
|
||||
uint32_t value;
|
||||
uint32_t value = 0;
|
||||
if (MP_OBJ_IS_TYPE(self->bitmap, &displayio_bitmap_type)) {
|
||||
value = common_hal_displayio_bitmap_get_pixel(self->bitmap, x, y);
|
||||
} else if (MP_OBJ_IS_TYPE(self->bitmap, &displayio_ondiskbitmap_type)) {
|
||||
|
|
Loading…
Reference in New Issue