synthio: Finish implementing Math blocks

This commit is contained in:
Jeff Epler 2023-05-17 16:15:31 -05:00
parent 5de4d197a2
commit 391438102b
No known key found for this signature in database
GPG Key ID: D5BF15AB975AB4DE
14 changed files with 833 additions and 75 deletions

View File

@ -41,6 +41,7 @@ SRC_BITMAP := \
shared-bindings/rainbowio/__init__.c \
shared-bindings/struct/__init__.c \
shared-bindings/synthio/__init__.c \
shared-bindings/synthio/Math.c \
shared-bindings/synthio/MidiTrack.c \
shared-bindings/synthio/LFO.c \
shared-bindings/synthio/Note.c \
@ -65,6 +66,7 @@ SRC_BITMAP := \
shared-module/rainbowio/__init__.c \
shared-module/struct/__init__.c \
shared-module/synthio/__init__.c \
shared-module/synthio/Math.c \
shared-module/synthio/MidiTrack.c \
shared-module/synthio/LFO.c \
shared-module/synthio/Note.c \

View File

@ -651,6 +651,7 @@ SRC_SHARED_MODULE_ALL = \
supervisor/__init__.c \
supervisor/StatusBar.c \
synthio/LFO.c \
synthio/Math.c \
synthio/MidiTrack.c \
synthio/Note.c \
synthio/Synthesizer.c \

View File

@ -34,7 +34,7 @@
#include "shared-module/synthio/LFO.h"
//| class LFO:
//| """A low-frequency oscillator
//| """A low-frequency oscillator block
//|
//| Every `rate` seconds, the output of the LFO cycles through its `waveform`.
//| The output at any particular moment is ``waveform[idx] * scale + offset``.

View File

@ -0,0 +1,286 @@
/*
* 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/proto.h"
#include "py/runtime.h"
#include "shared-bindings/util.h"
#include "shared-bindings/synthio/Math.h"
#include "shared-module/synthio/Math.h"
MAKE_ENUM_VALUE(synthio_math_operation_type, math_op, SUM, OP_SUM);
MAKE_ENUM_VALUE(synthio_math_operation_type, math_op, ADD_SUB, OP_ADD_SUB);
MAKE_ENUM_VALUE(synthio_math_operation_type, math_op, PRODUCT, OP_PRODUCT);
MAKE_ENUM_VALUE(synthio_math_operation_type, math_op, MUL_DIV, OP_MUL_DIV);
MAKE_ENUM_VALUE(synthio_math_operation_type, math_op, SCALE_OFFSET, OP_SCALE_OFFSET);
MAKE_ENUM_VALUE(synthio_math_operation_type, math_op, OFFSET_SCALE, OP_OFFSET_SCALE);
MAKE_ENUM_VALUE(synthio_math_operation_type, math_op, LERP, OP_LERP);
MAKE_ENUM_VALUE(synthio_math_operation_type, math_op, CONSTRAINED_LERP, OP_CONSTRAINED_LERP);
MAKE_ENUM_VALUE(synthio_math_operation_type, math_op, DIV_ADD, OP_DIV_ADD);
MAKE_ENUM_VALUE(synthio_math_operation_type, math_op, ADD_DIV, OP_ADD_DIV);
MAKE_ENUM_VALUE(synthio_math_operation_type, math_op, MID, OP_MID);
MAKE_ENUM_VALUE(synthio_math_operation_type, math_op, MAX, OP_MAX);
MAKE_ENUM_VALUE(synthio_math_operation_type, math_op, MIN, OP_MIN);
MAKE_ENUM_VALUE(synthio_math_operation_type, math_op, ABS, OP_ABS);
//| class MathOperation:
//| """Operation for a Math block"""
//|
//| SUM: "MathOperation"
//| """Computes ``a+b+c``. For 2-input sum, set one argument to ``0.0``. To hold a control value for multiple subscribers, set two arguments to ``0.0``."""
//|
//| ADD_SUB: "MathOperation"
//| """Computes ``a+b-c``. For 2-input subtraction, set ``b`` to ``0.0``."""
//|
//| PRODUCT: "MathOperation"
//| """Computes ``a*b*c``. For 2-input product, set one argument to ``1.0``."""
//|
//| MUL_DIV: "MathOperation"
//| """Computes ``a*b/c``. If ``c`` is zero, the output is ``1.0``."""
//|
//| SCALE_OFFSET: "MathOperation"
//| """Computes ``(a*b)+c``."""
//|
//| OFFSET_SCALE: "MathOperation"
//| """Computes ``(a+b)*c``. For 2-input multiplication, set ``b`` to 0."""
//|
//| LERP: "MathOperation"
//| """Computes ``a * (1-c) + b * c``."""
//|
//| CONSTRAINED_LERP: "MathOperation"
//| """Computes ``a * (1-c') + b * c'``, where ``c'`` is constrained to be between ``0.0`` and ``1.0``."""
//|
//| DIV_ADD: "MathOperation"
//| """Computes ``a/b+c``. If ``b`` is zero, the output is ``c``."""
//|
//| ADD_DIV: "MathOperation"
//| """Computes ``(a+b)/c``. For 2-input product, set ``b`` to ``0.0``."""
//|
//| MID: "MathOperation"
//| """Returns the middle of the 3 input values."""
//|
//| MAX: "MathOperation"
//| """Returns the biggest of the 3 input values."""
//|
//| MIN: "MathOperation"
//| """Returns the smallest of the 3 input values."""
//|
//| ABS: "MathOperation"
//| """Returns the absolute value of ``a``."""
//|
MAKE_ENUM_MAP(synthio_math_operation) {
MAKE_ENUM_MAP_ENTRY(math_op, SUM),
MAKE_ENUM_MAP_ENTRY(math_op, ADD_SUB),
MAKE_ENUM_MAP_ENTRY(math_op, PRODUCT),
MAKE_ENUM_MAP_ENTRY(math_op, MUL_DIV),
MAKE_ENUM_MAP_ENTRY(math_op, SCALE_OFFSET),
MAKE_ENUM_MAP_ENTRY(math_op, OFFSET_SCALE),
MAKE_ENUM_MAP_ENTRY(math_op, LERP),
MAKE_ENUM_MAP_ENTRY(math_op, CONSTRAINED_LERP),
MAKE_ENUM_MAP_ENTRY(math_op, DIV_ADD),
MAKE_ENUM_MAP_ENTRY(math_op, ADD_DIV),
MAKE_ENUM_MAP_ENTRY(math_op, MID),
MAKE_ENUM_MAP_ENTRY(math_op, MAX),
MAKE_ENUM_MAP_ENTRY(math_op, MIN),
MAKE_ENUM_MAP_ENTRY(math_op, ABS),
};
STATIC MP_DEFINE_CONST_DICT(synthio_math_operation_locals_dict, synthio_math_operation_locals_table);
MAKE_PRINTER(synthio, synthio_math_operation);
MAKE_ENUM_TYPE(synthio, MathOperation, synthio_math_operation);
//| class Math:
//| """An arithmetic block
//|
//| Performs an arithmetic operation on up to 3 inputs. See the
//| documentation of ``MathOperation`` for the specific functions available.
//|
//| The properties can all be changed at run-time.
//|
//| An Math only updates if it is actually associated with a playing `Synthesizer`,
//| including indirectly via a `Note` or another intermediate Math.
//|
//| Using the same Math as an input to multiple other Maths or Notes is OK, but
//| the result if an Math is tied to multiple Synthtesizer objects is undefined.
//|
//| In the current implementation, Maths are updated every 256 samples. This
//| should be considered an implementation detail.
//| """
//|
//| def __init__(
//| self,
//| operation: MathOperation,
//| a: BlockInput,
//| b: BlockInput = 0.0,
//| c: BlockInput = 1.0,
//| ):
//| pass
static const mp_arg_t math_properties[] = {
{ MP_QSTR_operation, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = NULL } },
{ MP_QSTR_a, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = NULL } },
{ MP_QSTR_b, MP_ARG_OBJ, {.u_obj = MP_ROM_INT(0) } },
{ MP_QSTR_c, MP_ARG_OBJ, {.u_obj = MP_ROM_INT(1) } },
};
STATIC mp_obj_t synthio_math_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
mp_arg_val_t args[MP_ARRAY_SIZE(math_properties)];
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(math_properties), math_properties, args);
synthio_math_obj_t *self = m_new_obj(synthio_math_obj_t);
self->base.base.type = &synthio_math_type;
self->base.last_tick = synthio_global_tick;
mp_obj_t result = MP_OBJ_FROM_PTR(self);
properties_construct_helper(result, math_properties, args, MP_ARRAY_SIZE(math_properties));
return result;
};
//| a: BlockInput
//| """The first input to the operation"""
STATIC mp_obj_t synthio_math_get_a(mp_obj_t self_in) {
synthio_math_obj_t *self = MP_OBJ_TO_PTR(self_in);
return common_hal_synthio_math_get_input_obj(self, 0);
}
MP_DEFINE_CONST_FUN_OBJ_1(synthio_math_get_a_obj, synthio_math_get_a);
STATIC mp_obj_t synthio_math_set_a(mp_obj_t self_in, mp_obj_t arg) {
synthio_math_obj_t *self = MP_OBJ_TO_PTR(self_in);
common_hal_synthio_math_set_input_obj(self, 0, arg, MP_QSTR_a);
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_2(synthio_math_set_a_obj, synthio_math_set_a);
MP_PROPERTY_GETSET(synthio_math_a_obj,
(mp_obj_t)&synthio_math_get_a_obj,
(mp_obj_t)&synthio_math_set_a_obj);
//| b: BlockInput
//| """The second input to the operation"""
STATIC mp_obj_t synthio_math_get_b(mp_obj_t self_in) {
synthio_math_obj_t *self = MP_OBJ_TO_PTR(self_in);
return common_hal_synthio_math_get_input_obj(self, 1);
}
MP_DEFINE_CONST_FUN_OBJ_1(synthio_math_get_b_obj, synthio_math_get_b);
STATIC mp_obj_t synthio_math_set_b(mp_obj_t self_in, mp_obj_t arg) {
synthio_math_obj_t *self = MP_OBJ_TO_PTR(self_in);
common_hal_synthio_math_set_input_obj(self, 1, arg, MP_QSTR_b);
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_2(synthio_math_set_b_obj, synthio_math_set_b);
MP_PROPERTY_GETSET(synthio_math_b_obj,
(mp_obj_t)&synthio_math_get_b_obj,
(mp_obj_t)&synthio_math_set_b_obj);
//| c: BlockInput
//| """The third input to the operation"""
STATIC mp_obj_t synthio_math_get_c(mp_obj_t self_in) {
synthio_math_obj_t *self = MP_OBJ_TO_PTR(self_in);
return common_hal_synthio_math_get_input_obj(self, 2);
}
MP_DEFINE_CONST_FUN_OBJ_1(synthio_math_get_c_obj, synthio_math_get_c);
STATIC mp_obj_t synthio_math_set_c(mp_obj_t self_in, mp_obj_t arg) {
synthio_math_obj_t *self = MP_OBJ_TO_PTR(self_in);
common_hal_synthio_math_set_input_obj(self, 2, arg, MP_QSTR_c);
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_2(synthio_math_set_c_obj, synthio_math_set_c);
MP_PROPERTY_GETSET(synthio_math_c_obj,
(mp_obj_t)&synthio_math_get_c_obj,
(mp_obj_t)&synthio_math_set_c_obj);
//| operation: MathOperation
//| """The function to compute"""
STATIC mp_obj_t synthio_math_get_operation(mp_obj_t self_in) {
synthio_math_obj_t *self = MP_OBJ_TO_PTR(self_in);
return cp_enum_find(&synthio_math_operation_type, common_hal_synthio_math_get_operation(self));
}
MP_DEFINE_CONST_FUN_OBJ_1(synthio_math_get_operation_obj, synthio_math_get_operation);
STATIC mp_obj_t synthio_math_set_operation(mp_obj_t self_in, mp_obj_t arg) {
synthio_math_obj_t *self = MP_OBJ_TO_PTR(self_in);
common_hal_synthio_math_set_operation(self, cp_enum_value(&synthio_math_operation_type, arg, MP_QSTR_operation));
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_2(synthio_math_set_operation_obj, synthio_math_set_operation);
MP_PROPERTY_GETSET(synthio_math_operation_obj,
(mp_obj_t)&synthio_math_get_operation_obj,
(mp_obj_t)&synthio_math_set_operation_obj);
//|
//| value: float
//| """The value of the oscillator (read-only)"""
//|
STATIC mp_obj_t synthio_math_get_value(mp_obj_t self_in) {
synthio_math_obj_t *self = MP_OBJ_TO_PTR(self_in);
return mp_obj_new_float(common_hal_synthio_math_get_value(self));
}
MP_DEFINE_CONST_FUN_OBJ_1(synthio_math_get_value_obj, synthio_math_get_value);
MP_PROPERTY_GETTER(synthio_math_value_obj,
(mp_obj_t)&synthio_math_get_value_obj);
static void math_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
(void)kind;
properties_print_helper(print, self_in, math_properties, MP_ARRAY_SIZE(math_properties));
}
STATIC const mp_rom_map_elem_t synthio_math_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_a), MP_ROM_PTR(&synthio_math_a_obj) },
{ MP_ROM_QSTR(MP_QSTR_b), MP_ROM_PTR(&synthio_math_b_obj) },
{ MP_ROM_QSTR(MP_QSTR_c), MP_ROM_PTR(&synthio_math_c_obj) },
{ MP_ROM_QSTR(MP_QSTR_operation), MP_ROM_PTR(&synthio_math_operation_obj) },
{ MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&synthio_math_value_obj) },
};
STATIC MP_DEFINE_CONST_DICT(synthio_math_locals_dict, synthio_math_locals_dict_table);
STATIC const synthio_block_proto_t math_proto = {
MP_PROTO_IMPLEMENT(MP_QSTR_synthio_block)
.tick = common_hal_synthio_math_tick,
};
const mp_obj_type_t synthio_math_type = {
{ &mp_type_type },
.flags = MP_TYPE_FLAG_EXTENDED,
.name = MP_QSTR_Math,
.make_new = synthio_math_make_new,
.locals_dict = (mp_obj_dict_t *)&synthio_math_locals_dict,
.print = math_print,
MP_TYPE_EXTENDED_FIELDS(
.protocol = &math_proto,
),
};

View File

@ -0,0 +1,60 @@
/*
* 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 enum {
OP_SUM,
OP_ADD_SUB,
OP_PRODUCT,
OP_MUL_DIV,
OP_SCALE_OFFSET,
OP_OFFSET_SCALE,
OP_LERP,
OP_CONSTRAINED_LERP,
OP_DIV_ADD,
OP_ADD_DIV,
OP_MID,
OP_MIN,
OP_MAX,
OP_ABS
} synthio_math_operation_t;
typedef struct synthio_math_obj synthio_math_obj_t;
extern const mp_obj_type_t synthio_math_type;
extern const mp_obj_type_t synthio_math_operation_type;
mp_obj_t common_hal_synthio_math_get_input_obj(synthio_math_obj_t *self, size_t i);
void common_hal_synthio_math_set_input_obj(synthio_math_obj_t *self, size_t i, mp_obj_t arg, qstr argname);
synthio_math_operation_t common_hal_synthio_math_get_operation(synthio_math_obj_t *self);
void common_hal_synthio_math_set_operation(synthio_math_obj_t *self, synthio_math_operation_t arg);
mp_float_t common_hal_synthio_math_get_value(synthio_math_obj_t *self);
mp_float_t common_hal_synthio_math_tick(mp_obj_t self_in);

View File

@ -37,6 +37,7 @@
#include "shared-bindings/synthio/__init__.h"
#include "shared-bindings/synthio/LFO.h"
#include "shared-bindings/synthio/Math.h"
#include "shared-bindings/synthio/MidiTrack.h"
#include "shared-bindings/synthio/Note.h"
#include "shared-bindings/synthio/Synthesizer.h"
@ -63,8 +64,8 @@ static const mp_arg_t envelope_properties[] = {
//| 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"""
//| BlockInput = Union["Math", "LFO", float, None]
//| """Blocks and Notes can take any of these types as inputs on certain attributes"""
//|
//| class Envelope:
//| def __init__(
@ -306,6 +307,8 @@ MP_DEFINE_CONST_FUN_OBJ_VAR(synthio_lfo_tick_obj, 1, synthio_lfo_tick);
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_Math), MP_ROM_PTR(&synthio_math_type) },
{ MP_ROM_QSTR(MP_QSTR_MathOperation), MP_ROM_PTR(&synthio_math_operation_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) },

View File

@ -64,58 +64,6 @@ mp_float_t common_hal_synthio_lfo_tick(mp_obj_t self_in) {
return value;
}
mp_float_t synthio_block_slot_get(synthio_block_slot_t *slot) {
if (slot->obj == mp_const_none) {
return MICROPY_FLOAT_CONST(0.);
}
mp_float_t value;
if (mp_obj_get_float_maybe(slot->obj, &value)) {
return value;
}
synthio_block_base_t *block = MP_OBJ_TO_PTR(slot->obj);
if (block->last_tick == synthio_global_tick) {
return block->value;
}
block->last_tick = synthio_global_tick;
// previously verified by call to mp_proto_get
const synthio_block_proto_t *p = mp_type_get_protocol_slot(mp_obj_get_type(slot->obj));
block->value = value = p->tick(slot);
return value;
}
mp_float_t synthio_block_slot_get_limited(synthio_block_slot_t *lfo_slot, mp_float_t lo, mp_float_t hi) {
mp_float_t value = synthio_block_slot_get(lfo_slot);
if (value < lo) {
return lo;
}
if (value > hi) {
return hi;
}
return value;
}
int32_t synthio_block_slot_get_scaled(synthio_block_slot_t *lfo_slot, mp_float_t lo, mp_float_t hi) {
mp_float_t value = synthio_block_slot_get_limited(lfo_slot, lo, hi);
return (int32_t)MICROPY_FLOAT_C_FUN(round)(MICROPY_FLOAT_C_FUN(ldexp)(value, 15));
}
void synthio_block_assign_slot(mp_obj_t obj, synthio_block_slot_t *slot, qstr arg_name) {
if (mp_proto_get(MP_QSTR_synthio_block, obj)) {
slot->obj = obj;
return;
}
mp_float_t value;
if (obj != mp_const_none && !mp_obj_get_float_maybe(obj, &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->obj = obj;
}
mp_obj_t common_hal_synthio_lfo_get_waveform_obj(synthio_lfo_obj_t *self) {
return self->waveform_obj;
}

View File

@ -26,26 +26,7 @@
#pragma once
#include "py/obj.h"
#include "py/proto.h"
#include "shared-module/synthio/__init__.h"
#include "shared-bindings/synthio/__init__.h"
typedef struct synthio_block_base {
mp_obj_base_t base;
uint8_t last_tick;
mp_float_t value;
} synthio_block_base_t;
typedef struct synthio_block_slot {
mp_obj_t obj;
} synthio_block_slot_t;
typedef struct {
MP_PROTOCOL_HEAD;
mp_float_t (*tick)(mp_obj_t obj);
} synthio_block_proto_t;
#include "shared-module/synthio/block.h"
typedef struct synthio_lfo_obj {
synthio_block_base_t base;

View File

@ -0,0 +1,122 @@
/*
* 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 <math.h>
#include "shared-bindings/synthio/Math.h"
#include "shared-module/synthio/Math.h"
mp_obj_t common_hal_synthio_math_get_input_obj(synthio_math_obj_t *self, size_t i) {
return self->inputs[i].obj;
}
void common_hal_synthio_math_set_input_obj(synthio_math_obj_t *self, size_t i, mp_obj_t arg, qstr argname) {
assert(i < MP_ARRAY_SIZE(self->inputs));
synthio_block_assign_slot(arg, &self->inputs[i], argname);
}
synthio_math_operation_t common_hal_synthio_math_get_operation(synthio_math_obj_t *self) {
return self->operation;
}
void common_hal_synthio_math_set_operation(synthio_math_obj_t *self, synthio_math_operation_t arg) {
self->operation = arg;
}
#define ZERO (MICROPY_FLOAT_CONST(0.))
mp_float_t common_hal_synthio_math_get_value(synthio_math_obj_t *self) {
return self->base.value;
}
mp_float_t common_hal_synthio_math_tick(mp_obj_t self_in) {
synthio_math_obj_t *self = MP_OBJ_TO_PTR(self_in);
mp_float_t a = synthio_block_slot_get(&self->inputs[0]);
if (self->operation == OP_ABS) {
return MICROPY_FLOAT_C_FUN(fabs)(a);
}
mp_float_t b = synthio_block_slot_get(&self->inputs[1]);
mp_float_t c = synthio_block_slot_get(&self->inputs[2]);
switch (self->operation) {
case OP_SUM:
return a + b + c;
case OP_ADD_SUB:
return a + b - c;
case OP_PRODUCT:
return a * b * c;
case OP_MUL_DIV:
if (fpclassify(c) == FP_ZERO) {
return 0;
}
return a * b / c;
case OP_SCALE_OFFSET:
return a * b + c;
case OP_OFFSET_SCALE:
return (a + b) * c;
case OP_CONSTRAINED_LERP:
c = MIN(1, MAX(0, c));
MP_FALLTHROUGH;
case OP_LERP:
return a * (1 - c) + b * c;
case OP_DIV_ADD:
if (fpclassify(b) == FP_ZERO) {
return ZERO;
}
return a / b + c;
case OP_ADD_DIV:
if (fpclassify(c) == FP_ZERO) {
return ZERO;
}
return (a + b) / c;
case OP_MID:
if (a < b) {
if (b < c) {
return b;
}
if (a < c) {
return c;
}
return a;
}
if (a < c) {
return a;
}
if (c < b) {
return b;
}
return c;
case OP_MIN:
return MIN(a,MIN(b,c));
case OP_MAX:
return MAX(a,MAX(b,c));
case OP_ABS:
break;
}
return ZERO;
}

View File

@ -0,0 +1,34 @@
/*
* 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 "shared-module/synthio/block.h"
typedef struct synthio_math_obj {
synthio_block_base_t base;
synthio_block_slot_t inputs[3];
synthio_math_operation_t operation;
} synthio_math_obj_t;

View File

@ -547,3 +547,55 @@ 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++;
}
mp_float_t synthio_block_slot_get(synthio_block_slot_t *slot) {
if (slot->obj == mp_const_none) {
return MICROPY_FLOAT_CONST(0.);
}
mp_float_t value;
if (mp_obj_get_float_maybe(slot->obj, &value)) {
return value;
}
synthio_block_base_t *block = MP_OBJ_TO_PTR(slot->obj);
if (block->last_tick == synthio_global_tick) {
return block->value;
}
block->last_tick = synthio_global_tick;
// previously verified by call to mp_proto_get
const synthio_block_proto_t *p = mp_type_get_protocol_slot(mp_obj_get_type(slot->obj));
block->value = value = p->tick(slot->obj);
return value;
}
mp_float_t synthio_block_slot_get_limited(synthio_block_slot_t *lfo_slot, mp_float_t lo, mp_float_t hi) {
mp_float_t value = synthio_block_slot_get(lfo_slot);
if (value < lo) {
return lo;
}
if (value > hi) {
return hi;
}
return value;
}
int32_t synthio_block_slot_get_scaled(synthio_block_slot_t *lfo_slot, mp_float_t lo, mp_float_t hi) {
mp_float_t value = synthio_block_slot_get_limited(lfo_slot, lo, hi);
return (int32_t)MICROPY_FLOAT_C_FUN(round)(MICROPY_FLOAT_C_FUN(ldexp)(value, 15));
}
void synthio_block_assign_slot(mp_obj_t obj, synthio_block_slot_t *slot, qstr arg_name) {
if (mp_proto_get(MP_QSTR_synthio_block, obj)) {
slot->obj = obj;
return;
}
mp_float_t value;
if (obj != mp_const_none && !mp_obj_get_float_maybe(obj, &value)) {
mp_raise_TypeError_varg(translate("%q must be of type %q, not %q"), arg_name, MP_QSTR_BlockInput, mp_obj_get_type_qstr(obj));
}
slot->obj = obj;
}

View File

@ -0,0 +1,58 @@
/*
* 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 "py/proto.h"
#include "shared-module/synthio/__init__.h"
#include "shared-bindings/synthio/__init__.h"
typedef struct synthio_block_base {
mp_obj_base_t base;
uint8_t last_tick;
mp_float_t value;
} synthio_block_base_t;
typedef struct synthio_block_slot {
mp_obj_t obj;
} synthio_block_slot_t;
typedef struct {
MP_PROTOCOL_HEAD;
mp_float_t (*tick)(mp_obj_t obj);
} synthio_block_proto_t;
// Update the value inside the lfo slot if the value is an LFO, returning the new value
mp_float_t synthio_block_slot_get(synthio_block_slot_t *block_slot);
// the same, but the output is constrained to be between lo and hi
mp_float_t synthio_block_slot_get_limited(synthio_block_slot_t *block_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 15 fractional bits
int32_t synthio_block_slot_get_scaled(synthio_block_slot_t *block_slot, mp_float_t lo, mp_float_t hi);
// Assign an object (which may be a float or a synthio_block_obj_t) to an block slot
void synthio_block_assign_slot(mp_obj_t obj, synthio_block_slot_t *block_slot, qstr arg_name);

View File

@ -0,0 +1,8 @@
from synthio import *
for values in ((-1, 4, 9), (4, -1, 9), (9, 4, -1), (0, 0, 0), (3, 2, 1), (2, 1), (1,)):
for op in MathOperation.__dict__.values():
o = Math(op, *values)
print(op, o)
print(lfo_tick(o))
print()

View File

@ -0,0 +1,203 @@
synthio.MathOperation.SUM Math(operation=synthio.MathOperation.SUM, a=-1, b=4, c=9)
(12.0,)
synthio.MathOperation.ADD_SUB Math(operation=synthio.MathOperation.ADD_SUB, a=-1, b=4, c=9)
(-6.0,)
synthio.MathOperation.PRODUCT Math(operation=synthio.MathOperation.PRODUCT, a=-1, b=4, c=9)
(-36.0,)
synthio.MathOperation.MUL_DIV Math(operation=synthio.MathOperation.MUL_DIV, a=-1, b=4, c=9)
(-0.4444444444444445,)
synthio.MathOperation.SCALE_OFFSET Math(operation=synthio.MathOperation.SCALE_OFFSET, a=-1, b=4, c=9)
(5.0,)
synthio.MathOperation.OFFSET_SCALE Math(operation=synthio.MathOperation.OFFSET_SCALE, a=-1, b=4, c=9)
(27.0,)
synthio.MathOperation.LERP Math(operation=synthio.MathOperation.LERP, a=-1, b=4, c=9)
(44.0,)
synthio.MathOperation.CONSTRAINED_LERP Math(operation=synthio.MathOperation.CONSTRAINED_LERP, a=-1, b=4, c=9)
(4.0,)
synthio.MathOperation.DIV_ADD Math(operation=synthio.MathOperation.DIV_ADD, a=-1, b=4, c=9)
(8.75,)
synthio.MathOperation.ADD_DIV Math(operation=synthio.MathOperation.ADD_DIV, a=-1, b=4, c=9)
(0.3333333333333333,)
synthio.MathOperation.MID Math(operation=synthio.MathOperation.MID, a=-1, b=4, c=9)
(4.0,)
synthio.MathOperation.MAX Math(operation=synthio.MathOperation.MAX, a=-1, b=4, c=9)
(9.0,)
synthio.MathOperation.MIN Math(operation=synthio.MathOperation.MIN, a=-1, b=4, c=9)
(-1.0,)
synthio.MathOperation.ABS Math(operation=synthio.MathOperation.ABS, a=-1, b=4, c=9)
(1.0,)
synthio.MathOperation.SUM Math(operation=synthio.MathOperation.SUM, a=4, b=-1, c=9)
(12.0,)
synthio.MathOperation.ADD_SUB Math(operation=synthio.MathOperation.ADD_SUB, a=4, b=-1, c=9)
(-6.0,)
synthio.MathOperation.PRODUCT Math(operation=synthio.MathOperation.PRODUCT, a=4, b=-1, c=9)
(-36.0,)
synthio.MathOperation.MUL_DIV Math(operation=synthio.MathOperation.MUL_DIV, a=4, b=-1, c=9)
(-0.4444444444444445,)
synthio.MathOperation.SCALE_OFFSET Math(operation=synthio.MathOperation.SCALE_OFFSET, a=4, b=-1, c=9)
(5.0,)
synthio.MathOperation.OFFSET_SCALE Math(operation=synthio.MathOperation.OFFSET_SCALE, a=4, b=-1, c=9)
(27.0,)
synthio.MathOperation.LERP Math(operation=synthio.MathOperation.LERP, a=4, b=-1, c=9)
(-41.0,)
synthio.MathOperation.CONSTRAINED_LERP Math(operation=synthio.MathOperation.CONSTRAINED_LERP, a=4, b=-1, c=9)
(-1.0,)
synthio.MathOperation.DIV_ADD Math(operation=synthio.MathOperation.DIV_ADD, a=4, b=-1, c=9)
(5.0,)
synthio.MathOperation.ADD_DIV Math(operation=synthio.MathOperation.ADD_DIV, a=4, b=-1, c=9)
(0.3333333333333333,)
synthio.MathOperation.MID Math(operation=synthio.MathOperation.MID, a=4, b=-1, c=9)
(4.0,)
synthio.MathOperation.MAX Math(operation=synthio.MathOperation.MAX, a=4, b=-1, c=9)
(9.0,)
synthio.MathOperation.MIN Math(operation=synthio.MathOperation.MIN, a=4, b=-1, c=9)
(-1.0,)
synthio.MathOperation.ABS Math(operation=synthio.MathOperation.ABS, a=4, b=-1, c=9)
(4.0,)
synthio.MathOperation.SUM Math(operation=synthio.MathOperation.SUM, a=9, b=4, c=-1)
(12.0,)
synthio.MathOperation.ADD_SUB Math(operation=synthio.MathOperation.ADD_SUB, a=9, b=4, c=-1)
(14.0,)
synthio.MathOperation.PRODUCT Math(operation=synthio.MathOperation.PRODUCT, a=9, b=4, c=-1)
(-36.0,)
synthio.MathOperation.MUL_DIV Math(operation=synthio.MathOperation.MUL_DIV, a=9, b=4, c=-1)
(-36.0,)
synthio.MathOperation.SCALE_OFFSET Math(operation=synthio.MathOperation.SCALE_OFFSET, a=9, b=4, c=-1)
(35.0,)
synthio.MathOperation.OFFSET_SCALE Math(operation=synthio.MathOperation.OFFSET_SCALE, a=9, b=4, c=-1)
(-13.0,)
synthio.MathOperation.LERP Math(operation=synthio.MathOperation.LERP, a=9, b=4, c=-1)
(14.0,)
synthio.MathOperation.CONSTRAINED_LERP Math(operation=synthio.MathOperation.CONSTRAINED_LERP, a=9, b=4, c=-1)
(9.0,)
synthio.MathOperation.DIV_ADD Math(operation=synthio.MathOperation.DIV_ADD, a=9, b=4, c=-1)
(1.25,)
synthio.MathOperation.ADD_DIV Math(operation=synthio.MathOperation.ADD_DIV, a=9, b=4, c=-1)
(-13.0,)
synthio.MathOperation.MID Math(operation=synthio.MathOperation.MID, a=9, b=4, c=-1)
(4.0,)
synthio.MathOperation.MAX Math(operation=synthio.MathOperation.MAX, a=9, b=4, c=-1)
(9.0,)
synthio.MathOperation.MIN Math(operation=synthio.MathOperation.MIN, a=9, b=4, c=-1)
(-1.0,)
synthio.MathOperation.ABS Math(operation=synthio.MathOperation.ABS, a=9, b=4, c=-1)
(9.0,)
synthio.MathOperation.SUM Math(operation=synthio.MathOperation.SUM, a=0, b=0, c=0)
(0.0,)
synthio.MathOperation.ADD_SUB Math(operation=synthio.MathOperation.ADD_SUB, a=0, b=0, c=0)
(0.0,)
synthio.MathOperation.PRODUCT Math(operation=synthio.MathOperation.PRODUCT, a=0, b=0, c=0)
(0.0,)
synthio.MathOperation.MUL_DIV Math(operation=synthio.MathOperation.MUL_DIV, a=0, b=0, c=0)
(0.0,)
synthio.MathOperation.SCALE_OFFSET Math(operation=synthio.MathOperation.SCALE_OFFSET, a=0, b=0, c=0)
(0.0,)
synthio.MathOperation.OFFSET_SCALE Math(operation=synthio.MathOperation.OFFSET_SCALE, a=0, b=0, c=0)
(0.0,)
synthio.MathOperation.LERP Math(operation=synthio.MathOperation.LERP, a=0, b=0, c=0)
(0.0,)
synthio.MathOperation.CONSTRAINED_LERP Math(operation=synthio.MathOperation.CONSTRAINED_LERP, a=0, b=0, c=0)
(0.0,)
synthio.MathOperation.DIV_ADD Math(operation=synthio.MathOperation.DIV_ADD, a=0, b=0, c=0)
(0.0,)
synthio.MathOperation.ADD_DIV Math(operation=synthio.MathOperation.ADD_DIV, a=0, b=0, c=0)
(0.0,)
synthio.MathOperation.MID Math(operation=synthio.MathOperation.MID, a=0, b=0, c=0)
(0.0,)
synthio.MathOperation.MAX Math(operation=synthio.MathOperation.MAX, a=0, b=0, c=0)
(0.0,)
synthio.MathOperation.MIN Math(operation=synthio.MathOperation.MIN, a=0, b=0, c=0)
(0.0,)
synthio.MathOperation.ABS Math(operation=synthio.MathOperation.ABS, a=0, b=0, c=0)
(0.0,)
synthio.MathOperation.SUM Math(operation=synthio.MathOperation.SUM, a=3, b=2, c=1)
(6.0,)
synthio.MathOperation.ADD_SUB Math(operation=synthio.MathOperation.ADD_SUB, a=3, b=2, c=1)
(4.0,)
synthio.MathOperation.PRODUCT Math(operation=synthio.MathOperation.PRODUCT, a=3, b=2, c=1)
(6.0,)
synthio.MathOperation.MUL_DIV Math(operation=synthio.MathOperation.MUL_DIV, a=3, b=2, c=1)
(6.0,)
synthio.MathOperation.SCALE_OFFSET Math(operation=synthio.MathOperation.SCALE_OFFSET, a=3, b=2, c=1)
(7.0,)
synthio.MathOperation.OFFSET_SCALE Math(operation=synthio.MathOperation.OFFSET_SCALE, a=3, b=2, c=1)
(5.0,)
synthio.MathOperation.LERP Math(operation=synthio.MathOperation.LERP, a=3, b=2, c=1)
(2.0,)
synthio.MathOperation.CONSTRAINED_LERP Math(operation=synthio.MathOperation.CONSTRAINED_LERP, a=3, b=2, c=1)
(2.0,)
synthio.MathOperation.DIV_ADD Math(operation=synthio.MathOperation.DIV_ADD, a=3, b=2, c=1)
(2.5,)
synthio.MathOperation.ADD_DIV Math(operation=synthio.MathOperation.ADD_DIV, a=3, b=2, c=1)
(5.0,)
synthio.MathOperation.MID Math(operation=synthio.MathOperation.MID, a=3, b=2, c=1)
(2.0,)
synthio.MathOperation.MAX Math(operation=synthio.MathOperation.MAX, a=3, b=2, c=1)
(3.0,)
synthio.MathOperation.MIN Math(operation=synthio.MathOperation.MIN, a=3, b=2, c=1)
(1.0,)
synthio.MathOperation.ABS Math(operation=synthio.MathOperation.ABS, a=3, b=2, c=1)
(3.0,)
synthio.MathOperation.SUM Math(operation=synthio.MathOperation.SUM, a=2, b=1, c=1)
(4.0,)
synthio.MathOperation.ADD_SUB Math(operation=synthio.MathOperation.ADD_SUB, a=2, b=1, c=1)
(2.0,)
synthio.MathOperation.PRODUCT Math(operation=synthio.MathOperation.PRODUCT, a=2, b=1, c=1)
(2.0,)
synthio.MathOperation.MUL_DIV Math(operation=synthio.MathOperation.MUL_DIV, a=2, b=1, c=1)
(2.0,)
synthio.MathOperation.SCALE_OFFSET Math(operation=synthio.MathOperation.SCALE_OFFSET, a=2, b=1, c=1)
(3.0,)
synthio.MathOperation.OFFSET_SCALE Math(operation=synthio.MathOperation.OFFSET_SCALE, a=2, b=1, c=1)
(3.0,)
synthio.MathOperation.LERP Math(operation=synthio.MathOperation.LERP, a=2, b=1, c=1)
(1.0,)
synthio.MathOperation.CONSTRAINED_LERP Math(operation=synthio.MathOperation.CONSTRAINED_LERP, a=2, b=1, c=1)
(1.0,)
synthio.MathOperation.DIV_ADD Math(operation=synthio.MathOperation.DIV_ADD, a=2, b=1, c=1)
(3.0,)
synthio.MathOperation.ADD_DIV Math(operation=synthio.MathOperation.ADD_DIV, a=2, b=1, c=1)
(3.0,)
synthio.MathOperation.MID Math(operation=synthio.MathOperation.MID, a=2, b=1, c=1)
(1.0,)
synthio.MathOperation.MAX Math(operation=synthio.MathOperation.MAX, a=2, b=1, c=1)
(2.0,)
synthio.MathOperation.MIN Math(operation=synthio.MathOperation.MIN, a=2, b=1, c=1)
(1.0,)
synthio.MathOperation.ABS Math(operation=synthio.MathOperation.ABS, a=2, b=1, c=1)
(2.0,)
synthio.MathOperation.SUM Math(operation=synthio.MathOperation.SUM, a=1, b=0, c=1)
(2.0,)
synthio.MathOperation.ADD_SUB Math(operation=synthio.MathOperation.ADD_SUB, a=1, b=0, c=1)
(0.0,)
synthio.MathOperation.PRODUCT Math(operation=synthio.MathOperation.PRODUCT, a=1, b=0, c=1)
(0.0,)
synthio.MathOperation.MUL_DIV Math(operation=synthio.MathOperation.MUL_DIV, a=1, b=0, c=1)
(0.0,)
synthio.MathOperation.SCALE_OFFSET Math(operation=synthio.MathOperation.SCALE_OFFSET, a=1, b=0, c=1)
(1.0,)
synthio.MathOperation.OFFSET_SCALE Math(operation=synthio.MathOperation.OFFSET_SCALE, a=1, b=0, c=1)
(1.0,)
synthio.MathOperation.LERP Math(operation=synthio.MathOperation.LERP, a=1, b=0, c=1)
(0.0,)
synthio.MathOperation.CONSTRAINED_LERP Math(operation=synthio.MathOperation.CONSTRAINED_LERP, a=1, b=0, c=1)
(0.0,)
synthio.MathOperation.DIV_ADD Math(operation=synthio.MathOperation.DIV_ADD, a=1, b=0, c=1)
(0.0,)
synthio.MathOperation.ADD_DIV Math(operation=synthio.MathOperation.ADD_DIV, a=1, b=0, c=1)
(1.0,)
synthio.MathOperation.MID Math(operation=synthio.MathOperation.MID, a=1, b=0, c=1)
(1.0,)
synthio.MathOperation.MAX Math(operation=synthio.MathOperation.MAX, a=1, b=0, c=1)
(1.0,)
synthio.MathOperation.MIN Math(operation=synthio.MathOperation.MIN, a=1, b=0, c=1)
(0.0,)
synthio.MathOperation.ABS Math(operation=synthio.MathOperation.ABS, a=1, b=0, c=1)
(1.0,)