synthio: Add LFOs
This commit is contained in:
parent
827eaeb1f9
commit
f83212314e
@ -42,6 +42,7 @@ SRC_BITMAP := \
|
||||
shared-bindings/struct/__init__.c \
|
||||
shared-bindings/synthio/__init__.c \
|
||||
shared-bindings/synthio/MidiTrack.c \
|
||||
shared-bindings/synthio/LFO.c \
|
||||
shared-bindings/synthio/Note.c \
|
||||
shared-bindings/synthio/Synthesizer.c \
|
||||
shared-bindings/traceback/__init__.c \
|
||||
@ -65,6 +66,7 @@ SRC_BITMAP := \
|
||||
shared-module/struct/__init__.c \
|
||||
shared-module/synthio/__init__.c \
|
||||
shared-module/synthio/MidiTrack.c \
|
||||
shared-module/synthio/LFO.c \
|
||||
shared-module/synthio/Note.c \
|
||||
shared-module/synthio/Synthesizer.c \
|
||||
shared-module/traceback/__init__.c \
|
||||
|
@ -650,6 +650,7 @@ SRC_SHARED_MODULE_ALL = \
|
||||
struct/__init__.c \
|
||||
supervisor/__init__.c \
|
||||
supervisor/StatusBar.c \
|
||||
synthio/LFO.c \
|
||||
synthio/MidiTrack.c \
|
||||
synthio/Note.c \
|
||||
synthio/Synthesizer.c \
|
||||
|
234
shared-bindings/synthio/LFO.c
Normal file
234
shared-bindings/synthio/LFO.c
Normal file
@ -0,0 +1,234 @@
|
||||
/*
|
||||
* 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 "py/obj.h"
|
||||
#include "py/objproperty.h"
|
||||
#include "py/runtime.h"
|
||||
#include "shared-bindings/util.h"
|
||||
#include "shared-bindings/synthio/LFO.h"
|
||||
#include "shared-module/synthio/LFO.h"
|
||||
|
||||
//| class LFO:
|
||||
//| """A low-frequency oscillator
|
||||
//|
|
||||
//| Every `rate` seconds, the output of the LFO cycles through its `waveform`.
|
||||
//| The output at any particular moment is ``waveform[idx] * scale + offset``.
|
||||
//| Internally, the calculation takes place in fixed point for speed.
|
||||
//|
|
||||
//| `rate`, `offset`, `scale`, and `once` can be changed at run-time.
|
||||
//|
|
||||
//| An LFO only updates if it is actually associated with a playing Note,
|
||||
//| including if it is indirectly associated with the Note via an intermediate
|
||||
//| LFO.
|
||||
//|
|
||||
//| Using the same LFO as an input to multiple other LFOs or Notes is OK, but
|
||||
//| the result if an LFO is tied to multiple Synthtesizer objects is undefined."""
|
||||
//|
|
||||
//| def __init__(
|
||||
//| self,
|
||||
//| waveform: ReadableBuffer,
|
||||
//| *,
|
||||
//| rate: BlockInput = 1.0,
|
||||
//| scale: BlockInput = 1.0,
|
||||
//| offset: BlockInput = 0,
|
||||
//| once=False
|
||||
//| ):
|
||||
//| pass
|
||||
static const mp_arg_t lfo_properties[] = {
|
||||
{ MP_QSTR_waveform, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = NULL } },
|
||||
{ MP_QSTR_rate, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_INT(1) } },
|
||||
{ MP_QSTR_scale, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_INT(1) } },
|
||||
{ MP_QSTR_offset, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_INT(0) } },
|
||||
{ MP_QSTR_once, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_INT(0) } },
|
||||
};
|
||||
|
||||
STATIC mp_obj_t synthio_lfo_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
|
||||
enum { ARG_waveform }; // others never directly referred to by argument number
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(lfo_properties)];
|
||||
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(lfo_properties), lfo_properties, args);
|
||||
|
||||
synthio_lfo_obj_t *self = m_new_obj(synthio_lfo_obj_t);
|
||||
self->base.type = &synthio_lfo_type;
|
||||
|
||||
synthio_synth_parse_waveform(&self->waveform_bufinfo, args[ARG_waveform].u_obj);
|
||||
self->waveform_obj = args[ARG_waveform].u_obj;
|
||||
self->last_tick = synthio_global_tick;
|
||||
|
||||
mp_obj_t result = MP_OBJ_FROM_PTR(self);
|
||||
properties_construct_helper(result, lfo_properties + 1, args + 1, MP_ARRAY_SIZE(lfo_properties) - 1);
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
//| waveform: Optional[ReadableBuffer]
|
||||
//| """The waveform of this lfo. (read-only, but the values in the buffer may be modified dynamically)"""
|
||||
STATIC mp_obj_t synthio_lfo_get_waveform(mp_obj_t self_in) {
|
||||
synthio_lfo_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
return common_hal_synthio_lfo_get_waveform_obj(self);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(synthio_lfo_get_waveform_obj, synthio_lfo_get_waveform);
|
||||
|
||||
MP_PROPERTY_GETTER(synthio_lfo_waveform_obj,
|
||||
(mp_obj_t)&synthio_lfo_get_waveform_obj);
|
||||
|
||||
//| rate: BlockInput
|
||||
//| """The rate (in Hz) at which the LFO cycles through its waveform"""
|
||||
STATIC mp_obj_t synthio_lfo_get_rate(mp_obj_t self_in) {
|
||||
synthio_lfo_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
return common_hal_synthio_lfo_get_rate_obj(self);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(synthio_lfo_get_rate_obj, synthio_lfo_get_rate);
|
||||
|
||||
STATIC mp_obj_t synthio_lfo_set_rate(mp_obj_t self_in, mp_obj_t arg) {
|
||||
synthio_lfo_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
common_hal_synthio_lfo_set_rate_obj(self, arg);
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(synthio_lfo_set_rate_obj, synthio_lfo_set_rate);
|
||||
MP_PROPERTY_GETSET(synthio_lfo_rate_obj,
|
||||
(mp_obj_t)&synthio_lfo_get_rate_obj,
|
||||
(mp_obj_t)&synthio_lfo_set_rate_obj);
|
||||
|
||||
|
||||
//| offset: BlockInput
|
||||
//| """An additive value applied to the LFO's output"""
|
||||
STATIC mp_obj_t synthio_lfo_get_offset(mp_obj_t self_in) {
|
||||
synthio_lfo_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
return common_hal_synthio_lfo_get_offset_obj(self);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(synthio_lfo_get_offset_obj, synthio_lfo_get_offset);
|
||||
|
||||
STATIC mp_obj_t synthio_lfo_set_offset(mp_obj_t self_in, mp_obj_t arg) {
|
||||
synthio_lfo_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
common_hal_synthio_lfo_set_offset_obj(self, arg);
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(synthio_lfo_set_offset_obj, synthio_lfo_set_offset);
|
||||
MP_PROPERTY_GETSET(synthio_lfo_offset_obj,
|
||||
(mp_obj_t)&synthio_lfo_get_offset_obj,
|
||||
(mp_obj_t)&synthio_lfo_set_offset_obj);
|
||||
|
||||
//| scale: BlockInput
|
||||
//| """An additive value applied to the LFO's output"""
|
||||
STATIC mp_obj_t synthio_lfo_get_scale(mp_obj_t self_in) {
|
||||
synthio_lfo_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
return common_hal_synthio_lfo_get_scale_obj(self);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(synthio_lfo_get_scale_obj, synthio_lfo_get_scale);
|
||||
|
||||
STATIC mp_obj_t synthio_lfo_set_scale(mp_obj_t self_in, mp_obj_t arg) {
|
||||
synthio_lfo_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
common_hal_synthio_lfo_set_scale_obj(self, arg);
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(synthio_lfo_set_scale_obj, synthio_lfo_set_scale);
|
||||
MP_PROPERTY_GETSET(synthio_lfo_scale_obj,
|
||||
(mp_obj_t)&synthio_lfo_get_scale_obj,
|
||||
(mp_obj_t)&synthio_lfo_set_scale_obj);
|
||||
|
||||
//|
|
||||
//| once: bool
|
||||
//| """True if the waveform should stop when it reaches its last output value, false if it should re-start at the beginning of its waveform"""
|
||||
STATIC mp_obj_t synthio_lfo_get_once(mp_obj_t self_in) {
|
||||
synthio_lfo_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
return mp_obj_new_bool(common_hal_synthio_lfo_get_once(self));
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(synthio_lfo_get_once_obj, synthio_lfo_get_once);
|
||||
|
||||
STATIC mp_obj_t synthio_lfo_set_once(mp_obj_t self_in, mp_obj_t arg) {
|
||||
synthio_lfo_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
common_hal_synthio_lfo_set_once(self, mp_obj_is_true(arg));
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(synthio_lfo_set_once_obj, synthio_lfo_set_once);
|
||||
MP_PROPERTY_GETSET(synthio_lfo_once_obj,
|
||||
(mp_obj_t)&synthio_lfo_get_once_obj,
|
||||
(mp_obj_t)&synthio_lfo_set_once_obj);
|
||||
|
||||
|
||||
//|
|
||||
//| phase: float
|
||||
//| """The phase of the oscillator, in the range 0 to 1 (read-only)"""
|
||||
STATIC mp_obj_t synthio_lfo_get_phase(mp_obj_t self_in) {
|
||||
synthio_lfo_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
return mp_obj_new_float(common_hal_synthio_lfo_get_phase(self));
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(synthio_lfo_get_phase_obj, synthio_lfo_get_phase);
|
||||
|
||||
MP_PROPERTY_GETTER(synthio_lfo_phase_obj,
|
||||
(mp_obj_t)&synthio_lfo_get_phase_obj);
|
||||
|
||||
|
||||
//|
|
||||
//| value: float
|
||||
//| """The value of the oscillator (read-only)"""
|
||||
STATIC mp_obj_t synthio_lfo_get_value(mp_obj_t self_in) {
|
||||
synthio_lfo_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
return mp_obj_new_float(common_hal_synthio_lfo_get_value(self));
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(synthio_lfo_get_value_obj, synthio_lfo_get_value);
|
||||
|
||||
MP_PROPERTY_GETTER(synthio_lfo_value_obj,
|
||||
(mp_obj_t)&synthio_lfo_get_value_obj);
|
||||
|
||||
|
||||
//|
|
||||
//| def retrigger(self):
|
||||
//| """Reset the LFO's internal index to the start of the waveform. Most useful when it its `once` property is `True`."""
|
||||
//|
|
||||
STATIC mp_obj_t synthio_lfo_retrigger(mp_obj_t self_in) {
|
||||
synthio_lfo_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
common_hal_synthio_lfo_retrigger(self);
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(synthio_lfo_retrigger_obj, synthio_lfo_retrigger);
|
||||
|
||||
static void lfo_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
|
||||
(void)kind;
|
||||
properties_print_helper(print, self_in, lfo_properties, MP_ARRAY_SIZE(lfo_properties));
|
||||
}
|
||||
|
||||
STATIC const mp_rom_map_elem_t synthio_lfo_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_waveform), MP_ROM_PTR(&synthio_lfo_waveform_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_rate), MP_ROM_PTR(&synthio_lfo_rate_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_scale), MP_ROM_PTR(&synthio_lfo_scale_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_offset), MP_ROM_PTR(&synthio_lfo_offset_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_once), MP_ROM_PTR(&synthio_lfo_once_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&synthio_lfo_value_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_phase), MP_ROM_PTR(&synthio_lfo_phase_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_retrigger), MP_ROM_PTR(&synthio_lfo_retrigger_obj) },
|
||||
};
|
||||
STATIC MP_DEFINE_CONST_DICT(synthio_lfo_locals_dict, synthio_lfo_locals_dict_table);
|
||||
|
||||
const mp_obj_type_t synthio_lfo_type = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_LFO,
|
||||
.make_new = synthio_lfo_make_new,
|
||||
.locals_dict = (mp_obj_dict_t *)&synthio_lfo_locals_dict,
|
||||
.print = lfo_print,
|
||||
};
|
53
shared-bindings/synthio/LFO.h
Normal file
53
shared-bindings/synthio/LFO.h
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* This file is part of the Micro Python project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* 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"
|
||||
|
||||
typedef struct synthio_lfo_obj synthio_lfo_obj_t;
|
||||
extern const mp_obj_type_t synthio_lfo_type;
|
||||
|
||||
mp_obj_t common_hal_synthio_lfo_get_waveform_obj(synthio_lfo_obj_t *self);
|
||||
void common_hal_synthio_lfo_set_waveform_obj(synthio_lfo_obj_t *self, mp_obj_t arg);
|
||||
|
||||
mp_obj_t common_hal_synthio_lfo_get_rate_obj(synthio_lfo_obj_t *self);
|
||||
void common_hal_synthio_lfo_set_rate_obj(synthio_lfo_obj_t *self, mp_obj_t arg);
|
||||
|
||||
mp_obj_t common_hal_synthio_lfo_get_scale_obj(synthio_lfo_obj_t *self);
|
||||
void common_hal_synthio_lfo_set_scale_obj(synthio_lfo_obj_t *self, mp_obj_t arg);
|
||||
|
||||
mp_obj_t common_hal_synthio_lfo_get_offset_obj(synthio_lfo_obj_t *self);
|
||||
void common_hal_synthio_lfo_set_offset_obj(synthio_lfo_obj_t *self, mp_obj_t arg);
|
||||
|
||||
bool common_hal_synthio_lfo_get_once(synthio_lfo_obj_t *self);
|
||||
void common_hal_synthio_lfo_set_once(synthio_lfo_obj_t *self, bool arg);
|
||||
|
||||
mp_float_t common_hal_synthio_lfo_get_value(synthio_lfo_obj_t *self);
|
||||
|
||||
mp_float_t common_hal_synthio_lfo_get_phase(synthio_lfo_obj_t *self);
|
||||
|
||||
void common_hal_synthio_lfo_retrigger(synthio_lfo_obj_t *self);
|
@ -37,15 +37,13 @@
|
||||
static const mp_arg_t note_properties[] = {
|
||||
{ MP_QSTR_frequency, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = NULL } },
|
||||
{ MP_QSTR_panning, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_INT(0) } },
|
||||
{ MP_QSTR_tremolo_rate, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = NULL } },
|
||||
{ MP_QSTR_tremolo_depth, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = NULL } },
|
||||
{ MP_QSTR_bend_rate, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = NULL } },
|
||||
{ MP_QSTR_bend_depth, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = NULL } },
|
||||
{ MP_QSTR_bend_mode, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = (mp_obj_t)MP_ROM_PTR(&bend_mode_VIBRATO_obj) } },
|
||||
{ MP_QSTR_amplitude, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_INT(1) } },
|
||||
{ MP_QSTR_bend, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_INT(0) } },
|
||||
{ MP_QSTR_waveform, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_envelope, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_filter, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_INT(1) } },
|
||||
{ MP_QSTR_ring_frequency, MP_ARG_OBJ, {.u_obj = NULL } },
|
||||
{ MP_QSTR_ring_frequency, MP_ARG_OBJ, {.u_obj = MP_ROM_INT(0) } },
|
||||
{ MP_QSTR_ring_bend, MP_ARG_OBJ, {.u_obj = MP_ROM_INT(0) } },
|
||||
{ MP_QSTR_ring_waveform, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_NONE } },
|
||||
};
|
||||
//| class Note:
|
||||
@ -53,14 +51,11 @@ static const mp_arg_t note_properties[] = {
|
||||
//| self,
|
||||
//| *,
|
||||
//| frequency: float,
|
||||
//| panning: float = 0.0,
|
||||
//| panning: BlockInput = 0.0,
|
||||
//| waveform: Optional[ReadableBuffer] = None,
|
||||
//| envelope: Optional[Envelope] = None,
|
||||
//| tremolo_depth: float = 0.0,
|
||||
//| tremolo_rate: float = 0.0,
|
||||
//| bend_depth: float = 0.0,
|
||||
//| bend_rate: float = 0.0,
|
||||
//| bend_mode: "BendMode" = BendMode.VIBRATO,
|
||||
//| amplitude: BlockInput = 0.0,
|
||||
//| bend: BlockInput = 0.0,
|
||||
//| ) -> None:
|
||||
//| """Construct a Note object, with a frequency in Hz, and optional panning, waveform, envelope, tremolo (volume change) and bend (frequency change).
|
||||
//|
|
||||
@ -76,16 +71,12 @@ STATIC mp_obj_t synthio_note_make_new(const mp_obj_type_t *type_in, size_t n_arg
|
||||
self->base.type = &synthio_note_type;
|
||||
|
||||
mp_obj_t result = MP_OBJ_FROM_PTR(self);
|
||||
for (size_t i = 0; i < MP_ARRAY_SIZE(note_properties); i++) {
|
||||
if (args[i].u_obj != NULL) {
|
||||
mp_store_attr(result, note_properties[i].qst, args[i].u_obj);
|
||||
}
|
||||
}
|
||||
properties_construct_helper(result, note_properties, args, MP_ARRAY_SIZE(note_properties));
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
//| frequency: float
|
||||
//| frequency: BlockInput
|
||||
//| """The base frequency of the note, in Hz."""
|
||||
STATIC mp_obj_t synthio_note_get_frequency(mp_obj_t self_in) {
|
||||
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
@ -121,7 +112,7 @@ MP_PROPERTY_GETSET(synthio_note_filter_obj,
|
||||
(mp_obj_t)&synthio_note_get_filter_obj,
|
||||
(mp_obj_t)&synthio_note_set_filter_obj);
|
||||
|
||||
//| panning: float
|
||||
//| panning: BlockInput
|
||||
//| """Defines the channel(s) in which the note appears.
|
||||
//|
|
||||
//| -1 is left channel only, 0 is both channels, and 1 is right channel.
|
||||
@ -130,13 +121,13 @@ MP_PROPERTY_GETSET(synthio_note_filter_obj,
|
||||
//| amplitude in the left channel and 1/2 amplitude in the right channel."""
|
||||
STATIC mp_obj_t synthio_note_get_panning(mp_obj_t self_in) {
|
||||
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
return mp_obj_new_float(common_hal_synthio_note_get_panning(self));
|
||||
return common_hal_synthio_note_get_panning(self);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(synthio_note_get_panning_obj, synthio_note_get_panning);
|
||||
|
||||
STATIC mp_obj_t synthio_note_set_panning(mp_obj_t self_in, mp_obj_t arg) {
|
||||
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
common_hal_synthio_note_set_panning(self, mp_obj_get_float(arg));
|
||||
common_hal_synthio_note_set_panning(self, arg);
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(synthio_note_set_panning_obj, synthio_note_set_panning);
|
||||
@ -145,108 +136,54 @@ MP_PROPERTY_GETSET(synthio_note_panning_obj,
|
||||
(mp_obj_t)&synthio_note_set_panning_obj);
|
||||
|
||||
|
||||
//| tremolo_depth: float
|
||||
//| """The tremolo depth of the note, from 0 to 1
|
||||
//| amplitude: BlockInput
|
||||
//| """The relative amplitude of the note, from 0 to 1
|
||||
//|
|
||||
//| A depth of 0 disables tremolo. A nonzero value enables tremolo,
|
||||
//| with the maximum decrease in amplitude being equal to the tremolo
|
||||
//| depth. A note with a tremolo depth of 1 will fade out to nothing, while
|
||||
//| a tremolo depth of 0.1 will give a minimum amplitude of 0.9."""
|
||||
STATIC mp_obj_t synthio_note_get_tremolo_depth(mp_obj_t self_in) {
|
||||
//| An amplitude of 0 makes the note inaudible. It is combined multiplicatively with
|
||||
//| the value from the note's envelope.
|
||||
//|
|
||||
//| To achieve a tremolo effect, attach an LFO here."""
|
||||
STATIC mp_obj_t synthio_note_get_amplitude(mp_obj_t self_in) {
|
||||
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
return mp_obj_new_float(common_hal_synthio_note_get_tremolo_depth(self));
|
||||
return common_hal_synthio_note_get_amplitude(self);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(synthio_note_get_tremolo_depth_obj, synthio_note_get_tremolo_depth);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(synthio_note_get_amplitude_obj, synthio_note_get_amplitude);
|
||||
|
||||
STATIC mp_obj_t synthio_note_set_tremolo_depth(mp_obj_t self_in, mp_obj_t arg) {
|
||||
STATIC mp_obj_t synthio_note_set_amplitude(mp_obj_t self_in, mp_obj_t arg) {
|
||||
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
common_hal_synthio_note_set_tremolo_depth(self, mp_obj_get_float(arg));
|
||||
common_hal_synthio_note_set_amplitude(self, arg);
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(synthio_note_set_tremolo_depth_obj, synthio_note_set_tremolo_depth);
|
||||
MP_PROPERTY_GETSET(synthio_note_tremolo_depth_obj,
|
||||
(mp_obj_t)&synthio_note_get_tremolo_depth_obj,
|
||||
(mp_obj_t)&synthio_note_set_tremolo_depth_obj);
|
||||
|
||||
//| tremolo_rate: float
|
||||
//| """The tremolo rate of the note, in Hz."""
|
||||
STATIC mp_obj_t synthio_note_get_tremolo_rate(mp_obj_t self_in) {
|
||||
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
return mp_obj_new_float(common_hal_synthio_note_get_tremolo_rate(self));
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(synthio_note_get_tremolo_rate_obj, synthio_note_get_tremolo_rate);
|
||||
|
||||
STATIC mp_obj_t synthio_note_set_tremolo_rate(mp_obj_t self_in, mp_obj_t arg) {
|
||||
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
common_hal_synthio_note_set_tremolo_rate(self, mp_obj_get_float(arg));
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(synthio_note_set_tremolo_rate_obj, synthio_note_set_tremolo_rate);
|
||||
MP_PROPERTY_GETSET(synthio_note_tremolo_rate_obj,
|
||||
(mp_obj_t)&synthio_note_get_tremolo_rate_obj,
|
||||
(mp_obj_t)&synthio_note_set_tremolo_rate_obj);
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(synthio_note_set_amplitude_obj, synthio_note_set_amplitude);
|
||||
MP_PROPERTY_GETSET(synthio_note_amplitude_obj,
|
||||
(mp_obj_t)&synthio_note_get_amplitude_obj,
|
||||
(mp_obj_t)&synthio_note_set_amplitude_obj);
|
||||
|
||||
//|
|
||||
//| bend_mode: BendMode
|
||||
//| """The type of bend operation"""
|
||||
STATIC mp_obj_t synthio_note_get_bend_mode(mp_obj_t self_in) {
|
||||
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
return cp_enum_find(&synthio_bend_mode_type, common_hal_synthio_note_get_bend_mode(self));
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(synthio_note_get_bend_mode_obj, synthio_note_get_bend_mode);
|
||||
|
||||
STATIC mp_obj_t synthio_note_set_bend_mode(mp_obj_t self_in, mp_obj_t arg) {
|
||||
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
common_hal_synthio_note_set_bend_mode(self, cp_enum_value(&synthio_bend_mode_type, arg, MP_QSTR_bend_mode));
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(synthio_note_set_bend_mode_obj, synthio_note_set_bend_mode);
|
||||
MP_PROPERTY_GETSET(synthio_note_bend_mode_obj,
|
||||
(mp_obj_t)&synthio_note_get_bend_mode_obj,
|
||||
(mp_obj_t)&synthio_note_set_bend_mode_obj);
|
||||
|
||||
//
|
||||
//| bend: BlockInput
|
||||
//| """The pitch bend depth of the note, from -1 to +1
|
||||
//|
|
||||
//| bend_depth: float
|
||||
//| """The bend depth of the note, from -1 to +1
|
||||
//|
|
||||
//| A depth of 0 disables bend. A depth of 1 corresponds to a bend of 1
|
||||
//| A depth of 0 plays the programmed frequency. A depth of 1 corresponds to a bend of 1
|
||||
//| octave. A depth of (1/12) = 0.833 corresponds to a bend of 1 semitone,
|
||||
//| and a depth of .00833 corresponds to one musical cent.
|
||||
//| and a depth of .00833 corresponds to one musical cent. Values of ±1 octave are supported.
|
||||
//|
|
||||
//| To achieve a vibrato or sweep effect, attach an LFO here.
|
||||
//| """
|
||||
STATIC mp_obj_t synthio_note_get_bend_depth(mp_obj_t self_in) {
|
||||
STATIC mp_obj_t synthio_note_get_bend(mp_obj_t self_in) {
|
||||
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
return mp_obj_new_float(common_hal_synthio_note_get_bend_depth(self));
|
||||
return common_hal_synthio_note_get_bend(self);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(synthio_note_get_bend_depth_obj, synthio_note_get_bend_depth);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(synthio_note_get_bend_obj, synthio_note_get_bend);
|
||||
|
||||
STATIC mp_obj_t synthio_note_set_bend_depth(mp_obj_t self_in, mp_obj_t arg) {
|
||||
STATIC mp_obj_t synthio_note_set_bend(mp_obj_t self_in, mp_obj_t arg) {
|
||||
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
common_hal_synthio_note_set_bend_depth(self, mp_obj_get_float(arg));
|
||||
common_hal_synthio_note_set_bend(self, arg);
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(synthio_note_set_bend_depth_obj, synthio_note_set_bend_depth);
|
||||
MP_PROPERTY_GETSET(synthio_note_bend_depth_obj,
|
||||
(mp_obj_t)&synthio_note_get_bend_depth_obj,
|
||||
(mp_obj_t)&synthio_note_set_bend_depth_obj);
|
||||
|
||||
//| bend_rate: float
|
||||
//| """The bend rate of the note, in Hz."""
|
||||
STATIC mp_obj_t synthio_note_get_bend_rate(mp_obj_t self_in) {
|
||||
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
return mp_obj_new_float(common_hal_synthio_note_get_bend_rate(self));
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(synthio_note_get_bend_rate_obj, synthio_note_get_bend_rate);
|
||||
|
||||
STATIC mp_obj_t synthio_note_set_bend_rate(mp_obj_t self_in, mp_obj_t arg) {
|
||||
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
common_hal_synthio_note_set_bend_rate(self, mp_obj_get_float(arg));
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(synthio_note_set_bend_rate_obj, synthio_note_set_bend_rate);
|
||||
MP_PROPERTY_GETSET(synthio_note_bend_rate_obj,
|
||||
(mp_obj_t)&synthio_note_get_bend_rate_obj,
|
||||
(mp_obj_t)&synthio_note_set_bend_rate_obj);
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(synthio_note_set_bend_obj, synthio_note_set_bend);
|
||||
MP_PROPERTY_GETSET(synthio_note_bend_obj,
|
||||
(mp_obj_t)&synthio_note_get_bend_obj,
|
||||
(mp_obj_t)&synthio_note_set_bend_obj);
|
||||
|
||||
//| waveform: Optional[ReadableBuffer]
|
||||
//| """The waveform of this note. Setting the waveform to a buffer of a different size resets the note's phase."""
|
||||
@ -306,6 +243,31 @@ MP_PROPERTY_GETSET(synthio_note_ring_frequency_obj,
|
||||
(mp_obj_t)&synthio_note_get_ring_frequency_obj,
|
||||
(mp_obj_t)&synthio_note_set_ring_frequency_obj);
|
||||
|
||||
//| ring_bend: float
|
||||
//| """The pitch bend depth of the note's ring waveform, from -1 to +1
|
||||
//|
|
||||
//| A depth of 0 plays the programmed frequency. A depth of 1 corresponds to a bend of 1
|
||||
//| octave. A depth of (1/12) = 0.833 corresponds to a bend of 1 semitone,
|
||||
//| and a depth of .00833 corresponds to one musical cent. Values of ±1 octave are supported.
|
||||
//|
|
||||
//| To achieve a vibrato or sweep effect on the ring waveform, attach an LFO here.
|
||||
//| """
|
||||
STATIC mp_obj_t synthio_note_get_ring_bend(mp_obj_t self_in) {
|
||||
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
return common_hal_synthio_note_get_ring_bend(self);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(synthio_note_get_ring_bend_obj, synthio_note_get_ring_bend);
|
||||
|
||||
STATIC mp_obj_t synthio_note_set_ring_bend(mp_obj_t self_in, mp_obj_t arg) {
|
||||
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
common_hal_synthio_note_set_ring_bend(self, arg);
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(synthio_note_set_ring_bend_obj, synthio_note_set_ring_bend);
|
||||
MP_PROPERTY_GETSET(synthio_note_ring_bend_obj,
|
||||
(mp_obj_t)&synthio_note_get_ring_bend_obj,
|
||||
(mp_obj_t)&synthio_note_set_ring_bend_obj);
|
||||
|
||||
//| ring_waveform: Optional[ReadableBuffer]
|
||||
//| """The ring waveform of this note. Setting the ring_waveform to a buffer of a different size resets the note's phase.
|
||||
//|
|
||||
@ -340,12 +302,10 @@ STATIC const mp_rom_map_elem_t synthio_note_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_panning), MP_ROM_PTR(&synthio_note_panning_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_waveform), MP_ROM_PTR(&synthio_note_waveform_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_envelope), MP_ROM_PTR(&synthio_note_envelope_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_tremolo_depth), MP_ROM_PTR(&synthio_note_tremolo_depth_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_tremolo_rate), MP_ROM_PTR(&synthio_note_tremolo_rate_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_bend_depth), MP_ROM_PTR(&synthio_note_bend_depth_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_bend_rate), MP_ROM_PTR(&synthio_note_bend_rate_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_bend_mode), MP_ROM_PTR(&synthio_note_bend_mode_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_amplitude), MP_ROM_PTR(&synthio_note_amplitude_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_bend), MP_ROM_PTR(&synthio_note_bend_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_ring_frequency), MP_ROM_PTR(&synthio_note_ring_frequency_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_ring_bend), MP_ROM_PTR(&synthio_note_ring_bend_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_ring_waveform), MP_ROM_PTR(&synthio_note_ring_waveform_obj) },
|
||||
};
|
||||
STATIC MP_DEFINE_CONST_DICT(synthio_note_locals_dict, synthio_note_locals_dict_table);
|
||||
|
@ -12,30 +12,24 @@ void common_hal_synthio_note_set_frequency(synthio_note_obj_t *self, mp_float_t
|
||||
bool common_hal_synthio_note_get_filter(synthio_note_obj_t *self);
|
||||
void common_hal_synthio_note_set_filter(synthio_note_obj_t *self, bool value);
|
||||
|
||||
mp_float_t common_hal_synthio_note_get_ring_frequency(synthio_note_obj_t *self);
|
||||
void common_hal_synthio_note_set_ring_frequency(synthio_note_obj_t *self, mp_float_t value);
|
||||
mp_obj_t common_hal_synthio_note_get_panning(synthio_note_obj_t *self);
|
||||
void common_hal_synthio_note_set_panning(synthio_note_obj_t *self, mp_obj_t value);
|
||||
|
||||
mp_float_t common_hal_synthio_note_get_panning(synthio_note_obj_t *self);
|
||||
void common_hal_synthio_note_set_panning(synthio_note_obj_t *self, mp_float_t value);
|
||||
mp_obj_t common_hal_synthio_note_get_amplitude(synthio_note_obj_t *self);
|
||||
void common_hal_synthio_note_set_amplitude(synthio_note_obj_t *self, mp_obj_t value);
|
||||
|
||||
mp_float_t common_hal_synthio_note_get_tremolo_rate(synthio_note_obj_t *self);
|
||||
void common_hal_synthio_note_set_tremolo_rate(synthio_note_obj_t *self, mp_float_t value);
|
||||
|
||||
mp_float_t common_hal_synthio_note_get_tremolo_depth(synthio_note_obj_t *self);
|
||||
void common_hal_synthio_note_set_tremolo_depth(synthio_note_obj_t *self, mp_float_t value);
|
||||
|
||||
synthio_bend_mode_t common_hal_synthio_note_get_bend_mode(synthio_note_obj_t *self);
|
||||
void common_hal_synthio_note_set_bend_mode(synthio_note_obj_t *self, synthio_bend_mode_t value);
|
||||
|
||||
mp_float_t common_hal_synthio_note_get_bend_rate(synthio_note_obj_t *self);
|
||||
void common_hal_synthio_note_set_bend_rate(synthio_note_obj_t *self, mp_float_t value);
|
||||
|
||||
mp_float_t common_hal_synthio_note_get_bend_depth(synthio_note_obj_t *self);
|
||||
void common_hal_synthio_note_set_bend_depth(synthio_note_obj_t *self, mp_float_t value);
|
||||
mp_obj_t common_hal_synthio_note_get_bend(synthio_note_obj_t *self);
|
||||
void common_hal_synthio_note_set_bend(synthio_note_obj_t *self, mp_obj_t value);
|
||||
|
||||
mp_obj_t common_hal_synthio_note_get_waveform_obj(synthio_note_obj_t *self);
|
||||
void common_hal_synthio_note_set_waveform(synthio_note_obj_t *self, mp_obj_t value);
|
||||
|
||||
mp_float_t common_hal_synthio_note_get_ring_frequency(synthio_note_obj_t *self);
|
||||
void common_hal_synthio_note_set_ring_frequency(synthio_note_obj_t *self, mp_float_t value);
|
||||
|
||||
mp_obj_t common_hal_synthio_note_get_ring_bend(synthio_note_obj_t *self);
|
||||
void common_hal_synthio_note_set_ring_bend(synthio_note_obj_t *self, mp_obj_t value);
|
||||
|
||||
mp_obj_t common_hal_synthio_note_get_ring_waveform_obj(synthio_note_obj_t *self);
|
||||
void common_hal_synthio_note_set_ring_waveform(synthio_note_obj_t *self, mp_obj_t value);
|
||||
|
||||
|
@ -24,6 +24,7 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "py/enum.h"
|
||||
@ -35,10 +36,13 @@
|
||||
#include "extmod/vfs_posix.h"
|
||||
|
||||
#include "shared-bindings/synthio/__init__.h"
|
||||
#include "shared-bindings/synthio/LFO.h"
|
||||
#include "shared-bindings/synthio/MidiTrack.h"
|
||||
#include "shared-bindings/synthio/Note.h"
|
||||
#include "shared-bindings/synthio/Synthesizer.h"
|
||||
|
||||
#include "shared-module/synthio/LFO.h"
|
||||
|
||||
#define default_attack_time (MICROPY_FLOAT_CONST(0.1))
|
||||
#define default_decay_time (MICROPY_FLOAT_CONST(0.05))
|
||||
#define default_release_time (MICROPY_FLOAT_CONST(0.2))
|
||||
@ -57,9 +61,11 @@ static const mp_arg_t envelope_properties[] = {
|
||||
//| """Support for multi-channel audio synthesis
|
||||
//|
|
||||
//| At least 2 simultaneous notes are supported. samd5x, mimxrt10xx and rp2040 platforms support up to 12 notes.
|
||||
//|
|
||||
//| """
|
||||
//|
|
||||
//| BlockInput = Union["LFO", float]
|
||||
//| """LFOs and Notes can take any of these types as inputs on certain attributes"""
|
||||
//|
|
||||
//| class Envelope:
|
||||
//| def __init__(
|
||||
//| self,
|
||||
@ -283,51 +289,34 @@ STATIC mp_obj_t onevo_to_hz(mp_obj_t arg) {
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(synthio_onevo_to_hz_obj, onevo_to_hz);
|
||||
|
||||
MAKE_ENUM_VALUE(synthio_bend_mode_type, bend_mode, STATIC, SYNTHIO_BEND_MODE_STATIC);
|
||||
MAKE_ENUM_VALUE(synthio_bend_mode_type, bend_mode, VIBRATO, SYNTHIO_BEND_MODE_VIBRATO);
|
||||
MAKE_ENUM_VALUE(synthio_bend_mode_type, bend_mode, SWEEP, SYNTHIO_BEND_MODE_SWEEP);
|
||||
MAKE_ENUM_VALUE(synthio_bend_mode_type, bend_mode, SWEEP_IN, SYNTHIO_BEND_MODE_SWEEP_IN);
|
||||
|
||||
//|
|
||||
//| class BendMode:
|
||||
//| """Controls the way the ``Note.pitch_bend_depth`` and ``Note.pitch_bend_rate`` properties are interpreted."""
|
||||
//|
|
||||
//| STATIC: "BendMode"
|
||||
//| """The Note's pitch is modified by its ``pitch_bend_depth``. ``pitch_bend_rate`` is ignored."""
|
||||
//|
|
||||
//| VIBRATO: "BendMode"
|
||||
//| """The Note's pitch varies by ``±pitch_bend_depth`` at a rate of ``pitch_bend_rate`` Hz."""
|
||||
//|
|
||||
//| SWEEP: "BendMode"
|
||||
//| """The Note's pitch starts at ``Note.frequency`` then sweeps up or down by ``pitch_bend_depth`` over ``1/pitch_bend_rate`` seconds."""
|
||||
//|
|
||||
//| SWEEP_IN: "BendMode"
|
||||
//| """The Note's pitch sweep is the reverse of ``SWEEP`` mode, starting at the bent pitch and arriving at the tuned pitch."""
|
||||
//|
|
||||
MAKE_ENUM_MAP(synthio_bend_mode) {
|
||||
MAKE_ENUM_MAP_ENTRY(bend_mode, STATIC),
|
||||
MAKE_ENUM_MAP_ENTRY(bend_mode, VIBRATO),
|
||||
MAKE_ENUM_MAP_ENTRY(bend_mode, SWEEP),
|
||||
MAKE_ENUM_MAP_ENTRY(bend_mode, SWEEP_IN),
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(synthio_bend_mode_locals_dict, synthio_bend_mode_locals_table);
|
||||
|
||||
MAKE_PRINTER(synthio, synthio_bend_mode);
|
||||
|
||||
MAKE_ENUM_TYPE(synthio, BendMode, synthio_bend_mode);
|
||||
|
||||
#if CIRCUITPY_AUDIOCORE_DEBUG
|
||||
STATIC mp_obj_t synthio_lfo_tick(size_t n, const mp_obj_t *args) {
|
||||
shared_bindings_synthio_lfo_tick(48000);
|
||||
mp_obj_t result[n];
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
synthio_lfo_slot_t slot;
|
||||
synthio_lfo_assign_input(args[i], &slot, MP_QSTR_arg);
|
||||
mp_float_t value = synthio_lfo_obj_tick(&slot);
|
||||
result[i] = mp_obj_new_float(value);
|
||||
}
|
||||
return mp_obj_new_tuple(n, result);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR(synthio_lfo_tick_obj, 1, synthio_lfo_tick);
|
||||
#endif
|
||||
|
||||
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_BendMode), MP_ROM_PTR(&synthio_bend_mode_type) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_MidiTrack), MP_ROM_PTR(&synthio_miditrack_type) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_Note), MP_ROM_PTR(&synthio_note_type) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_LFO), MP_ROM_PTR(&synthio_lfo_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) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_Envelope), MP_ROM_PTR(&synthio_envelope_type_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_midi_to_hz), MP_ROM_PTR(&synthio_midi_to_hz_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_onevo_to_hz), MP_ROM_PTR(&synthio_midi_to_hz_obj) },
|
||||
#if CIRCUITPY_AUDIOCORE_DEBUG
|
||||
{ MP_ROM_QSTR(MP_QSTR_lfo_tick), MP_ROM_PTR(&synthio_lfo_tick_obj) },
|
||||
#endif
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(synthio_module_globals, synthio_module_globals_table);
|
||||
|
@ -24,9 +24,6 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_UTIL_H
|
||||
#define MICROPY_INCLUDED_SHARED_BINDINGS_UTIL_H
|
||||
|
||||
#include "py/runtime.h"
|
||||
|
||||
#include "shared-bindings/util.h"
|
||||
@ -50,4 +47,10 @@ void properties_print_helper(const mp_print_t *print, mp_obj_t self_in, const mp
|
||||
mp_print_str(print, ")");
|
||||
}
|
||||
|
||||
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_UTIL_H
|
||||
void properties_construct_helper(mp_obj_t self_in, const mp_arg_t *args, const mp_arg_val_t *vals, size_t n_properties) {
|
||||
for (size_t i = 0; i < n_properties; i++) {
|
||||
if (vals[i].u_obj != NULL) {
|
||||
mp_store_attr(self_in, args[i].qst, vals[i].u_obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,14 +24,11 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_UTIL_H
|
||||
#define MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_UTIL_H
|
||||
#pragma once
|
||||
|
||||
#include "py/mpprint.h"
|
||||
#include "py/runtime.h"
|
||||
|
||||
void raise_deinited_error(void);
|
||||
void properties_print_helper(const mp_print_t *print, mp_obj_t self_in, const mp_arg_t *properties, size_t n_properties);
|
||||
|
||||
|
||||
#endif // MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_UTIL_H
|
||||
void properties_construct_helper(mp_obj_t self_in, const mp_arg_t *args, const mp_arg_val_t *vals, size_t n_properties);
|
||||
|
155
shared-module/synthio/LFO.c
Normal file
155
shared-module/synthio/LFO.c
Normal file
@ -0,0 +1,155 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* 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-bindings/synthio/LFO.h"
|
||||
#include "shared-module/synthio/LFO.h"
|
||||
#include <math.h>
|
||||
|
||||
#define ONE (MICROPY_FLOAT_CONST(1.))
|
||||
#define ZERO (MICROPY_FLOAT_CONST(0.))
|
||||
|
||||
|
||||
mp_float_t synthio_lfo_obj_tick(synthio_lfo_slot_t *slot) {
|
||||
mp_obj_t obj = slot->obj;
|
||||
|
||||
if (!mp_obj_is_type(obj, &synthio_lfo_type)) {
|
||||
return slot->value;
|
||||
}
|
||||
|
||||
synthio_lfo_obj_t *lfo = MP_OBJ_TO_PTR(obj);
|
||||
if (lfo->last_tick == synthio_global_tick) {
|
||||
slot->value = lfo->value;
|
||||
return slot->value;
|
||||
}
|
||||
|
||||
lfo->last_tick = synthio_global_tick;
|
||||
|
||||
mp_float_t rate = synthio_lfo_obj_tick(&lfo->rate) * synthio_global_rate_scale;
|
||||
;
|
||||
|
||||
mp_float_t accum = lfo->accum + rate;
|
||||
int len = lfo->waveform_bufinfo.len / 2;
|
||||
mp_float_t frac = accum - MICROPY_FLOAT_C_FUN(floor)(accum);
|
||||
size_t idx = (int)(frac * len);
|
||||
|
||||
if (lfo->once) {
|
||||
if (rate > 0) {
|
||||
if (accum >= ONE) {
|
||||
frac = ONE;
|
||||
idx = len - 1;
|
||||
}
|
||||
} else if (rate < 0 && accum < ZERO) {
|
||||
frac = ZERO;
|
||||
idx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
lfo->accum = frac;
|
||||
|
||||
int16_t *waveform = lfo->waveform_bufinfo.buf;
|
||||
assert(idx < lfo->waveform_bufinfo.len / 2);
|
||||
mp_float_t scale = synthio_lfo_obj_tick(&lfo->scale);
|
||||
mp_float_t offset = synthio_lfo_obj_tick(&lfo->offset);
|
||||
mp_float_t value = MICROPY_FLOAT_C_FUN(ldexp)(waveform[idx], -15) * scale + offset;
|
||||
|
||||
lfo->value = slot->value = value;
|
||||
return value;
|
||||
}
|
||||
|
||||
mp_float_t synthio_lfo_obj_tick_limited(synthio_lfo_slot_t *lfo_slot, mp_float_t lo, mp_float_t hi) {
|
||||
mp_float_t value = synthio_lfo_obj_tick(lfo_slot);
|
||||
if (value < lo) {
|
||||
return lo;
|
||||
}
|
||||
if (value > hi) {
|
||||
return hi;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
int32_t synthio_lfo_obj_tick_scaled(synthio_lfo_slot_t *lfo_slot, mp_float_t lo, mp_float_t hi, int shift) {
|
||||
mp_float_t value = synthio_lfo_obj_tick_limited(lfo_slot, lo, hi);
|
||||
return (int32_t)MICROPY_FLOAT_C_FUN(round)(MICROPY_FLOAT_C_FUN(ldexp)(value, shift));
|
||||
}
|
||||
|
||||
void synthio_lfo_assign_input(mp_obj_t obj, synthio_lfo_slot_t *slot, qstr arg_name) {
|
||||
if (mp_obj_is_type(obj, &synthio_lfo_type)) {
|
||||
slot->obj = obj;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mp_obj_get_float_maybe(obj, &slot->value)) {
|
||||
mp_raise_TypeError_varg(translate("%q must be of type %q or %q, not %q"), arg_name, MP_QSTR_Lfo, MP_QSTR_float, mp_obj_get_type_qstr(obj));
|
||||
}
|
||||
|
||||
slot->value = mp_obj_get_float(obj);
|
||||
slot->obj = obj;
|
||||
}
|
||||
|
||||
mp_obj_t common_hal_synthio_lfo_get_waveform_obj(synthio_lfo_obj_t *self) {
|
||||
return self->waveform_obj;
|
||||
}
|
||||
|
||||
mp_obj_t common_hal_synthio_lfo_get_rate_obj(synthio_lfo_obj_t *self) {
|
||||
return self->rate.obj;
|
||||
}
|
||||
|
||||
void common_hal_synthio_lfo_set_rate_obj(synthio_lfo_obj_t *self, mp_obj_t arg) {
|
||||
synthio_lfo_assign_input(arg, &self->rate, MP_QSTR_rate);
|
||||
}
|
||||
|
||||
mp_obj_t common_hal_synthio_lfo_get_scale_obj(synthio_lfo_obj_t *self) {
|
||||
return self->scale.obj;
|
||||
}
|
||||
void common_hal_synthio_lfo_set_scale_obj(synthio_lfo_obj_t *self, mp_obj_t arg) {
|
||||
synthio_lfo_assign_input(arg, &self->scale, MP_QSTR_scale);
|
||||
}
|
||||
|
||||
mp_obj_t common_hal_synthio_lfo_get_offset_obj(synthio_lfo_obj_t *self) {
|
||||
return self->offset.obj;
|
||||
}
|
||||
void common_hal_synthio_lfo_set_offset_obj(synthio_lfo_obj_t *self, mp_obj_t arg) {
|
||||
synthio_lfo_assign_input(arg, &self->offset, MP_QSTR_offset);
|
||||
}
|
||||
|
||||
bool common_hal_synthio_lfo_get_once(synthio_lfo_obj_t *self) {
|
||||
return self->once;
|
||||
}
|
||||
void common_hal_synthio_lfo_set_once(synthio_lfo_obj_t *self, bool arg) {
|
||||
self->once = arg;
|
||||
}
|
||||
|
||||
mp_float_t common_hal_synthio_lfo_get_value(synthio_lfo_obj_t *self) {
|
||||
return self->value;
|
||||
}
|
||||
|
||||
mp_float_t common_hal_synthio_lfo_get_phase(synthio_lfo_obj_t *self) {
|
||||
return self->accum;
|
||||
}
|
||||
|
||||
void common_hal_synthio_lfo_retrigger(synthio_lfo_obj_t *self) {
|
||||
self->accum = 0;
|
||||
}
|
62
shared-module/synthio/LFO.h
Normal file
62
shared-module/synthio/LFO.h
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* 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"
|
||||
#include "shared-bindings/synthio/__init__.h"
|
||||
|
||||
typedef struct synthio_lfo_slot {
|
||||
mp_obj_t obj;
|
||||
mp_float_t value;
|
||||
} synthio_lfo_slot_t;
|
||||
|
||||
typedef struct synthio_lfo_obj {
|
||||
mp_obj_base_t base;
|
||||
|
||||
int32_t sample_rate;
|
||||
|
||||
uint8_t last_tick;
|
||||
bool once;
|
||||
|
||||
synthio_lfo_slot_t rate, scale, offset;
|
||||
mp_float_t accum, value;
|
||||
|
||||
mp_obj_t waveform_obj;
|
||||
mp_buffer_info_t waveform_bufinfo;
|
||||
} synthio_lfo_obj_t;
|
||||
|
||||
// Update the value inside the lfo slot if the value is an LFO, returning the new value
|
||||
mp_float_t synthio_lfo_obj_tick(synthio_lfo_slot_t *lfo_slot);
|
||||
// the same, but the output is constrained to be between lo and hi
|
||||
mp_float_t synthio_lfo_obj_tick_limited(synthio_lfo_slot_t *lfo_slot, mp_float_t lo, mp_float_t hi);
|
||||
// the same, but the output is constrained to be between lo and hi and converted to an integer with `shift` fractional bits
|
||||
int32_t synthio_lfo_obj_tick_scaled(synthio_lfo_slot_t *lfo_slot, mp_float_t lo, mp_float_t hi, int shift);
|
||||
|
||||
// Assign an object (which may be a float or a synthio_lfo_obj_t) to an lfo slot
|
||||
void synthio_lfo_assign_input(mp_obj_t obj, synthio_lfo_slot_t *lfo_slot, qstr arg_name);
|
@ -30,10 +30,6 @@
|
||||
#include "shared-bindings/synthio/Note.h"
|
||||
#include "shared-bindings/synthio/__init__.h"
|
||||
|
||||
static int32_t round_float_to_int(mp_float_t f) {
|
||||
return (int32_t)(f + MICROPY_FLOAT_CONST(0.5));
|
||||
}
|
||||
|
||||
mp_float_t common_hal_synthio_note_get_frequency(synthio_note_obj_t *self) {
|
||||
return self->frequency;
|
||||
}
|
||||
@ -62,73 +58,36 @@ void common_hal_synthio_note_set_ring_frequency(synthio_note_obj_t *self, mp_flo
|
||||
self->ring_frequency_scaled = synthio_frequency_convert_float_to_scaled(val);
|
||||
}
|
||||
|
||||
mp_float_t common_hal_synthio_note_get_panning(synthio_note_obj_t *self) {
|
||||
return self->panning;
|
||||
mp_obj_t common_hal_synthio_note_get_panning(synthio_note_obj_t *self) {
|
||||
return self->panning.obj;
|
||||
}
|
||||
|
||||
void common_hal_synthio_note_set_panning(synthio_note_obj_t *self, mp_float_t value_in) {
|
||||
mp_float_t val = mp_arg_validate_float_range(value_in, -1, 1, MP_QSTR_panning);
|
||||
self->panning = val;
|
||||
if (val >= 0) {
|
||||
self->left_panning_scaled = 32768;
|
||||
self->right_panning_scaled = 32768 - round_float_to_int(val * 32768);
|
||||
} else {
|
||||
self->right_panning_scaled = 32768;
|
||||
self->left_panning_scaled = 32768 + round_float_to_int(val * 32768);
|
||||
}
|
||||
void common_hal_synthio_note_set_panning(synthio_note_obj_t *self, mp_obj_t value_in) {
|
||||
synthio_lfo_assign_input(value_in, &self->panning, MP_QSTR_panning);
|
||||
}
|
||||
|
||||
mp_float_t common_hal_synthio_note_get_tremolo_depth(synthio_note_obj_t *self) {
|
||||
return self->tremolo_descr.amplitude;
|
||||
mp_obj_t common_hal_synthio_note_get_amplitude(synthio_note_obj_t *self) {
|
||||
return self->amplitude.obj;
|
||||
}
|
||||
|
||||
void common_hal_synthio_note_set_tremolo_depth(synthio_note_obj_t *self, mp_float_t value_in) {
|
||||
mp_float_t val = mp_arg_validate_float_range(value_in, 0, 1, MP_QSTR_tremolo_depth);
|
||||
self->tremolo_descr.amplitude = val;
|
||||
self->tremolo_state.amplitude_scaled = round_float_to_int(val * 32768);
|
||||
void common_hal_synthio_note_set_amplitude(synthio_note_obj_t *self, mp_obj_t value_in) {
|
||||
synthio_lfo_assign_input(value_in, &self->amplitude, MP_QSTR_amplitude);
|
||||
}
|
||||
|
||||
mp_float_t common_hal_synthio_note_get_tremolo_rate(synthio_note_obj_t *self) {
|
||||
return self->tremolo_descr.frequency;
|
||||
mp_obj_t common_hal_synthio_note_get_bend(synthio_note_obj_t *self) {
|
||||
return self->bend.obj;
|
||||
}
|
||||
|
||||
void common_hal_synthio_note_set_tremolo_rate(synthio_note_obj_t *self, mp_float_t value_in) {
|
||||
mp_float_t val = mp_arg_validate_float_range(value_in, 0, 60, MP_QSTR_tremolo_rate);
|
||||
self->tremolo_descr.frequency = val;
|
||||
if (self->sample_rate != 0) {
|
||||
self->tremolo_state.dds = synthio_frequency_convert_float_to_dds(val * 65536, self->sample_rate);
|
||||
}
|
||||
void common_hal_synthio_note_set_bend(synthio_note_obj_t *self, mp_obj_t value_in) {
|
||||
synthio_lfo_assign_input(value_in, &self->bend, MP_QSTR_bend);
|
||||
}
|
||||
|
||||
mp_float_t common_hal_synthio_note_get_bend_depth(synthio_note_obj_t *self) {
|
||||
return self->bend_descr.amplitude;
|
||||
mp_obj_t common_hal_synthio_note_get_ring_bend(synthio_note_obj_t *self) {
|
||||
return self->ring_bend.obj;
|
||||
}
|
||||
|
||||
void common_hal_synthio_note_set_bend_depth(synthio_note_obj_t *self, mp_float_t value_in) {
|
||||
mp_float_t val = mp_arg_validate_float_range(value_in, -1, 1, MP_QSTR_bend_depth);
|
||||
self->bend_descr.amplitude = val;
|
||||
self->bend_state.amplitude_scaled = round_float_to_int(val * 32768);
|
||||
}
|
||||
|
||||
mp_float_t common_hal_synthio_note_get_bend_rate(synthio_note_obj_t *self) {
|
||||
return self->bend_descr.frequency;
|
||||
}
|
||||
|
||||
synthio_bend_mode_t common_hal_synthio_note_get_bend_mode(synthio_note_obj_t *self) {
|
||||
return self->bend_mode;
|
||||
}
|
||||
|
||||
void common_hal_synthio_note_set_bend_mode(synthio_note_obj_t *self, synthio_bend_mode_t value) {
|
||||
self->bend_mode = value;
|
||||
}
|
||||
|
||||
|
||||
void common_hal_synthio_note_set_bend_rate(synthio_note_obj_t *self, mp_float_t value_in) {
|
||||
mp_float_t val = mp_arg_validate_float_range(value_in, 0, 60, MP_QSTR_bend_rate);
|
||||
self->bend_descr.frequency = val;
|
||||
if (self->sample_rate != 0) {
|
||||
self->bend_state.dds = synthio_frequency_convert_float_to_dds(val * 65536, self->sample_rate);
|
||||
}
|
||||
void common_hal_synthio_note_set_ring_bend(synthio_note_obj_t *self, mp_obj_t value_in) {
|
||||
synthio_lfo_assign_input(value_in, &self->ring_bend, MP_QSTR_ring_bend);
|
||||
}
|
||||
|
||||
mp_obj_t common_hal_synthio_note_get_envelope_obj(synthio_note_obj_t *self) {
|
||||
@ -184,22 +143,10 @@ void synthio_note_recalculate(synthio_note_obj_t *self, int32_t sample_rate) {
|
||||
if (self->envelope_obj != mp_const_none) {
|
||||
synthio_envelope_definition_set(&self->envelope_def, self->envelope_obj, sample_rate);
|
||||
}
|
||||
|
||||
synthio_lfo_set(&self->tremolo_state, &self->tremolo_descr, sample_rate);
|
||||
self->tremolo_state.offset_scaled = 32768 - self->tremolo_state.amplitude_scaled;
|
||||
synthio_lfo_set(&self->bend_state, &self->bend_descr, sample_rate);
|
||||
self->bend_state.offset_scaled = 32768;
|
||||
}
|
||||
|
||||
void synthio_note_start(synthio_note_obj_t *self, int32_t sample_rate) {
|
||||
synthio_note_recalculate(self, sample_rate);
|
||||
if (self->bend_mode != SYNTHIO_BEND_MODE_VIBRATO) {
|
||||
self->bend_state.phase = 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t synthio_note_envelope(synthio_note_obj_t *self) {
|
||||
return self->amplitude_scaled;
|
||||
}
|
||||
|
||||
// Perform a pitch bend operation
|
||||
@ -213,10 +160,10 @@ uint32_t synthio_note_envelope(synthio_note_obj_t *self) {
|
||||
|
||||
STATIC uint16_t pitch_bend_table[] = { 0, 1948, 4013, 6200, 8517, 10972, 13573, 16329, 19248, 22341, 25618, 29090, 32768 };
|
||||
|
||||
STATIC uint32_t pitch_bend(uint32_t frequency_scaled, uint16_t bend_value) {
|
||||
bool down = (bend_value < 32768);
|
||||
if (!down) {
|
||||
bend_value -= 32768;
|
||||
STATIC uint32_t pitch_bend(uint32_t frequency_scaled, int16_t bend_value) {
|
||||
bool down = (bend_value < 0);
|
||||
if (down) {
|
||||
bend_value += 32768;
|
||||
}
|
||||
uint32_t bend_value_semitone = (uint32_t)bend_value * 24; // 65536/semitone
|
||||
uint32_t semitone = bend_value_semitone >> 16;
|
||||
@ -227,26 +174,34 @@ STATIC uint32_t pitch_bend(uint32_t frequency_scaled, uint16_t bend_value) {
|
||||
return (frequency_scaled * (uint64_t)f) >> (15 + down);
|
||||
}
|
||||
|
||||
STATIC int synthio_bend_value(synthio_note_obj_t *self, int16_t dur) {
|
||||
switch (self->bend_mode) {
|
||||
case SYNTHIO_BEND_MODE_STATIC:
|
||||
return self->bend_state.amplitude_scaled + self->bend_state.offset_scaled;
|
||||
case SYNTHIO_BEND_MODE_VIBRATO:
|
||||
return synthio_lfo_step(&self->bend_state, dur);
|
||||
case SYNTHIO_BEND_MODE_SWEEP:
|
||||
return synthio_sweep_step(&self->bend_state, dur);
|
||||
case SYNTHIO_BEND_MODE_SWEEP_IN:
|
||||
return synthio_sweep_in_step(&self->bend_state, dur);
|
||||
default:
|
||||
return 32768;
|
||||
}
|
||||
}
|
||||
#define ZERO MICROPY_FLOAT_CONST(0.)
|
||||
#define ONE MICROPY_FLOAT_CONST(1.)
|
||||
#define ALMOST_ONE (MICROPY_FLOAT_CONST(32767.) / 32768)
|
||||
|
||||
uint32_t synthio_note_step(synthio_note_obj_t *self, int32_t sample_rate, int16_t dur, uint16_t loudness[2]) {
|
||||
int tremolo_value = synthio_lfo_step(&self->tremolo_state, dur);
|
||||
loudness[0] = (((loudness[0] * tremolo_value) >> 15) * self->left_panning_scaled) >> 15;
|
||||
loudness[1] = (((loudness[1] * tremolo_value) >> 15) * self->right_panning_scaled) >> 15;
|
||||
int bend_value = synthio_bend_value(self, dur);
|
||||
int panning = synthio_lfo_obj_tick_scaled(&self->panning, -ALMOST_ONE, ALMOST_ONE, 15);
|
||||
int left_panning_scaled, right_panning_scaled;
|
||||
if (panning >= 0) {
|
||||
left_panning_scaled = 32768;
|
||||
right_panning_scaled = 32767 - panning;
|
||||
} else {
|
||||
right_panning_scaled = 32768;
|
||||
left_panning_scaled = 32767 + panning;
|
||||
}
|
||||
|
||||
int amplitude = synthio_lfo_obj_tick_scaled(&self->amplitude, ZERO, ALMOST_ONE, 15);
|
||||
left_panning_scaled = (left_panning_scaled * amplitude) >> 15;
|
||||
right_panning_scaled = (right_panning_scaled * amplitude) >> 15;
|
||||
loudness[0] = (loudness[0] * left_panning_scaled) >> 15;
|
||||
loudness[1] = (loudness[1] * right_panning_scaled) >> 15;
|
||||
|
||||
if (self->ring_frequency_scaled != 0) {
|
||||
int ring_bend_value = synthio_lfo_obj_tick_scaled(&self->ring_bend, -ONE, ALMOST_ONE, 15);
|
||||
self->ring_frequency_bent = pitch_bend(self->ring_frequency_scaled, ring_bend_value);
|
||||
}
|
||||
|
||||
int bend_value = synthio_lfo_obj_tick_scaled(&self->bend, -ONE, ALMOST_ONE, 15);
|
||||
uint32_t frequency_scaled = pitch_bend(self->frequency_scaled, bend_value);
|
||||
return frequency_scaled;
|
||||
|
||||
}
|
||||
|
@ -27,25 +27,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "shared-module/synthio/__init__.h"
|
||||
#include "shared-module/synthio/LFO.h"
|
||||
#include "shared-bindings/synthio/__init__.h"
|
||||
|
||||
typedef struct synthio_note_obj {
|
||||
mp_obj_base_t base;
|
||||
|
||||
synthio_lfo_slot_t panning, bend, amplitude, ring_bend;
|
||||
|
||||
mp_float_t frequency, ring_frequency;
|
||||
mp_float_t panning;
|
||||
mp_obj_t waveform_obj, envelope_obj, ring_waveform_obj;
|
||||
|
||||
int32_t sample_rate;
|
||||
|
||||
int32_t frequency_scaled;
|
||||
int32_t ring_frequency_scaled;
|
||||
int32_t amplitude_scaled;
|
||||
int32_t left_panning_scaled, right_panning_scaled;
|
||||
int32_t ring_frequency_scaled, ring_frequency_bent;
|
||||
bool filter;
|
||||
synthio_bend_mode_t bend_mode;
|
||||
synthio_lfo_descr_t tremolo_descr, bend_descr;
|
||||
synthio_lfo_state_t tremolo_state, bend_state;
|
||||
|
||||
mp_buffer_info_t waveform_buf;
|
||||
mp_buffer_info_t ring_waveform_buf;
|
||||
@ -56,4 +53,3 @@ void synthio_note_recalculate(synthio_note_obj_t *self, int32_t sample_rate);
|
||||
uint32_t synthio_note_step(synthio_note_obj_t *self, int32_t sample_rate, int16_t dur, uint16_t loudness[2]);
|
||||
void synthio_note_start(synthio_note_obj_t *self, int32_t sample_rate);
|
||||
bool synthio_note_playing(synthio_note_obj_t *self);
|
||||
uint32_t synthio_note_envelope(synthio_note_obj_t *self);
|
||||
|
@ -32,6 +32,9 @@
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
mp_float_t synthio_global_rate_scale;
|
||||
uint8_t synthio_global_tick;
|
||||
|
||||
STATIC const int16_t square_wave[] = {-32768, 32767};
|
||||
|
||||
STATIC const uint16_t notes[] = {8372, 8870, 9397, 9956, 10548, 11175, 11840,
|
||||
@ -219,7 +222,7 @@ static void synth_note_into_buffer(synthio_synth_t *synth, int chan, int32_t *ou
|
||||
if (note->ring_frequency_scaled != 0 && note->ring_waveform_buf.buf) {
|
||||
ring_waveform = note->ring_waveform_buf.buf;
|
||||
ring_waveform_length = note->ring_waveform_buf.len / sizeof(int16_t);
|
||||
ring_dds_rate = synthio_frequency_convert_scaled_to_dds((uint64_t)note->ring_frequency_scaled * ring_waveform_length, sample_rate);
|
||||
ring_dds_rate = synthio_frequency_convert_scaled_to_dds((uint64_t)note->ring_frequency_bent * ring_waveform_length, sample_rate);
|
||||
uint32_t lim = ring_waveform_length << SYNTHIO_FREQUENCY_SHIFT;
|
||||
if (ring_dds_rate > lim / sizeof(int16_t)) {
|
||||
ring_dds_rate = 0; // can't ring at that frequency
|
||||
@ -350,6 +353,8 @@ void synthio_synth_synthesize(synthio_synth_t *synth, uint8_t **bufptr, uint32_t
|
||||
return;
|
||||
}
|
||||
|
||||
shared_bindings_synthio_lfo_tick(synth->sample_rate);
|
||||
|
||||
synth->buffer_index = !synth->buffer_index;
|
||||
synth->other_channel = 1 - channel;
|
||||
synth->other_buffer_index = synth->buffer_index;
|
||||
@ -587,3 +592,8 @@ int synthio_lfo_step(synthio_lfo_state_t *state, uint16_t dur) {
|
||||
}
|
||||
return v / 16384 + state->offset_scaled;
|
||||
}
|
||||
|
||||
void shared_bindings_synthio_lfo_tick(uint32_t sample_rate) {
|
||||
synthio_global_rate_scale = (mp_float_t)SYNTHIO_MAX_DUR / sample_rate;
|
||||
synthio_global_tick++;
|
||||
}
|
||||
|
@ -112,3 +112,7 @@ void synthio_lfo_set(synthio_lfo_state_t *state, const synthio_lfo_descr_t *desc
|
||||
int synthio_lfo_step(synthio_lfo_state_t *state, uint16_t dur);
|
||||
int synthio_sweep_step(synthio_lfo_state_t *state, uint16_t dur);
|
||||
int synthio_sweep_in_step(synthio_lfo_state_t *state, uint16_t dur);
|
||||
|
||||
extern mp_float_t synthio_global_rate_scale;
|
||||
extern uint8_t synthio_global_tick;
|
||||
void shared_bindings_synthio_lfo_tick(uint32_t sample_rate);
|
||||
|
@ -65,6 +65,8 @@ noise = np.array(
|
||||
dtype=np.int16,
|
||||
)
|
||||
|
||||
bend_in = np.linspace(32767, 0, num=SAMPLE_SIZE, endpoint=True, dtype=np.int16)
|
||||
|
||||
envelope = synthio.Envelope(
|
||||
attack_time=0.1, decay_time=0.05, release_time=0.2, attack_level=1, sustain_level=0.8
|
||||
)
|
||||
@ -157,8 +159,8 @@ def synthesize4(synth):
|
||||
synth.press(chord)
|
||||
for i in range(16):
|
||||
for c in chord:
|
||||
c.tremolo_depth = i / 50
|
||||
c.tremolo_rate = (i + 1) / 4
|
||||
d = i / 50
|
||||
c.amplitude = synthio.LFO(scale=d / 2, offset=1 - d, rate=(i + 1) / 4, waveform=sine)
|
||||
yield 48
|
||||
yield 36
|
||||
|
||||
@ -171,17 +173,14 @@ def synthesize5(synth):
|
||||
synth.press(chord)
|
||||
for i in range(16):
|
||||
for c in chord:
|
||||
c.bend_depth = 1 / 24
|
||||
c.bend_rate = (i + 1) / 2
|
||||
c.bend = synthio.LFO(scale=1 / 24, rate=(i + 1) / 2, waveform=sine)
|
||||
yield 24
|
||||
synth.release_all()
|
||||
yield 100
|
||||
|
||||
for c in chord:
|
||||
synth.release_all()
|
||||
c.bend_mode = synthio.BendMode.SWEEP_IN
|
||||
c.bend_depth = randf(-1, 1)
|
||||
c.bend_rate = 1 / 2
|
||||
c.bend = synthio.LFO(scale=randf(-1, 1), rate=1 / 2, waveform=bend_in)
|
||||
synth.press(chord)
|
||||
yield 320
|
||||
|
||||
|
@ -25,6 +25,9 @@ fast_decay_envelope = synthio.Envelope(
|
||||
synth = synthio.Synthesizer(sample_rate=48000)
|
||||
|
||||
|
||||
# for a note without sustain phase,
|
||||
# switching to an envelope with decay_time 0.25 is about the same as
|
||||
# releasing with a release time of 0.25
|
||||
def synthesize(synth):
|
||||
notes = (synthio.Note(frequency=440, waveform=sine, envelope=envelope),)
|
||||
synth.press(notes)
|
||||
|
@ -1,30 +1,30 @@
|
||||
()
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||
(Note(frequency=830.6076004423605, panning=0.0, tremolo_rate=0.0, tremolo_depth=0.0, bend_rate=0.0, bend_depth=0.0, bend_mode=synthio.BendMode.VIBRATO, waveform=None, envelope=None, filter=True, ring_frequency=0.0, ring_waveform=None),)
|
||||
[-16383, -16383, -16383, -16383, 16383, 16383, 16383, 16383, 16383, -16383, -16383, -16383, -16383, -16383, 16383, 16383, 16383, 16383, 16383, -16383, -16383, -16383, -16383, -16383]
|
||||
(Note(frequency=830.6076004423605, panning=0.0, tremolo_rate=0.0, tremolo_depth=0.0, bend_rate=0.0, bend_depth=0.0, bend_mode=synthio.BendMode.VIBRATO, waveform=None, envelope=None, filter=True, ring_frequency=0.0, ring_waveform=None), Note(frequency=830.6076004423605, panning=0.0, tremolo_rate=0.0, tremolo_depth=0.0, bend_rate=0.0, bend_depth=0.0, bend_mode=synthio.BendMode.VIBRATO, waveform=None, envelope=None, filter=True, ring_frequency=0.0, ring_waveform=None))
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 28045, 0, 0, 0, 0, -28046, 0, 0, 0, 0, 28045, 0, 0, 0, 0, -28046]
|
||||
(Note(frequency=830.6076004423605, panning=0.0, tremolo_rate=0.0, tremolo_depth=0.0, bend_rate=0.0, bend_depth=0.0, bend_mode=synthio.BendMode.VIBRATO, waveform=None, envelope=None, filter=True, ring_frequency=0.0, ring_waveform=None),)
|
||||
[0, 0, 0, 28045, 0, 0, 0, 0, 0, 0, 0, 0, 28045, 0, 0, 0, 0, -28046, 0, 0, 0, 0, 28045, 0]
|
||||
(-5242, 5242)
|
||||
(-10485, 10484)
|
||||
(-15727, 15727)
|
||||
(-16383, 16383)
|
||||
(-14286, 14286)
|
||||
(-13106, 13106)
|
||||
(-13106, 13106)
|
||||
(-13106, 13106)
|
||||
(-13106, 13106)
|
||||
(-13106, 13106)
|
||||
(-13106, 13106)
|
||||
(-13106, 13106)
|
||||
(-13106, 13106)
|
||||
(-11009, 11009)
|
||||
(-8912, 8912)
|
||||
(-6815, 6815)
|
||||
(-4718, 4718)
|
||||
(-2621, 2621)
|
||||
(-524, 524)
|
||||
(Note(frequency=830.6076004423605, panning=0, amplitude=1, bend=0, waveform=None, envelope=None, filter=True, ring_frequency=0.0, ring_bend=0, ring_waveform=None),)
|
||||
[-16383, -16383, -16383, -16383, 16382, 16382, 16382, 16382, 16382, -16383, -16383, -16383, -16383, -16383, 16382, 16382, 16382, 16382, 16382, -16383, -16383, -16383, -16383, -16383]
|
||||
(Note(frequency=830.6076004423605, panning=0, amplitude=1, bend=0, waveform=None, envelope=None, filter=True, ring_frequency=0.0, ring_bend=0, ring_waveform=None), Note(frequency=830.6076004423605, panning=0, amplitude=1, bend=0, waveform=None, envelope=None, filter=True, ring_frequency=0.0, ring_bend=0, ring_waveform=None))
|
||||
[-1, -1, -1, -1, -1, -1, -1, -1, 28045, -1, -1, -1, -1, -28046, -1, -1, -1, -1, 28045, -1, -1, -1, -1, -28046]
|
||||
(Note(frequency=830.6076004423605, panning=0, amplitude=1, bend=0, waveform=None, envelope=None, filter=True, ring_frequency=0.0, ring_bend=0, ring_waveform=None),)
|
||||
[-1, -1, -1, 28045, -1, -1, -1, -1, -1, -1, -1, -1, 28045, -1, -1, -1, -1, -28046, -1, -1, -1, -1, 28045, -1]
|
||||
(-5242, 5241)
|
||||
(-10484, 10484)
|
||||
(-15727, 15726)
|
||||
(-16383, 16382)
|
||||
(-14286, 14285)
|
||||
(-13106, 13105)
|
||||
(-13106, 13105)
|
||||
(-13106, 13105)
|
||||
(-13106, 13105)
|
||||
(-13106, 13105)
|
||||
(-13106, 13105)
|
||||
(-13106, 13105)
|
||||
(-13106, 13105)
|
||||
(-11009, 11008)
|
||||
(-8912, 8911)
|
||||
(-6815, 6814)
|
||||
(-4718, 4717)
|
||||
(-2621, 2620)
|
||||
(-524, 523)
|
||||
(0, 0)
|
||||
(0, 0)
|
||||
(0, 0)
|
||||
|
Loading…
Reference in New Issue
Block a user