Merge pull request #7825 from jepler/better-synthio

Better synthio
This commit is contained in:
Scott Shawcroft 2023-04-05 12:23:10 -07:00 committed by GitHub
commit 6df88ac948
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 915 additions and 145 deletions

View File

@ -193,6 +193,10 @@ msgstr ""
msgid "%q must be array of type 'H'"
msgstr ""
#: shared-bindings/synthio/MidiTrack.c shared-bindings/synthio/__init__.c
msgid "%q must be array of type 'h'"
msgstr ""
#: ports/raspberrypi/bindings/cyw43/__init__.c py/argcheck.c py/objexcept.c
#: shared-bindings/canio/CAN.c shared-bindings/digitalio/Pull.c
msgid "%q must be of type %q or %q, not %q"

View File

@ -44,6 +44,8 @@ CIRCUITPY_AUDIOIO = 1
CIRCUITPY_AUDIOMIXER = 1
CIRCUITPY_DISPLAYIO = 1
CIRCUITPY_KEYPAD = 1
CIRCUITPY_KEYPAD_SHIFTREGISTERKEYS = 0
CIRCUITPY_KEYPAD_KEYMATRIX = 0
CIRCUITPY_MATH = 1
CIRCUITPY_STAGE = 1
CIRCUITPY_SYNTHIO = 1

View File

@ -38,7 +38,6 @@ INC += \
-Iboards/$(BOARD) \
-Iperipherals/ \
-Iperipherals/mimxrt10xx/ \
-Isdk/CMSIS/Include \
-Isdk/devices/$(CHIP_FAMILY) \
-Isdk/devices/$(CHIP_FAMILY)/drivers \
-Isdk/drivers/common

View File

@ -18,6 +18,7 @@ CIRCUITPY_AUDIOIO = 0
CIRCUITPY_AUDIOMIXER = 1
CIRCUITPY_AUDIOMP3 = 1
CIRCUITPY_AUDIOPWMIO = 1
CIRCUITPY_SYNTHIO_MAX_CHANNELS = 12
CIRCUITPY_BUSDEVICE = 1
CIRCUITPY_COUNTIO = 0
CIRCUITPY_FREQUENCYIO = 0

View File

@ -19,6 +19,7 @@ CIRCUITPY_PWMIO ?= 1
CIRCUITPY_RGBMATRIX ?= $(CIRCUITPY_DISPLAYIO)
CIRCUITPY_ROTARYIO ?= 1
CIRCUITPY_ROTARYIO_SOFTENCODER = 1
CIRCUITPY_SYNTHIO_MAX_CHANNELS = 12
# Things that need to be implemented.
# Use PWM internally

View File

@ -30,15 +30,30 @@ SRC_BITMAP := \
displayio_min.c \
shared-bindings/aesio/aes.c \
shared-bindings/aesio/__init__.c \
shared-bindings/audiocore/__init__.c \
shared-bindings/audiocore/RawSample.c \
shared-bindings/audiocore/WaveFile.c \
shared-bindings/audiomixer/__init__.c \
shared-bindings/audiomixer/Mixer.c \
shared-bindings/audiomixer/MixerVoice.c \
shared-bindings/bitmaptools/__init__.c \
shared-bindings/displayio/Bitmap.c \
shared-bindings/rainbowio/__init__.c \
shared-bindings/struct/__init__.c \
shared-bindings/synthio/__init__.c \
shared-bindings/synthio/MidiTrack.c \
shared-bindings/synthio/Synthesizer.c \
shared-bindings/traceback/__init__.c \
shared-bindings/util.c \
shared-bindings/zlib/__init__.c \
shared-module/aesio/aes.c \
shared-module/aesio/__init__.c \
shared-module/audiocore/__init__.c \
shared-module/audiocore/RawSample.c \
shared-module/audiocore/WaveFile.c \
shared-module/audiomixer/__init__.c \
shared-module/audiomixer/Mixer.c \
shared-module/audiomixer/MixerVoice.c \
shared-module/bitmaptools/__init__.c \
shared-module/displayio/area.c \
shared-module/displayio/Bitmap.c \
@ -47,6 +62,9 @@ SRC_BITMAP := \
shared-module/os/getenv.c \
shared-module/rainbowio/__init__.c \
shared-module/struct/__init__.c \
shared-module/synthio/__init__.c \
shared-module/synthio/MidiTrack.c \
shared-module/synthio/Synthesizer.c \
shared-module/traceback/__init__.c \
shared-module/zlib/__init__.c \
@ -54,12 +72,17 @@ SRC_C += $(SRC_BITMAP)
CFLAGS += \
-DCIRCUITPY_AESIO=1 \
-DCIRCUITPY_AUDIOCORE=1 \
-DCIRCUITPY_AUDIOMIXER=1 \
-DCIRCUITPY_AUDIOCORE_DEBUG=1 \
-DCIRCUITPY_BITMAPTOOLS=1 \
-DCIRCUITPY_DISPLAYIO_UNIX=1 \
-DCIRCUITPY_OS_GETENV=1 \
-DCIRCUITPY_GIFIO=1 \
-DCIRCUITPY_OS_GETENV=1 \
-DCIRCUITPY_RAINBOWIO=1 \
-DCIRCUITPY_STRUCT=1 \
-DCIRCUITPY_SYNTHIO=1 \
-DCIRCUITPY_SYNTHIO_MAX_CHANNELS=14 \
-DCIRCUITPY_TRACEBACK=1 \
-DCIRCUITPY_ZLIB=1

View File

@ -648,6 +648,7 @@ SRC_SHARED_MODULE_ALL = \
supervisor/__init__.c \
supervisor/StatusBar.c \
synthio/MidiTrack.c \
synthio/Synthesizer.c \
synthio/__init__.c \
terminalio/Terminal.c \
terminalio/__init__.c \

View File

@ -107,6 +107,11 @@ CFLAGS += -DCIRCUITPY_AUDIOCORE=$(CIRCUITPY_AUDIOCORE)
CIRCUITPY_AUDIOMIXER ?= $(CIRCUITPY_AUDIOIO)
CFLAGS += -DCIRCUITPY_AUDIOMIXER=$(CIRCUITPY_AUDIOMIXER)
ifndef CIRCUITPY_AUDIOCORE_DEBUG
CIRCUITPY_AUDIOCORE_DEBUG ?= 0
endif
CFLAGS += -DCIRCUITPY_AUDIOCORE_DEBUG=$(CIRCUITPY_AUDIOCORE_DEBUG)
ifndef CIRCUITPY_AUDIOMP3
ifeq ($(CIRCUITPY_FULL_BUILD),1)
CIRCUITPY_AUDIOMP3 = $(CIRCUITPY_AUDIOCORE)
@ -286,6 +291,15 @@ CFLAGS += -DCIRCUITPY_JSON=$(CIRCUITPY_JSON)
CIRCUITPY_KEYPAD ?= $(CIRCUITPY_FULL_BUILD)
CFLAGS += -DCIRCUITPY_KEYPAD=$(CIRCUITPY_KEYPAD)
CIRCUITPY_KEYPAD_KEYS ?= $(CIRCUITPY_KEYPAD)
CFLAGS += -DCIRCUITPY_KEYPAD_KEYS=$(CIRCUITPY_KEYPAD_KEYS)
CIRCUITPY_KEYPAD_KEYMATRIX ?= $(CIRCUITPY_KEYPAD)
CFLAGS += -DCIRCUITPY_KEYPAD_KEYMATRIX=$(CIRCUITPY_KEYPAD_KEYMATRIX)
CIRCUITPY_KEYPAD_SHIFTREGISTERKEYS ?= $(CIRCUITPY_KEYPAD)
CFLAGS += -DCIRCUITPY_KEYPAD_SHIFTREGISTERKEYS=$(CIRCUITPY_KEYPAD_SHIFTREGISTERKEYS)
CIRCUITPY_MATH ?= 1
CFLAGS += -DCIRCUITPY_MATH=$(CIRCUITPY_MATH)
@ -437,6 +451,10 @@ CFLAGS += -DCIRCUITPY_SUPERVISOR=$(CIRCUITPY_SUPERVISOR)
CIRCUITPY_SYNTHIO ?= $(CIRCUITPY_AUDIOCORE)
CFLAGS += -DCIRCUITPY_SYNTHIO=$(CIRCUITPY_SYNTHIO)
CIRCUITPY_SYNTHIO_MAX_CHANNELS ?= 2
CFLAGS += -DCIRCUITPY_SYNTHIO_MAX_CHANNELS=$(CIRCUITPY_SYNTHIO_MAX_CHANNELS)
CIRCUITPY_SYS ?= 1
CFLAGS += -DCIRCUITPY_SYS=$(CIRCUITPY_SYS)

View File

@ -97,7 +97,7 @@
//| ...
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 *all_args) {
#if !CIRCUITPY_AUDIOBUSIO_I2SOUT
mp_raise_NotImplementedError(translate("I2SOut not available"));
mp_raise_NotImplementedError_varg(translate("%q"), MP_QSTR_I2SOut);
return NULL; // Not reachable.
#else
enum { ARG_bit_clock, ARG_word_select, ARG_data, ARG_left_justified };

View File

@ -94,7 +94,7 @@
//| ...
STATIC mp_obj_t audiobusio_pdmin_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
#if !CIRCUITPY_AUDIOBUSIO_PDMIN
mp_raise_NotImplementedError(translate("PDMIn not available"));
mp_raise_NotImplementedError_varg(translate("%q"), MP_QSTR_PDMIn);
#else
enum { ARG_clock_pin, ARG_data_pin, ARG_sample_rate, ARG_bit_depth, ARG_mono, ARG_oversample, ARG_startup_delay };
static const mp_arg_t allowed_args[] = {

View File

@ -40,7 +40,7 @@
//| def __init__(
//| self, buffer: ReadableBuffer, *, channel_count: int = 1, sample_rate: int = 8000
//| ) -> None:
//| """Create a RawSample based on the given buffer of signed values. If channel_count is more than
//| """Create a RawSample based on the given buffer of 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.

View File

@ -32,6 +32,7 @@
#include "shared-bindings/audiocore/WaveFile.h"
#include "shared-bindings/util.h"
#include "supervisor/shared/translate/translate.h"
#include "extmod/vfs_posix.h"
//| class WaveFile:
//| """Load a wave file for audio playback

View File

@ -29,7 +29,6 @@
#include "py/obj.h"
#include "py/runtime.h"
#include "shared-bindings/microcontroller/Pin.h"
#include "shared-bindings/audiocore/__init__.h"
#include "shared-bindings/audiocore/RawSample.h"
#include "shared-bindings/audiocore/WaveFile.h"
@ -37,10 +36,57 @@
//| """Support for audio samples"""
#if CIRCUITPY_AUDIOCORE_DEBUG
// (no docstrings so that the debug functions are not shown on docs.circuitpython.org)
STATIC mp_obj_t audiocore_get_buffer(mp_obj_t sample_in) {
uint8_t *buffer = NULL;
uint32_t buffer_length = 0;
audioio_get_buffer_result_t gbr = audiosample_get_buffer(sample_in, false, 0, &buffer, &buffer_length);
mp_obj_t result[2] = {mp_obj_new_int_from_uint(gbr), mp_const_none};
if (gbr != GET_BUFFER_ERROR) {
// copies the data because the gc semantics of get_buffer are unclear
result[1] = mp_obj_new_bytes(buffer, buffer_length);
}
return mp_obj_new_tuple(2, result);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(audiocore_get_buffer_obj, audiocore_get_buffer);
STATIC mp_obj_t audiocore_get_structure(mp_obj_t sample_in) {
bool single_buffer, samples_signed;
uint32_t max_buffer_length;
uint8_t spacing;
audiosample_get_buffer_structure(sample_in, false, &single_buffer, &samples_signed, &max_buffer_length, &spacing);
mp_obj_t result[4] = {
mp_obj_new_int_from_uint(single_buffer),
mp_obj_new_int_from_uint(samples_signed),
mp_obj_new_int_from_uint(max_buffer_length),
mp_obj_new_int_from_uint(spacing),
};
return mp_obj_new_tuple(4, result);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(audiocore_get_structure_obj, audiocore_get_structure);
STATIC mp_obj_t audiocore_reset_buffer(mp_obj_t sample_in) {
audiosample_reset_buffer(sample_in, false, 0);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(audiocore_reset_buffer_obj, audiocore_reset_buffer);
#endif
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) },
#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) },
{ MP_ROM_QSTR(MP_QSTR_get_structure), MP_ROM_PTR(&audiocore_get_structure_obj) },
#endif
};
STATIC MP_DEFINE_CONST_DICT(audiocore_module_globals, audiocore_module_globals_table);

View File

@ -33,8 +33,6 @@
#include "py/binary.h"
#include "py/objproperty.h"
#include "py/runtime.h"
#include "shared-bindings/microcontroller/Pin.h"
#include "shared-bindings/audiocore/RawSample.h"
#include "shared-bindings/util.h"
#include "supervisor/shared/translate/translate.h"

View File

@ -27,9 +27,7 @@
#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOMIXER_MIXER_H
#define MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOMIXER_MIXER_H
#include "common-hal/microcontroller/Pin.h"
#include "shared-module/audiomixer/Mixer.h"
#include "shared-bindings/audiocore/RawSample.h"
extern const mp_obj_type_t audiomixer_mixer_type;
extern const mp_obj_type_t audiomixer_mixervoice_type;

View File

@ -32,8 +32,6 @@
#include "py/binary.h"
#include "py/objproperty.h"
#include "py/runtime.h"
#include "shared-bindings/microcontroller/Pin.h"
#include "shared-bindings/audiocore/RawSample.h"
#include "shared-bindings/util.h"
#include "supervisor/shared/translate/translate.h"
@ -115,7 +113,7 @@ STATIC mp_obj_t audiomixer_mixervoice_obj_set_level(size_t n_args, const mp_obj_
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
float level = mp_obj_get_float(args[ARG_level].u_obj);
mp_float_t level = mp_obj_get_float(args[ARG_level].u_obj);
if (level > 1 || level < 0) {
mp_raise_ValueError(translate("level must be between 0 and 1"));

View File

@ -26,9 +26,6 @@
#ifndef SHARED_BINDINGS_AUDIOMIXER_MIXERVOICE_H_
#define SHARED_BINDINGS_AUDIOMIXER_MIXERVOICE_H_
#include "common-hal/microcontroller/Pin.h"
#include "shared-bindings/audiocore/RawSample.h"
#include "shared-module/audiomixer/MixerVoice.h"
#include "shared-module/audiomixer/Mixer.h"
@ -39,8 +36,8 @@ void common_hal_audiomixer_mixervoice_construct(audiomixer_mixervoice_obj_t *sel
void common_hal_audiomixer_mixervoice_set_parent(audiomixer_mixervoice_obj_t *self, audiomixer_mixer_obj_t *parent);
void common_hal_audiomixer_mixervoice_play(audiomixer_mixervoice_obj_t *self, mp_obj_t sample, bool loop);
void common_hal_audiomixer_mixervoice_stop(audiomixer_mixervoice_obj_t *self);
float common_hal_audiomixer_mixervoice_get_level(audiomixer_mixervoice_obj_t *self);
void common_hal_audiomixer_mixervoice_set_level(audiomixer_mixervoice_obj_t *self, float gain);
mp_float_t common_hal_audiomixer_mixervoice_get_level(audiomixer_mixervoice_obj_t *self);
void common_hal_audiomixer_mixervoice_set_level(audiomixer_mixervoice_obj_t *self, mp_float_t gain);
bool common_hal_audiomixer_mixervoice_get_playing(audiomixer_mixervoice_obj_t *self);

View File

@ -29,7 +29,6 @@
#include "py/obj.h"
#include "py/runtime.h"
#include "shared-bindings/microcontroller/Pin.h"
#include "shared-bindings/audiomixer/Mixer.h"
//| """Support for audio mixing"""

View File

@ -167,9 +167,9 @@ STATIC mp_obj_t keypad_event_unary_op(mp_unary_op_t op, mp_obj_t self_in) {
STATIC void keypad_event_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
keypad_event_obj_t *self = MP_OBJ_TO_PTR(self_in);
mp_printf(print, "<Event: key_number %d %s>",
mp_printf(print, "<Event: key_number %d %q>",
common_hal_keypad_event_get_key_number(self),
common_hal_keypad_event_get_pressed(self) ? "pressed" : "released");
common_hal_keypad_event_get_pressed(self) ? MP_QSTR_pressed : MP_QSTR_released);
}
STATIC const mp_rom_map_elem_t keypad_event_locals_dict_table[] = {

View File

@ -71,6 +71,7 @@
//| ...
STATIC mp_obj_t keypad_keymatrix_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
#if CIRCUITPY_KEYPAD_KEYMATRIX
keypad_keymatrix_obj_t *self = m_new_obj(keypad_keymatrix_obj_t);
self->base.type = &keypad_keymatrix_type;
enum { ARG_row_pins, ARG_column_pins, ARG_columns_to_anodes, ARG_interval, ARG_max_events };
@ -114,8 +115,13 @@ STATIC mp_obj_t keypad_keymatrix_make_new(const mp_obj_type_t *type, size_t n_ar
common_hal_keypad_keymatrix_construct(self, num_row_pins, row_pins_array, num_column_pins, column_pins_array, args[ARG_columns_to_anodes].u_bool, interval, max_events);
return MP_OBJ_FROM_PTR(self);
#else
mp_raise_NotImplementedError_varg(translate("%q"), MP_QSTR_KeyMatrix);
#endif
}
#if CIRCUITPY_KEYPAD_KEYMATRIX
//| def deinit(self) -> None:
//| """Stop scanning and release the pins."""
//| ...
@ -228,9 +234,13 @@ STATIC const mp_rom_map_elem_t keypad_keymatrix_locals_dict_table[] = {
STATIC MP_DEFINE_CONST_DICT(keypad_keymatrix_locals_dict, keypad_keymatrix_locals_dict_table);
#endif
const mp_obj_type_t keypad_keymatrix_type = {
{ &mp_type_type },
.name = MP_QSTR_KeyMatrix,
.make_new = keypad_keymatrix_make_new,
#if CIRCUITPY_KEYPAD_KEYMATRIX
.locals_dict = (mp_obj_t)&keypad_keymatrix_locals_dict,
#endif
};

View File

@ -73,6 +73,7 @@
//| ...
STATIC mp_obj_t keypad_keys_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
#if CIRCUITPY_KEYPAD_KEYS
keypad_keys_obj_t *self = m_new_obj(keypad_keys_obj_t);
self->base.type = &keypad_keys_type;
enum { ARG_pins, ARG_value_when_pressed, ARG_pull, ARG_interval, ARG_max_events };
@ -106,8 +107,13 @@ STATIC mp_obj_t keypad_keys_make_new(const mp_obj_type_t *type, size_t n_args, s
common_hal_keypad_keys_construct(self, num_pins, pins_array, value_when_pressed, args[ARG_pull].u_bool, interval, max_events);
return MP_OBJ_FROM_PTR(self);
#else
mp_raise_NotImplementedError_varg(translate("%q"), MP_QSTR_Keys);
#endif
}
#if CIRCUITPY_KEYPAD_KEYS
//| def deinit(self) -> None:
//| """Stop scanning and release the pins."""
//| ...
@ -162,10 +168,13 @@ STATIC const mp_rom_map_elem_t keypad_keys_locals_dict_table[] = {
};
STATIC MP_DEFINE_CONST_DICT(keypad_keys_locals_dict, keypad_keys_locals_dict_table);
#endif
const mp_obj_type_t keypad_keys_type = {
{ &mp_type_type },
.name = MP_QSTR_Keys,
.make_new = keypad_keys_make_new,
#if CIRCUITPY_KEYPAD_KEYS
.locals_dict = (mp_obj_t)&keypad_keys_locals_dict,
#endif
};

View File

@ -82,6 +82,7 @@
//| ...
STATIC mp_obj_t keypad_shiftregisterkeys_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
#if CIRCUITPY_KEYPAD_SHIFTREGISTERKEYS
keypad_shiftregisterkeys_obj_t *self = m_new_obj(keypad_shiftregisterkeys_obj_t);
self->base.type = &keypad_shiftregisterkeys_type;
enum { ARG_clock, ARG_data, ARG_latch, ARG_value_to_latch, ARG_key_count, ARG_value_when_pressed, ARG_interval, ARG_max_events };
@ -113,8 +114,12 @@ STATIC mp_obj_t keypad_shiftregisterkeys_make_new(const mp_obj_type_t *type, siz
self, clock, data, latch, value_to_latch, key_count, value_when_pressed, interval, max_events);
return MP_OBJ_FROM_PTR(self);
#else
mp_raise_NotImplementedError_varg(translate("%q"), MP_QSTR_ShiftRegisterKeys);
#endif
}
#if CIRCUITPY_KEYPAD_SHIFTREGISTERKEYS
//| def deinit(self) -> None:
//| """Stop scanning and release the pins."""
//| ...
@ -169,10 +174,13 @@ STATIC const mp_rom_map_elem_t keypad_shiftregisterkeys_locals_dict_table[] = {
};
STATIC MP_DEFINE_CONST_DICT(keypad_shiftregisterkeys_locals_dict, keypad_shiftregisterkeys_locals_dict_table);
#endif
const mp_obj_type_t keypad_shiftregisterkeys_type = {
{ &mp_type_type },
.name = MP_QSTR_ShiftRegisterKeys,
.make_new = keypad_shiftregisterkeys_make_new,
#if CIRCUITPY_KEYPAD_SHIFTREGISTERKEYS
.locals_dict = (mp_obj_t)&keypad_shiftregisterkeys_locals_dict,
#endif
};

View File

@ -32,13 +32,19 @@
#include "py/runtime.h"
#include "shared-bindings/util.h"
#include "shared-bindings/synthio/MidiTrack.h"
#include "shared-bindings/synthio/__init__.h"
#include "supervisor/shared/translate/translate.h"
//| class MidiTrack:
//| """Simple square-wave MIDI synth"""
//| """Simple MIDI synth"""
//|
//| def __init__(
//| self, buffer: ReadableBuffer, tempo: int, *, sample_rate: int = 11025
//| self,
//| buffer: ReadableBuffer,
//| tempo: int,
//| *,
//| sample_rate: int = 11025,
//| waveform: ReadableBuffer = None
//| ) -> None:
//| """Create a MidiTrack from the given stream of MIDI events. Only "Note On" and "Note Off" events
//| are supported; channel numbers and key velocities are ignored. Up to two notes may be on at the
@ -47,6 +53,7 @@
//| :param ~circuitpython_typing.ReadableBuffer buffer: Stream of MIDI events, as stored in a MIDI file track chunk
//| :param int tempo: Tempo of the streamed events, in MIDI ticks per second
//| :param int sample_rate: The desired playback sample rate; higher sample rate requires more memory
//| :param ReadableBuffer waveform: A single-cycle waveform. Default is a 50% duty cycle square wave. If specified, must be a ReadableBuffer of type 'h' (signed 16 bit)
//|
//| Simple melody::
//|
@ -65,11 +72,12 @@
//| print("stopped")"""
//| ...
STATIC mp_obj_t synthio_miditrack_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
enum { ARG_buffer, ARG_tempo, ARG_sample_rate };
enum { ARG_buffer, ARG_tempo, ARG_sample_rate, ARG_waveform };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_buffer, MP_ARG_OBJ | MP_ARG_REQUIRED },
{ MP_QSTR_tempo, MP_ARG_INT | MP_ARG_REQUIRED },
{ MP_QSTR_sample_rate, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 11025} },
{ MP_QSTR_waveform, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none } },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
@ -77,13 +85,18 @@ STATIC mp_obj_t synthio_miditrack_make_new(const mp_obj_type_t *type, size_t n_a
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_READ);
mp_buffer_info_t bufinfo_waveform;
synthio_synth_parse_waveform(&bufinfo_waveform, args[ARG_waveform].u_obj);
synthio_miditrack_obj_t *self = m_new_obj(synthio_miditrack_obj_t);
self->base.type = &synthio_miditrack_type;
common_hal_synthio_miditrack_construct(self,
(uint8_t *)bufinfo.buf, bufinfo.len,
args[ARG_tempo].u_int,
args[ARG_sample_rate].u_int);
args[ARG_sample_rate].u_int,
bufinfo_waveform.buf,
bufinfo_waveform.len / 2);
return MP_OBJ_FROM_PTR(self);
}

View File

@ -32,7 +32,7 @@
extern const mp_obj_type_t synthio_miditrack_type;
void common_hal_synthio_miditrack_construct(synthio_miditrack_obj_t *self,
const uint8_t *buffer, uint32_t len, uint32_t tempo, uint32_t sample_rate);
const uint8_t *buffer, uint32_t len, uint32_t tempo, uint32_t sample_rate, const int16_t *waveform, uint16_t waveform_len);
void common_hal_synthio_miditrack_deinit(synthio_miditrack_obj_t *self);
bool common_hal_synthio_miditrack_deinited(synthio_miditrack_obj_t *self);

View File

@ -0,0 +1,211 @@
/*
* This file is part of the Micro Python project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Artyom Skrobov
* Copyright (c) 2023 Jeff Epler 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 "shared/runtime/context_manager_helpers.h"
#include "py/binary.h"
#include "py/objproperty.h"
#include "py/runtime.h"
#include "shared-bindings/util.h"
#include "shared-bindings/synthio/Synthesizer.h"
#include "shared-bindings/synthio/__init__.h"
#include "supervisor/shared/translate/translate.h"
//| class Synth:
//| def __init__(self, *, sample_rate: int = 11025, waveform: ReadableBuffer = None) -> None:
//| """Create a synthesizer object."""
STATIC mp_obj_t synthio_synthesizer_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
enum { ARG_sample_rate, ARG_waveform };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_sample_rate, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 11025} },
{ MP_QSTR_waveform, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none } },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
mp_buffer_info_t bufinfo_waveform;
synthio_synth_parse_waveform(&bufinfo_waveform, args[ARG_waveform].u_obj);
synthio_synthesizer_obj_t *self = m_new_obj(synthio_synthesizer_obj_t);
self->base.type = &synthio_synthesizer_type;
common_hal_synthio_synthesizer_construct(self,
args[ARG_sample_rate].u_int,
bufinfo_waveform.buf,
bufinfo_waveform.len / 2);
return MP_OBJ_FROM_PTR(self);
}
STATIC void check_for_deinit(synthio_synthesizer_obj_t *self) {
if (common_hal_synthio_synthesizer_deinited(self)) {
raise_deinited_error();
}
}
//| def press(self, /, press: Sequence[int] = ()) -> None:
//| """Turn some notes on. Notes use MIDI numbering, with 60 being middle C."""
STATIC mp_obj_t synthio_synthesizer_press(mp_obj_t self_in, mp_obj_t press) {
synthio_synthesizer_obj_t *self = MP_OBJ_TO_PTR(self_in);
check_for_deinit(self);
common_hal_synthio_synthesizer_press(self, press);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(synthio_synthesizer_press_obj, synthio_synthesizer_press);
//
//| def release_then_press(
//| self, release: Sequence[int] = (), press: Sequence[int] = ()
//| ) -> None:
//| """Turn some notes on and/or off. Notes use MIDI numbering, with 60 being middle C."""
STATIC mp_obj_t synthio_synthesizer_release_then_press(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_release, ARG_press };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_release, MP_ARG_OBJ, {.u_obj = mp_const_empty_tuple } },
{ MP_QSTR_press, MP_ARG_OBJ, {.u_obj = mp_const_empty_tuple } },
};
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);
synthio_synthesizer_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
check_for_deinit(self);
common_hal_synthio_synthesizer_release(self, args[ARG_release].u_obj);
common_hal_synthio_synthesizer_press(self, args[ARG_press].u_obj);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(synthio_synthesizer_release_then_press_obj, 1, synthio_synthesizer_release_then_press);
//
//| def release_all_then_press(self, /, press: Sequence[int]) -> None:
//| """Turn any currently-playing notes off, then turn on the given notes"""
STATIC mp_obj_t synthio_synthesizer_release_all_then_press(mp_obj_t self_in, mp_obj_t press) {
synthio_synthesizer_obj_t *self = MP_OBJ_TO_PTR(self_in);
check_for_deinit(self);
common_hal_synthio_synthesizer_release_all(self);
common_hal_synthio_synthesizer_press(self, press);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(synthio_synthesizer_release_all_then_press_obj, synthio_synthesizer_release_all_then_press);
//
//| def release_all(self) -> None:
//| """Turn any currently-playing notes off"""
STATIC mp_obj_t synthio_synthesizer_release_all(mp_obj_t self_in) {
synthio_synthesizer_obj_t *self = MP_OBJ_TO_PTR(self_in);
check_for_deinit(self);
common_hal_synthio_synthesizer_release_all(self);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(synthio_synthesizer_release_all_obj, synthio_synthesizer_release_all);
//| def deinit(self) -> None:
//| """Deinitialises the object and releases any memory resources for reuse."""
//| ...
STATIC mp_obj_t synthio_synthesizer_deinit(mp_obj_t self_in) {
synthio_synthesizer_obj_t *self = MP_OBJ_TO_PTR(self_in);
common_hal_synthio_synthesizer_deinit(self);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(synthio_synthesizer_deinit_obj, synthio_synthesizer_deinit);
//| def __enter__(self) -> MidiTrack:
//| """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 synthio_synthesizer_obj___exit__(size_t n_args, const mp_obj_t *args) {
(void)n_args;
common_hal_synthio_synthesizer_deinit(args[0]);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(synthio_synthesizer___exit___obj, 4, 4, synthio_synthesizer_obj___exit__);
//| sample_rate: int
//| """32 bit value that tells how quickly samples are played in Hertz (cycles per second)."""
STATIC mp_obj_t synthio_synthesizer_obj_get_sample_rate(mp_obj_t self_in) {
synthio_synthesizer_obj_t *self = MP_OBJ_TO_PTR(self_in);
check_for_deinit(self);
return MP_OBJ_NEW_SMALL_INT(common_hal_synthio_synthesizer_get_sample_rate(self));
}
MP_DEFINE_CONST_FUN_OBJ_1(synthio_synthesizer_get_sample_rate_obj, synthio_synthesizer_obj_get_sample_rate);
MP_PROPERTY_GETTER(synthio_synthesizer_sample_rate_obj,
(mp_obj_t)&synthio_synthesizer_get_sample_rate_obj);
//| pressed: Tuple[int]
//| """A sequence of the currently pressed notes (read-only property)"""
//|
STATIC mp_obj_t synthio_synthesizer_obj_get_pressed(mp_obj_t self_in) {
synthio_synthesizer_obj_t *self = MP_OBJ_TO_PTR(self_in);
check_for_deinit(self);
return common_hal_synthio_synthesizer_get_pressed_notes(self);
}
MP_DEFINE_CONST_FUN_OBJ_1(synthio_synthesizer_get_pressed_obj, synthio_synthesizer_obj_get_pressed);
MP_PROPERTY_GETTER(synthio_synthesizer_pressed_obj,
(mp_obj_t)&synthio_synthesizer_get_pressed_obj);
STATIC const mp_rom_map_elem_t synthio_synthesizer_locals_dict_table[] = {
// Methods
{ MP_ROM_QSTR(MP_QSTR_press), MP_ROM_PTR(&synthio_synthesizer_press_obj) },
{ MP_ROM_QSTR(MP_QSTR_release_all), MP_ROM_PTR(&synthio_synthesizer_release_all_obj) },
{ MP_ROM_QSTR(MP_QSTR_release_then_press), MP_ROM_PTR(&synthio_synthesizer_release_then_press_obj) },
{ MP_ROM_QSTR(MP_QSTR_release_all_then_press), MP_ROM_PTR(&synthio_synthesizer_release_all_then_press_obj) },
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&synthio_synthesizer_deinit_obj) },
{ MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) },
{ MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&synthio_synthesizer___exit___obj) },
// Properties
{ MP_ROM_QSTR(MP_QSTR_sample_rate), MP_ROM_PTR(&synthio_synthesizer_sample_rate_obj) },
{ MP_ROM_QSTR(MP_QSTR_pressed), MP_ROM_PTR(&synthio_synthesizer_pressed_obj) },
};
STATIC MP_DEFINE_CONST_DICT(synthio_synthesizer_locals_dict, synthio_synthesizer_locals_dict_table);
STATIC const audiosample_p_t synthio_synthesizer_proto = {
MP_PROTO_IMPLEMENT(MP_QSTR_protocol_audiosample)
.sample_rate = (audiosample_sample_rate_fun)common_hal_synthio_synthesizer_get_sample_rate,
.bits_per_sample = (audiosample_bits_per_sample_fun)common_hal_synthio_synthesizer_get_bits_per_sample,
.channel_count = (audiosample_channel_count_fun)common_hal_synthio_synthesizer_get_channel_count,
.reset_buffer = (audiosample_reset_buffer_fun)synthio_synthesizer_reset_buffer,
.get_buffer = (audiosample_get_buffer_fun)synthio_synthesizer_get_buffer,
.get_buffer_structure = (audiosample_get_buffer_structure_fun)synthio_synthesizer_get_buffer_structure,
};
const mp_obj_type_t synthio_synthesizer_type = {
{ &mp_type_type },
.name = MP_QSTR_Synthesizer,
.flags = MP_TYPE_FLAG_EXTENDED,
.make_new = synthio_synthesizer_make_new,
.locals_dict = (mp_obj_dict_t *)&synthio_synthesizer_locals_dict,
MP_TYPE_EXTENDED_FIELDS(
.protocol = &synthio_synthesizer_proto,
),
};

View File

@ -0,0 +1,45 @@
/*
* This file is part of the Micro Python project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Artyom Skrobov
* Copyright (c) 2023 Jeff Epler 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.
*/
#pragma once
#include "shared-module/synthio/Synthesizer.h"
extern const mp_obj_type_t synthio_synthesizer_type;
void common_hal_synthio_synthesizer_construct(synthio_synthesizer_obj_t *self,
uint32_t sample_rate, const int16_t *waveform, uint16_t waveform_len);
void common_hal_synthio_synthesizer_deinit(synthio_synthesizer_obj_t *self);
bool common_hal_synthio_synthesizer_deinited(synthio_synthesizer_obj_t *self);
uint32_t common_hal_synthio_synthesizer_get_sample_rate(synthio_synthesizer_obj_t *self);
uint8_t common_hal_synthio_synthesizer_get_bits_per_sample(synthio_synthesizer_obj_t *self);
uint8_t common_hal_synthio_synthesizer_get_channel_count(synthio_synthesizer_obj_t *self);
void common_hal_synthio_synthesizer_release(synthio_synthesizer_obj_t *self, mp_obj_t to_release);
void common_hal_synthio_synthesizer_press(synthio_synthesizer_obj_t *self, mp_obj_t to_press);
void common_hal_synthio_synthesizer_release_all(synthio_synthesizer_obj_t *self);
mp_obj_t common_hal_synthio_synthesizer_get_pressed_notes(synthio_synthesizer_obj_t *self);

View File

@ -30,9 +30,11 @@
#include "py/obj.h"
#include "py/runtime.h"
#include "extmod/vfs_fat.h"
#include "extmod/vfs_posix.h"
#include "shared-bindings/synthio/__init__.h"
#include "shared-bindings/synthio/MidiTrack.h"
#include "shared-bindings/synthio/Synthesizer.h"
//| """Support for MIDI synthesis"""
//|
@ -42,6 +44,7 @@
//|
//| :param typing.BinaryIO file: Already opened MIDI file
//| :param int sample_rate: The desired playback sample rate; higher sample rate requires more memory
//| :param ReadableBuffer waveform: A single-cycle waveform. Default is a 50% duty cycle square wave. If specified, must be a ReadableBuffer of type 'h' (signed 16 bit)
//|
//|
//| Playing a MIDI file from flash::
@ -62,10 +65,11 @@
//| ...
//|
STATIC mp_obj_t synthio_from_file(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_file, ARG_sample_rate };
enum { ARG_file, ARG_sample_rate, ARG_waveform };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_file, MP_ARG_OBJ | MP_ARG_REQUIRED },
{ MP_QSTR_sample_rate, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 11025} },
{ MP_QSTR_waveform, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_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);
@ -74,6 +78,10 @@ STATIC mp_obj_t synthio_from_file(size_t n_args, const mp_obj_t *pos_args, mp_ma
}
pyb_file_obj_t *file = MP_OBJ_TO_PTR(args[ARG_file].u_obj);
mp_buffer_info_t bufinfo_waveform;
synthio_synth_parse_waveform(&bufinfo_waveform, args[ARG_waveform].u_obj);
uint8_t chunk_header[14];
f_rewind(&file->fp);
UINT bytes_read;
@ -113,9 +121,13 @@ STATIC mp_obj_t synthio_from_file(size_t n_args, const mp_obj_t *pos_args, mp_ma
result->base.type = &synthio_miditrack_type;
common_hal_synthio_miditrack_construct(result, buffer, track_size,
tempo, args[ARG_sample_rate].u_int);
tempo, args[ARG_sample_rate].u_int, bufinfo_waveform.buf, bufinfo_waveform.len / 2);
#if MICROPY_MALLOC_USES_ALLOCATED_SIZE
m_free(buffer, track_size);
#else
m_free(buffer);
#endif
return MP_OBJ_FROM_PTR(result);
}
@ -125,6 +137,7 @@ MP_DEFINE_CONST_FUN_OBJ_KW(synthio_from_file_obj, 1, synthio_from_file);
STATIC const mp_rom_map_elem_t synthio_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_synthio) },
{ MP_ROM_QSTR(MP_QSTR_MidiTrack), MP_ROM_PTR(&synthio_miditrack_type) },
{ MP_ROM_QSTR(MP_QSTR_Synthesizer), MP_ROM_PTR(&synthio_synthesizer_type) },
{ MP_ROM_QSTR(MP_QSTR_from_file), MP_ROM_PTR(&synthio_from_file_obj) },
};

View File

@ -24,11 +24,6 @@
* THE SOFTWARE.
*/
#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_SYNTHIO___INIT___H
#define MICROPY_INCLUDED_SHARED_BINDINGS_SYNTHIO___INIT___H
#pragma once
#include "py/obj.h"
// Nothing now.
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_SYNTHIO___INIT___H
extern int16_t shared_bindings_synthio_square_wave[];

View File

@ -33,7 +33,10 @@
#include "py/runtime.h"
#include "shared-module/audiocore/__init__.h"
#include "shared-module/audiocore/RawSample.h"
#if defined(__arm__) && __arm__
#include "cmsis_compiler.h"
#endif
void common_hal_audiomixer_mixer_construct(audiomixer_mixer_obj_t *self,
uint8_t voice_count,
@ -140,7 +143,7 @@ static inline uint32_t mult16signed(uint32_t val, int32_t mul) {
float mod_mul = (float)mul / (float)((1 << 15) - 1);
for (int8_t i = 0; i < 2; i++) {
int16_t ai = (val >> (sizeof(uint16_t) * 8 * i));
int32_t intermediate = ai * mod_mul;
int32_t intermediate = (int32_t)(ai * mod_mul);
if (intermediate > SHRT_MAX) {
intermediate = SHRT_MAX;
} else if (intermediate < SHRT_MIN) {

View File

@ -42,12 +42,12 @@ void common_hal_audiomixer_mixervoice_set_parent(audiomixer_mixervoice_obj_t *se
self->parent = parent;
}
float common_hal_audiomixer_mixervoice_get_level(audiomixer_mixervoice_obj_t *self) {
return (float)self->level / (1 << 15);
mp_float_t common_hal_audiomixer_mixervoice_get_level(audiomixer_mixervoice_obj_t *self) {
return (mp_float_t)self->level / (1 << 15);
}
void common_hal_audiomixer_mixervoice_set_level(audiomixer_mixervoice_obj_t *self, float level) {
self->level = level * (1 << 15);
void common_hal_audiomixer_mixervoice_set_level(audiomixer_mixervoice_obj_t *self, mp_float_t level) {
self->level = (uint16_t)(level * (1 << 15));
}
void common_hal_audiomixer_mixervoice_play(audiomixer_mixervoice_obj_t *self, mp_obj_t sample, bool loop) {

View File

@ -27,10 +27,6 @@
#include "py/runtime.h"
#include "shared-bindings/synthio/MidiTrack.h"
#define LOUDNESS 0x4000 // 0.5
#define BITS_PER_SAMPLE 16
#define BYTES_PER_SAMPLE (BITS_PER_SAMPLE / 8)
#define SILENCE 0x80
STATIC NORETURN void raise_midi_stream_error(uint32_t pos) {
mp_raise_ValueError_varg(translate("Error in MIDI stream at position %d"), pos);
@ -47,36 +43,42 @@ STATIC uint8_t parse_note(const uint8_t *buffer, uint32_t len, uint32_t *pos) {
return note;
}
STATIC void terminate_span(synthio_miditrack_obj_t *self, uint16_t *dur, uint16_t *max_dur) {
STATIC void terminate_span(synthio_miditrack_obj_t *self, uint16_t *dur) {
if (*dur) {
self->track[self->total_spans - 1].dur = *dur;
if (*dur > *max_dur) {
*max_dur = *dur;
}
*dur = 0;
} else {
self->total_spans--;
}
}
STATIC void add_span(synthio_miditrack_obj_t *self, uint8_t note1, uint8_t note2) {
synthio_midi_span_t span = { 0, {note1, note2} };
self->track = m_realloc(self->track,
(self->total_spans + 1) * sizeof(synthio_midi_span_t));
self->track[self->total_spans++] = span;
STATIC void add_span(synthio_miditrack_obj_t *self, const synthio_midi_span_t *span) {
self->track = m_renew(synthio_midi_span_t, self->track, self->total_spans, self->total_spans + 1);
self->track[self->total_spans++] = *span;
}
STATIC void change_span_note(synthio_miditrack_obj_t *self, uint8_t old_note, uint8_t new_note, uint16_t *dur) {
synthio_midi_span_t span = self->track[self->total_spans - 1];
if (synthio_span_change_note(&span, old_note, new_note)) {
terminate_span(self, dur);
add_span(self, &span);
*dur = 0;
}
}
void common_hal_synthio_miditrack_construct(synthio_miditrack_obj_t *self,
const uint8_t *buffer, uint32_t len, uint32_t tempo, uint32_t sample_rate) {
const uint8_t *buffer, uint32_t len, uint32_t tempo, uint32_t sample_rate,
const int16_t *waveform, uint16_t waveform_length) {
synthio_midi_span_t initial = { 0, {SILENCE, SILENCE} };
self->sample_rate = sample_rate;
self->synth.sample_rate = sample_rate;
self->track = m_malloc(sizeof(synthio_midi_span_t), false);
synthio_span_init(self->track);
self->next_span = 0;
self->total_spans = 1;
*self->track = initial;
self->synth.waveform = waveform;
self->synth.waveform_length = waveform_length;
uint16_t dur = 0, max_dur = 0;
uint16_t dur = 0;
uint32_t pos = 0;
while (pos < len) {
uint8_t c;
@ -91,37 +93,19 @@ void common_hal_synthio_miditrack_construct(synthio_miditrack_obj_t *self,
raise_midi_stream_error(pos);
}
// dur is carried over here so that if a note on/off message doesn't actually produce a change, the
// underlying "span" is extended. Otherwise, it is zeroed out in the call to `terminate_span`.
dur += delta * sample_rate / tempo;
switch (buffer[pos++] >> 4) {
case 8: { // Note Off
uint8_t note = parse_note(buffer, len, &pos);
// Ignore if not a note which is playing
synthio_midi_span_t last_span = self->track[self->total_spans - 1];
if (last_span.note[0] == note || last_span.note[1] == note) {
terminate_span(self, &dur, &max_dur);
if (last_span.note[0] == note) {
add_span(self, last_span.note[1], SILENCE);
} else {
add_span(self, last_span.note[0], SILENCE);
}
}
change_span_note(self, note, SYNTHIO_SILENCE, &dur);
break;
}
case 9: { // Note On
uint8_t note = parse_note(buffer, len, &pos);
// Ignore if two notes are already playing
synthio_midi_span_t last_span = self->track[self->total_spans - 1];
if (last_span.note[1] == SILENCE) {
terminate_span(self, &dur, &max_dur);
if (last_span.note[0] == SILENCE) {
add_span(self, note, SILENCE);
} else {
add_span(self, last_span.note[0], note);
}
}
change_span_note(self, SYNTHIO_SILENCE, note, &dur);
break;
}
case 10:
@ -142,27 +126,29 @@ void common_hal_synthio_miditrack_construct(synthio_miditrack_obj_t *self,
raise_midi_stream_error(pos);
}
}
terminate_span(self, &dur, &max_dur);
terminate_span(self, &dur);
self->buffer_length = max_dur * BYTES_PER_SAMPLE;
self->buffer = m_malloc(self->buffer_length, false);
uint16_t max_dur = 0;
for (int i = 0; i < self->total_spans; i++) {
max_dur = MAX(self->track[i].dur, max_dur);
}
synthio_synth_init(&self->synth, max_dur);
}
void common_hal_synthio_miditrack_deinit(synthio_miditrack_obj_t *self) {
m_free(self->buffer);
self->buffer = NULL;
m_free(self->track);
synthio_synth_deinit(&self->synth);
m_del(synthio_midi_span_t, self->track, self->total_spans + 1);
self->track = NULL;
}
bool common_hal_synthio_miditrack_deinited(synthio_miditrack_obj_t *self) {
return self->buffer == NULL;
return synthio_synth_deinited(&self->synth);
}
uint32_t common_hal_synthio_miditrack_get_sample_rate(synthio_miditrack_obj_t *self) {
return self->sample_rate;
return self->synth.sample_rate;
}
uint8_t common_hal_synthio_miditrack_get_bits_per_sample(synthio_miditrack_obj_t *self) {
return BITS_PER_SAMPLE;
return SYNTHIO_BITS_PER_SAMPLE;
}
uint8_t common_hal_synthio_miditrack_get_channel_count(synthio_miditrack_obj_t *self) {
return 1;
@ -170,47 +156,29 @@ uint8_t common_hal_synthio_miditrack_get_channel_count(synthio_miditrack_obj_t *
void synthio_miditrack_reset_buffer(synthio_miditrack_obj_t *self,
bool single_channel_output, uint8_t channel) {
synthio_synth_reset_buffer(&self->synth, single_channel_output, channel);
self->synth.span.dur = 0;
self->next_span = 0;
}
STATIC const uint16_t notes[] = {8372, 8870, 9397, 9956, 10548, 11175, 11840,
12544, 13290, 14080, 14917, 15804}; // 9th octave
audioio_get_buffer_result_t synthio_miditrack_get_buffer(synthio_miditrack_obj_t *self,
bool single_channel_output, uint8_t channel, uint8_t **buffer, uint32_t *buffer_length) {
if (self->next_span >= self->total_spans) {
*buffer_length = 0;
return GET_BUFFER_DONE;
if (self->synth.span.dur == 0) {
if (self->next_span >= self->total_spans) {
*buffer_length = 0;
return GET_BUFFER_DONE;
}
self->synth.span = self->track[self->next_span++];
}
synthio_midi_span_t span = self->track[self->next_span++];
*buffer_length = span.dur * BYTES_PER_SAMPLE;
uint8_t octave1 = span.note[0] / 12; // 0..10
uint8_t octave2 = span.note[1] / 12; // 0..10
int32_t base_freq1 = notes[span.note[0] % 12];
int32_t base_freq2 = notes[span.note[1] % 12];
int32_t sample_rate = self->sample_rate;
synthio_synth_synthesize(&self->synth, buffer, buffer_length, single_channel_output ? 0 : channel);
for (uint16_t i = 0; i < span.dur; i++) {
int16_t semiperiod1 = span.note[0] == SILENCE ? 0 :
((base_freq1 * i * 2) / sample_rate) >> (10 - octave1);
int16_t semiperiod2 = span.note[1] == SILENCE ? semiperiod1 :
((base_freq2 * i * 2) / sample_rate) >> (10 - octave2);
self->buffer[i] = ((semiperiod1 % 2 + semiperiod2 % 2) - 1) * LOUDNESS;
}
*buffer = (uint8_t *)self->buffer;
return self->next_span >= self->total_spans ?
return (self->synth.span.dur == 0 && self->next_span >= self->total_spans) ?
GET_BUFFER_DONE : GET_BUFFER_MORE_DATA;
}
void synthio_miditrack_get_buffer_structure(synthio_miditrack_obj_t *self, bool single_channel_output,
bool *single_buffer, bool *samples_signed, uint32_t *max_buffer_length, uint8_t *spacing) {
*single_buffer = true;
*samples_signed = true;
*max_buffer_length = self->buffer_length;
*spacing = 1;
return synthio_synth_get_buffer_structure(&self->synth, single_channel_output, single_buffer, samples_signed, max_buffer_length, spacing);
}

View File

@ -31,19 +31,12 @@
#include "shared-module/synthio/__init__.h"
typedef struct {
uint16_t dur;
uint8_t note[2];
} synthio_midi_span_t;
typedef struct {
mp_obj_base_t base;
uint32_t sample_rate;
uint16_t *buffer;
uint16_t buffer_length;
synthio_midi_span_t *track;
synthio_synth_t synth;
uint16_t next_span;
uint16_t total_spans;
synthio_midi_span_t *track;
} synthio_miditrack_obj_t;

View File

@ -0,0 +1,105 @@
/*
* This file is part of the Micro Python project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Artyom Skrobov
*
* 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 "py/runtime.h"
#include "shared-bindings/synthio/Synthesizer.h"
void common_hal_synthio_synthesizer_construct(synthio_synthesizer_obj_t *self,
uint32_t sample_rate, const int16_t *waveform, uint16_t waveform_length) {
self->synth.sample_rate = sample_rate;
self->synth.waveform = waveform;
self->synth.waveform_length = waveform_length;
synthio_synth_init(&self->synth, SYNTHIO_MAX_DUR);
common_hal_synthio_synthesizer_release_all(self);
}
void common_hal_synthio_synthesizer_deinit(synthio_synthesizer_obj_t *self) {
synthio_synth_deinit(&self->synth);
}
bool common_hal_synthio_synthesizer_deinited(synthio_synthesizer_obj_t *self) {
return synthio_synth_deinited(&self->synth);
}
uint32_t common_hal_synthio_synthesizer_get_sample_rate(synthio_synthesizer_obj_t *self) {
return self->synth.sample_rate;
}
uint8_t common_hal_synthio_synthesizer_get_bits_per_sample(synthio_synthesizer_obj_t *self) {
return SYNTHIO_BITS_PER_SAMPLE;
}
uint8_t common_hal_synthio_synthesizer_get_channel_count(synthio_synthesizer_obj_t *self) {
return 1;
}
void synthio_synthesizer_reset_buffer(synthio_synthesizer_obj_t *self,
bool single_channel_output, uint8_t channel) {
synthio_synth_reset_buffer(&self->synth, single_channel_output, channel);
}
audioio_get_buffer_result_t synthio_synthesizer_get_buffer(synthio_synthesizer_obj_t *self,
bool single_channel_output, uint8_t channel, uint8_t **buffer, uint32_t *buffer_length) {
self->synth.span.dur = SYNTHIO_MAX_DUR;
synthio_synth_synthesize(&self->synth, buffer, buffer_length, single_channel_output ? channel : 0);
return GET_BUFFER_MORE_DATA;
}
void synthio_synthesizer_get_buffer_structure(synthio_synthesizer_obj_t *self, bool single_channel_output,
bool *single_buffer, bool *samples_signed, uint32_t *max_buffer_length, uint8_t *spacing) {
return synthio_synth_get_buffer_structure(&self->synth, single_channel_output, single_buffer, samples_signed, max_buffer_length, spacing);
}
void common_hal_synthio_synthesizer_release_all(synthio_synthesizer_obj_t *self) {
synthio_span_init(&self->synth.span);
}
void common_hal_synthio_synthesizer_release(synthio_synthesizer_obj_t *self, mp_obj_t to_release) {
mp_obj_iter_buf_t iter_buf;
mp_obj_t iterable = mp_getiter(to_release, &iter_buf);
mp_obj_t item;
while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
synthio_span_change_note(&self->synth.span, mp_arg_validate_int_range(mp_obj_get_int(item), 0, 127, MP_QSTR_note), SYNTHIO_SILENCE);
}
}
void common_hal_synthio_synthesizer_press(synthio_synthesizer_obj_t *self, mp_obj_t to_press) {
mp_obj_iter_buf_t iter_buf;
mp_obj_t iterable = mp_getiter(to_press, &iter_buf);
mp_obj_t item;
while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
synthio_span_change_note(&self->synth.span, SYNTHIO_SILENCE, mp_arg_validate_int_range(mp_obj_get_int(item), 0, 127, MP_QSTR_note));
}
}
mp_obj_t common_hal_synthio_synthesizer_get_pressed_notes(synthio_synthesizer_obj_t *self) {
mp_obj_tuple_t *result = MP_OBJ_TO_PTR(mp_obj_new_tuple(synthio_span_count_active_channels(&self->synth.span), NULL));
for (size_t i = 0, j = 0; i < CIRCUITPY_SYNTHIO_MAX_CHANNELS; i++) {
if (self->synth.span.note[i] != SYNTHIO_SILENCE) {
result->items[j++] = MP_OBJ_NEW_SMALL_INT(self->synth.span.note[i]);
}
}
return MP_OBJ_FROM_PTR(result);
}

View File

@ -0,0 +1,53 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Artyom Skrobov
* Copyright (c) 2023 Jeff Epler 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.
*/
#pragma once
#include "py/obj.h"
#include "shared-module/synthio/__init__.h"
typedef struct {
mp_obj_base_t base;
synthio_synth_t synth;
} synthio_synthesizer_obj_t;
// These are not available from Python because it may be called in an interrupt.
void synthio_synthesizer_reset_buffer(synthio_synthesizer_obj_t *self,
bool single_channel_output,
uint8_t channel);
audioio_get_buffer_result_t synthio_synthesizer_get_buffer(synthio_synthesizer_obj_t *self,
bool single_channel_output,
uint8_t channel,
uint8_t **buffer,
uint32_t *buffer_length); // length in bytes
void synthio_synthesizer_get_buffer_structure(synthio_synthesizer_obj_t *self, bool single_channel_output,
bool *single_buffer, bool *samples_signed,
uint32_t *max_buffer_length, uint8_t *spacing);

View File

@ -0,0 +1,169 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Artyom Skrobov
* Copyright (c) 2023 Jeff Epler 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/synthio/__init__.h"
#include "py/runtime.h"
STATIC const int16_t square_wave[] = {-32768, 32767};
STATIC const uint16_t notes[] = {8372, 8870, 9397, 9956, 10548, 11175, 11840,
12544, 13290, 14080, 14917, 15804}; // 9th octave
int synthio_span_count_active_channels(synthio_midi_span_t *span) {
int result = 0;
for (int i = 0; i < CIRCUITPY_SYNTHIO_MAX_CHANNELS; i++) {
if (span->note[i] != SYNTHIO_SILENCE) {
result += 1;
}
}
return result;
}
void synthio_synth_synthesize(synthio_synth_t *synth, uint8_t **bufptr, uint32_t *buffer_length, uint8_t channel) {
if (channel == synth->other_channel) {
*buffer_length = synth->last_buffer_length;
*bufptr = (uint8_t *)(synth->buffers[synth->other_buffer_index] + channel);
return;
}
synth->buffer_index = !synth->buffer_index;
synth->other_channel = 1 - channel;
synth->other_buffer_index = synth->buffer_index;
int16_t *out_buffer = (int16_t *)(void *)synth->buffers[synth->buffer_index];
uint16_t dur = MIN(SYNTHIO_MAX_DUR, synth->span.dur);
synth->span.dur -= dur;
memset(out_buffer, 0, synth->buffer_length);
int32_t sample_rate = synth->sample_rate;
int active_channels = synthio_span_count_active_channels(&synth->span);
const int16_t *waveform = synth->waveform;
uint32_t waveform_length = synth->waveform_length;
if (active_channels) {
int16_t loudness = 0x3fff / (1 + active_channels);
for (int chan = 0; chan < CIRCUITPY_SYNTHIO_MAX_CHANNELS; chan++) {
if (synth->span.note[chan] == SYNTHIO_SILENCE) {
synth->accum[chan] = 0;
continue;
}
uint8_t octave = synth->span.note[chan] / 12;
uint16_t base_freq = notes[synth->span.note[chan] % 12];
uint32_t accum = synth->accum[chan];
#define SHIFT (16)
// rate = base_freq * waveform_length
// den = sample_rate * 2 ^ (10 - octave)
// den = sample_rate * 2 ^ 10 / 2^octave
// dds_rate = 2^SHIFT * rate / den
// dds_rate = 2^(SHIFT-10+octave) * base_freq * waveform_length / sample_rate
uint32_t dds_rate = (sample_rate / 2 + ((uint64_t)(base_freq * waveform_length) << (SHIFT - 10 + octave))) / sample_rate;
for (uint16_t i = 0; i < dur; i++) {
accum += dds_rate;
if (accum > waveform_length << SHIFT) {
accum -= waveform_length << SHIFT;
}
int16_t idx = accum >> SHIFT;
out_buffer[i] += (waveform[idx] * loudness) / 65536;
}
synth->accum[chan] = accum;
}
}
*buffer_length = synth->last_buffer_length = dur * SYNTHIO_BYTES_PER_SAMPLE;
*bufptr = (uint8_t *)out_buffer;
}
void synthio_synth_reset_buffer(synthio_synth_t *synth, bool single_channel_output, uint8_t channel) {
if (single_channel_output && channel == 1) {
return;
}
synth->other_channel = -1;
}
bool synthio_synth_deinited(synthio_synth_t *synth) {
return synth->buffers[0] == NULL;
}
void synthio_synth_deinit(synthio_synth_t *synth) {
m_del(uint8_t, synth->buffers[0], synth->buffer_length);
m_del(uint8_t, synth->buffers[1], synth->buffer_length);
synth->buffers[0] = NULL;
synth->buffers[1] = NULL;
}
void synthio_synth_init(synthio_synth_t *synth, uint16_t max_dur) {
synth->buffer_length = MIN(SYNTHIO_MAX_DUR, max_dur) * SYNTHIO_BYTES_PER_SAMPLE;
synth->buffers[0] = m_malloc(synth->buffer_length, false);
synth->buffers[1] = m_malloc(synth->buffer_length, false);
synth->other_channel = -1;
}
void synthio_synth_get_buffer_structure(synthio_synth_t *synth, bool single_channel_output,
bool *single_buffer, bool *samples_signed, uint32_t *max_buffer_length, uint8_t *spacing) {
*single_buffer = false;
*samples_signed = true;
*max_buffer_length = synth->buffer_length;
*spacing = 1;
}
void synthio_synth_parse_waveform(mp_buffer_info_t *bufinfo_waveform, mp_obj_t waveform_obj) {
*bufinfo_waveform = ((mp_buffer_info_t) { .buf = (void *)square_wave, .len = 4 });
if (waveform_obj != mp_const_none) {
mp_get_buffer_raise(waveform_obj, bufinfo_waveform, MP_BUFFER_READ);
if (bufinfo_waveform->typecode != 'h') {
mp_raise_ValueError_varg(translate("%q must be array of type 'h'"), MP_QSTR_waveform);
}
}
mp_arg_validate_length_range(bufinfo_waveform->len / 2, 2, 1024, MP_QSTR_waveform);
}
void synthio_span_init(synthio_midi_span_t *span) {
span->dur = 0;
for (size_t i = 0; i < CIRCUITPY_SYNTHIO_MAX_CHANNELS; i++) { span->note[i] = SYNTHIO_SILENCE;
}
}
STATIC int find_channel_with_note(const synthio_midi_span_t *span, uint8_t note) {
for (int i = 0; i < CIRCUITPY_SYNTHIO_MAX_CHANNELS; i++) {
if (span->note[i] == note) {
return i;
}
}
return -1;
}
bool synthio_span_change_note(synthio_midi_span_t *span, uint8_t old_note, uint8_t new_note) {
int channel = find_channel_with_note(span, old_note);
if (channel != -1) {
span->note[channel] = new_note;
return true;
}
return false;
}

View File

@ -24,9 +24,41 @@
* THE SOFTWARE.
*/
#ifndef MICROPY_INCLUDED_SHARED_MODULE_SYNTHIO__INIT__H
#define MICROPY_INCLUDED_SHARED_MODULE_SYNTHIO__INIT__H
#pragma once
#define SYNTHIO_BITS_PER_SAMPLE (16)
#define SYNTHIO_BYTES_PER_SAMPLE (SYNTHIO_BITS_PER_SAMPLE / 8)
#define SYNTHIO_MAX_DUR (256)
#define SYNTHIO_SILENCE (0x80)
#include "shared-module/audiocore/__init__.h"
#endif // MICROPY_INCLUDED_SHARED_MODULE_SYNTHIO__INIT__H
typedef struct {
uint16_t dur;
uint8_t note[CIRCUITPY_SYNTHIO_MAX_CHANNELS];
} synthio_midi_span_t;
typedef struct {
uint32_t sample_rate;
int16_t *buffers[2];
const int16_t *waveform;
uint16_t buffer_length;
uint16_t last_buffer_length;
uint8_t other_channel, buffer_index, other_buffer_index;
uint16_t waveform_length;
synthio_midi_span_t span;
uint32_t accum[CIRCUITPY_SYNTHIO_MAX_CHANNELS];
} synthio_synth_t;
void synthio_span_init(synthio_midi_span_t *span);
void synthio_synth_synthesize(synthio_synth_t *synth, uint8_t **buffer, uint32_t *buffer_length, uint8_t channel);
void synthio_synth_deinit(synthio_synth_t *synth);
bool synthio_synth_deinited(synthio_synth_t *synth);
void synthio_synth_init(synthio_synth_t *synth, uint16_t max_dur);
void synthio_synth_get_buffer_structure(synthio_synth_t *synth, bool single_channel_output,
bool *single_buffer, bool *samples_signed, uint32_t *max_buffer_length, uint8_t *spacing);
void synthio_synth_reset_buffer(synthio_synth_t *synth, bool single_channel_output, uint8_t channel);
void synthio_synth_parse_waveform(mp_buffer_info_t *bufinfo_waveform, mp_obj_t waveform_obj);
bool synthio_span_change_note(synthio_midi_span_t *span, uint8_t old_note, uint8_t new_note);
int synthio_span_count_active_channels(synthio_midi_span_t *span);

View File

@ -0,0 +1,20 @@
import array
try:
from synthio import MidiTrack
from audiocore import get_buffer, get_structure
except ImportError:
print("SKIP")
raise SystemExit
SCORE = b"\0\x90@\0\x20\x90b\0\x20\x80@\0\0\x80\b\0"
with MidiTrack(SCORE, sample_rate=8000, tempo=640) as m:
print(get_structure(m))
print(get_buffer(m))
with MidiTrack(
SCORE, sample_rate=8000, tempo=640, waveform=array.array("h", [0, 32767, 0, -32768])
) as m:
print(get_structure(m))
print(get_buffer(m))

View File

@ -0,0 +1,4 @@
(0, 1, 512, 1)
(1, b'\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\xff\x0f\xff\x0f')
(0, 1, 512, 1)
(1, b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x01\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\xff\x0f\x00\x00\x00\x00')

View File

@ -0,0 +1,24 @@
import struct
import synthio
import audiocore
def dump_samples():
print(struct.unpack("12h", audiocore.get_buffer(s)[1][:24]))
s = synthio.Synthesizer(sample_rate=8000)
print(s.pressed)
dump_samples()
s.press((80,))
print(s.pressed)
dump_samples()
s.press((91,))
print(s.pressed)
dump_samples()
s.release_then_press((80,))
print(s.pressed)
dump_samples()

View File

@ -0,0 +1,8 @@
()
(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
(80,)
(-4095, -4095, -4095, -4095, 4095, 4095, 4095, 4095, 4095, -4095, -4095, -4095)
(80, 91)
(0, 0, 5460, 5460, 0, -5460, -5460, 0, 5460, 5460, 0, 0)
(91,)
(-4095, 4095, 4095, 4095, -4095, -4095, 4095, 4095, 4095, -4095, -4095, 4095)

View File

@ -30,20 +30,21 @@ ame__
mport
builtins micropython _asyncio _thread
_uasyncio aesio array binascii
bitmaptools btree cexample cmath
collections cppexample displayio errno
ffi framebuf gc hashlib
json math qrio rainbowio
re struct sys termios
traceback ubinascii uctypes uerrno
uheapq uio ujson ulab
ulab.numpy ulab.numpy.fft ulab.numpy.linalg
ulab.scipy ulab.scipy.linalg
ulab.scipy.optimize ulab.scipy.signal
ulab.scipy.special ulab.utils uos
urandom ure uselect utime
utimeq uzlib zlib
_uasyncio aesio array audiocore
audiomixer binascii bitmaptools btree
cexample cmath collections cppexample
displayio errno ffi framebuf
gc hashlib json math
qrio rainbowio re struct
synthio sys termios traceback
ubinascii uctypes uerrno uheapq
uio ujson ulab ulab.numpy
ulab.numpy.fft ulab.numpy.linalg ulab.scipy
ulab.scipy.linalg ulab.scipy.optimize
ulab.scipy.signal ulab.scipy.special
ulab.utils uos urandom ure
uselect utime utimeq uzlib
zlib
ime
utime utimeq