Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Add MonoWaveFile with adjustable playback speed
  • Loading branch information
Sundström Valter committed Aug 25, 2024
commit 179d284f3e0d754be6965ce8d7aabcf6ff033cb1
1 change: 1 addition & 0 deletions py/circuitpy_defns.mk
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,7 @@ SRC_SHARED_MODULE_ALL = \
atexit/__init__.c \
audiocore/RawSample.c \
audiocore/WaveFile.c \
audiocore/MonoWaveFile.c \
audiocore/__init__.c \
audioio/__init__.c \
audiomixer/Mixer.c \
Expand Down
203 changes: 203 additions & 0 deletions shared-bindings/audiocore/MonoWaveFile.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2018 Scott Shawcroft for Adafruit Industries
//
// SPDX-License-Identifier: MIT

#include <stdint.h>

#include "shared/runtime/context_manager_helpers.h"
#include "py/objproperty.h"
#include "py/runtime.h"
#include "shared-bindings/audiocore/MonoWaveFile.h"
#include "shared-bindings/util.h"
#include "extmod/vfs_posix.h"

//| class MonoWaveFile:
//| """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. If a buffer is provided, it will be used instead of allocating
//| an internal buffer, which can prevent memory fragmentation."""
//|
//| def __init__(self, file: Union[str, typing.BinaryIO], buffer: WriteableBuffer) -> None:
//| """Load a .wav file for playback with `audioio.AudioOut` or `audiobusio.I2SOut`.
//|
//| :param Union[str, typing.BinaryIO] file: The name of a wave file (preferred) or an already opened wave file
//| :param ~circuitpython_typing.WriteableBuffer buffer: Optional pre-allocated buffer,
//| that will be split in half and used for double-buffering of the data.
//| The buffer must be 8 to 1024 bytes long.
//| If not provided, two 256 byte buffers are initially allocated internally.
//|
//| Playing a wave file from flash::
//|
//| import board
//| import audiocore
//| import audioio
//| import digitalio
//|
//| # Required for CircuitPlayground Express
//| speaker_enable = digitalio.DigitalInOut(board.SPEAKER_ENABLE)
//| speaker_enable.switch_to_output(value=True)
//|
//| wav = audiocore.WaveFile("cplay-5.1-16bit-16khz.wav")
//| a = audioio.AudioOut(board.A0)
//|
//| print("playing")
//| a.play(wav)
//| while a.playing:
//| pass
//| print("stopped")
//| """
//| ...
static mp_obj_t audioio_monowavefile_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, 2, false);
mp_obj_t arg = args[0];

if (mp_obj_is_str(arg)) {
arg = mp_call_function_2(MP_OBJ_FROM_PTR(&mp_builtin_open_obj), arg, MP_ROM_QSTR(MP_QSTR_rb));
}

audioio_monowavefile_obj_t *self = mp_obj_malloc(audioio_monowavefile_obj_t, &audioio_monowavefile_type);
if (!mp_obj_is_type(arg, &mp_type_vfs_fat_fileio)) {
mp_raise_TypeError(MP_ERROR_TEXT("file must be a file opened in byte mode"));
}
uint8_t *buffer = NULL;
size_t buffer_size = 0;
if (n_args >= 2) {
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_WRITE);
buffer = bufinfo.buf;
buffer_size = mp_arg_validate_length_range(bufinfo.len, 8, 1024, MP_QSTR_buffer);
}
common_hal_audioio_monowavefile_construct(self, MP_OBJ_TO_PTR(arg),
buffer, buffer_size);

return MP_OBJ_FROM_PTR(self);
}

//| def deinit(self) -> None:
//| """Deinitialises the WaveFile and releases all memory resources for reuse."""
//| ...
static mp_obj_t audioio_monowavefile_deinit(mp_obj_t self_in) {
audioio_monowavefile_obj_t *self = MP_OBJ_TO_PTR(self_in);
common_hal_audioio_monowavefile_deinit(self);
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_1(audioio_monowavefile_deinit_obj, audioio_monowavefile_deinit);

static void check_for_deinit(audioio_monowavefile_obj_t *self) {
if (common_hal_audioio_monowavefile_deinited(self)) {
raise_deinited_error();
}
}

//| def __enter__(self) -> WaveFile:
//| """No-op used by Context Managers."""
//| ...
// Provided by context manager helper.

//| def __exit__(self) -> None:
//| """Automatically deinitializes the hardware when exiting a context. See
//| :ref:`lifetime-and-contextmanagers` for more info."""
//| ...
static mp_obj_t audioio_monowavefile_obj___exit__(size_t n_args, const mp_obj_t *args) {
(void)n_args;
common_hal_audioio_monowavefile_deinit(args[0]);
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(audioio_monowavefile___exit___obj, 4, 4, audioio_monowavefile_obj___exit__);

//| sample_rate: int
//| """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_monowavefile_obj_get_sample_rate(mp_obj_t self_in) {
audioio_monowavefile_obj_t *self = MP_OBJ_TO_PTR(self_in);
check_for_deinit(self);
return MP_OBJ_NEW_SMALL_INT(common_hal_audioio_monowavefile_get_sample_rate(self));
}
MP_DEFINE_CONST_FUN_OBJ_1(audioio_monowavefile_get_sample_rate_obj, audioio_monowavefile_obj_get_sample_rate);

static mp_obj_t audioio_monowavefile_obj_set_sample_rate(mp_obj_t self_in, mp_obj_t sample_rate) {
audioio_monowavefile_obj_t *self = MP_OBJ_TO_PTR(self_in);
check_for_deinit(self);
common_hal_audioio_monowavefile_set_sample_rate(self, mp_obj_get_int(sample_rate));
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_2(audioio_monowavefile_set_sample_rate_obj, audioio_monowavefile_obj_set_sample_rate);

MP_PROPERTY_GETSET(audioio_monowavefile_sample_rate_obj,
(mp_obj_t)&audioio_monowavefile_get_sample_rate_obj,
(mp_obj_t)&audioio_monowavefile_set_sample_rate_obj);


//| stretch: float
//| """TODO"""
STATIC mp_obj_t audioio_monowavefile_obj_set_speed(mp_obj_t self_in, mp_obj_t speed) {
audioio_monowavefile_obj_t *self = MP_OBJ_TO_PTR(self_in);
check_for_deinit(self);
common_hal_audioio_monowavefile_set_speed(self, mp_obj_get_float(speed));
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_2(audioio_monowavefile_set_speed_obj, audioio_monowavefile_obj_set_speed);


//| bits_per_sample: int
//| """Bits per sample. (read only)"""
static mp_obj_t audioio_monowavefile_obj_get_bits_per_sample(mp_obj_t self_in) {
audioio_monowavefile_obj_t *self = MP_OBJ_TO_PTR(self_in);
check_for_deinit(self);
return MP_OBJ_NEW_SMALL_INT(common_hal_audioio_monowavefile_get_bits_per_sample(self));
}
MP_DEFINE_CONST_FUN_OBJ_1(audioio_monowavefile_get_bits_per_sample_obj, audioio_monowavefile_obj_get_bits_per_sample);

MP_PROPERTY_GETTER(audioio_monowavefile_bits_per_sample_obj,
(mp_obj_t)&audioio_monowavefile_get_bits_per_sample_obj);
//| channel_count: int
//| """Number of audio channels. (read only)"""
//|
static mp_obj_t audioio_monowavefile_obj_get_channel_count(mp_obj_t self_in) {
audioio_monowavefile_obj_t *self = MP_OBJ_TO_PTR(self_in);
check_for_deinit(self);
return MP_OBJ_NEW_SMALL_INT(common_hal_audioio_monowavefile_get_channel_count(self));
}
MP_DEFINE_CONST_FUN_OBJ_1(audioio_monowavefile_get_channel_count_obj, audioio_monowavefile_obj_get_channel_count);

MP_PROPERTY_GETTER(audioio_monowavefile_channel_count_obj,
(mp_obj_t)&audioio_monowavefile_get_channel_count_obj);


static const mp_rom_map_elem_t audioio_monowavefile_locals_dict_table[] = {
// Methods
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&audioio_monowavefile_deinit_obj) },
{ MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) },
{ MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&audioio_monowavefile___exit___obj) },
{ MP_ROM_QSTR(MP_QSTR_set_speed), MP_ROM_PTR(&audioio_monowavefile_set_speed_obj) },

// Properties
{ MP_ROM_QSTR(MP_QSTR_sample_rate), MP_ROM_PTR(&audioio_monowavefile_sample_rate_obj) },
{ MP_ROM_QSTR(MP_QSTR_bits_per_sample), MP_ROM_PTR(&audioio_monowavefile_bits_per_sample_obj) },
{ MP_ROM_QSTR(MP_QSTR_channel_count), MP_ROM_PTR(&audioio_monowavefile_channel_count_obj) },
};
static MP_DEFINE_CONST_DICT(audioio_monowavefile_locals_dict, audioio_monowavefile_locals_dict_table);

static const audiosample_p_t audioio_monowavefile_proto = {
MP_PROTO_IMPLEMENT(MP_QSTR_protocol_audiosample)
.sample_rate = (audiosample_sample_rate_fun)common_hal_audioio_monowavefile_get_sample_rate,
.bits_per_sample = (audiosample_bits_per_sample_fun)common_hal_audioio_monowavefile_get_bits_per_sample,
.channel_count = (audiosample_channel_count_fun)common_hal_audioio_monowavefile_get_channel_count,
.reset_buffer = (audiosample_reset_buffer_fun)audioio_monowavefile_reset_buffer,
.get_buffer = (audiosample_get_buffer_fun)audioio_monowavefile_get_buffer,
.get_buffer_structure = (audiosample_get_buffer_structure_fun)audioio_monowavefile_get_buffer_structure,
};


MP_DEFINE_CONST_OBJ_TYPE(
audioio_monowavefile_type,
MP_QSTR_MonoWaveFile,
MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS,
make_new, audioio_monowavefile_make_new,
locals_dict, &audioio_monowavefile_locals_dict,
protocol, &audioio_monowavefile_proto
);
25 changes: 25 additions & 0 deletions shared-bindings/audiocore/MonoWaveFile.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2017 Scott Shawcroft for Adafruit Industries
//
// SPDX-License-Identifier: MIT

#pragma once

#include "py/obj.h"
#include "extmod/vfs_fat.h"

#include "shared-module/audiocore/MonoWaveFile.h"

extern const mp_obj_type_t audioio_monowavefile_type;

void common_hal_audioio_monowavefile_construct(audioio_monowavefile_obj_t *self,
pyb_file_obj_t *file, uint8_t *buffer, size_t buffer_size);

void common_hal_audioio_monowavefile_deinit(audioio_monowavefile_obj_t *self);
bool common_hal_audioio_monowavefile_deinited(audioio_monowavefile_obj_t *self);
uint32_t common_hal_audioio_monowavefile_get_sample_rate(audioio_monowavefile_obj_t *self);
void common_hal_audioio_monowavefile_set_sample_rate(audioio_monowavefile_obj_t *self, uint32_t sample_rate);
uint8_t common_hal_audioio_monowavefile_get_bits_per_sample(audioio_monowavefile_obj_t *self);
uint8_t common_hal_audioio_monowavefile_get_channel_count(audioio_monowavefile_obj_t *self);
void common_hal_audioio_monowavefile_set_speed(audioio_monowavefile_obj_t *self, float speed);
2 changes: 2 additions & 0 deletions shared-bindings/audiocore/__init__.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "shared-bindings/audiocore/__init__.h"
#include "shared-bindings/audiocore/RawSample.h"
#include "shared-bindings/audiocore/WaveFile.h"
#include "shared-bindings/audiocore/MonoWaveFile.h"
// #include "shared-bindings/audiomixer/Mixer.h"

//| """Support for audio samples"""
Expand Down Expand Up @@ -78,6 +79,7 @@ static const mp_rom_map_elem_t audiocore_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_audiocore) },
{ MP_ROM_QSTR(MP_QSTR_RawSample), MP_ROM_PTR(&audioio_rawsample_type) },
{ MP_ROM_QSTR(MP_QSTR_WaveFile), MP_ROM_PTR(&audioio_wavefile_type) },
{ MP_ROM_QSTR(MP_QSTR_MonoWaveFile), MP_ROM_PTR(&audioio_monowavefile_type) },
#if CIRCUITPY_AUDIOCORE_DEBUG
{ MP_ROM_QSTR(MP_QSTR_get_buffer), MP_ROM_PTR(&audiocore_get_buffer_obj) },
{ MP_ROM_QSTR(MP_QSTR_reset_buffer), MP_ROM_PTR(&audiocore_reset_buffer_obj) },
Expand Down
Loading