synthio: start generalizing LFO to Block
This commit is contained in:
parent
3d2db5dbe0
commit
5de4d197a2
|
@ -27,6 +27,7 @@
|
|||
|
||||
#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/LFO.h"
|
||||
|
@ -40,9 +41,9 @@
|
|||
//|
|
||||
//| `rate`, `offset`, `scale`, and `once` can be changed at run-time. `waveform` may be mutated.
|
||||
//|
|
||||
//| `waveform` must be a ``ReadableBuffer`` with elements of type `h` (16-bit signed integer).
|
||||
//| Internally, the elements of `waveform` are scaled so that the input
|
||||
//| range ``[-32768,32767]`` maps to ``[-1.0, 0.99996]``.
|
||||
//| `waveform` must be a ``ReadableBuffer`` with elements of type ``'h'``
|
||||
//| (16-bit signed integer). Internally, the elements of `waveform` are scaled
|
||||
//| so that the input range ``[-32768,32767]`` maps to ``[-1.0, 0.99996]``.
|
||||
//|
|
||||
//| An LFO only updates if it is actually associated with a playing `Synthesizer`,
|
||||
//| including indirectly via a `Note` or another intermediate LFO.
|
||||
|
@ -80,11 +81,11 @@ STATIC mp_obj_t synthio_lfo_make_new(const mp_obj_type_t *type_in, size_t n_args
|
|||
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;
|
||||
self->base.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;
|
||||
self->base.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);
|
||||
|
@ -232,10 +233,30 @@ STATIC const mp_rom_map_elem_t synthio_lfo_locals_dict_table[] = {
|
|||
};
|
||||
STATIC MP_DEFINE_CONST_DICT(synthio_lfo_locals_dict, synthio_lfo_locals_dict_table);
|
||||
|
||||
|
||||
STATIC const synthio_block_proto_t lfo_proto = {
|
||||
MP_PROTO_IMPLEMENT(MP_QSTR_synthio_block)
|
||||
.tick = common_hal_synthio_lfo_tick,
|
||||
};
|
||||
|
||||
const mp_obj_type_t synthio_lfo_type = {
|
||||
{ &mp_type_type },
|
||||
.flags = MP_TYPE_FLAG_EXTENDED,
|
||||
.name = MP_QSTR_LFO,
|
||||
.make_new = synthio_lfo_make_new,
|
||||
.locals_dict = (mp_obj_dict_t *)&synthio_lfo_locals_dict,
|
||||
.print = lfo_print,
|
||||
MP_TYPE_EXTENDED_FIELDS(
|
||||
.protocol = &lfo_proto,
|
||||
),
|
||||
};
|
||||
|
||||
#if 0
|
||||
const mp_obj_type_t synthio_math_type = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_Math,
|
||||
.make_new = synthio_math_make_new,
|
||||
.locals_dict = (mp_obj_dict_t *)&synthio_math_locals_dict,
|
||||
.print = math_print,
|
||||
};
|
||||
#endif
|
||||
|
|
|
@ -51,3 +51,4 @@ 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);
|
||||
mp_float_t common_hal_synthio_lfo_tick(mp_obj_t self_in);
|
||||
|
|
|
@ -294,9 +294,9 @@ 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);
|
||||
synthio_block_slot_t slot;
|
||||
synthio_block_assign_slot(args[i], &slot, MP_QSTR_arg);
|
||||
mp_float_t value = synthio_block_slot_get(&slot);
|
||||
result[i] = mp_obj_new_float(value);
|
||||
}
|
||||
return mp_obj_new_tuple(n, result);
|
||||
|
|
|
@ -31,24 +31,10 @@
|
|||
#define ONE (MICROPY_FLOAT_CONST(1.))
|
||||
#define ZERO (MICROPY_FLOAT_CONST(0.))
|
||||
|
||||
mp_float_t common_hal_synthio_lfo_tick(mp_obj_t self_in) {
|
||||
synthio_lfo_obj_t *lfo = MP_OBJ_TO_PTR(self_in);
|
||||
|
||||
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 rate = synthio_block_slot_get(&lfo->rate) * synthio_global_rate_scale;
|
||||
|
||||
mp_float_t accum = lfo->accum + rate;
|
||||
int len = lfo->waveform_bufinfo.len / 2;
|
||||
|
@ -71,16 +57,37 @@ mp_float_t synthio_lfo_obj_tick(synthio_lfo_slot_t *slot) {
|
|||
|
||||
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 scale = synthio_block_slot_get(&lfo->scale);
|
||||
mp_float_t offset = synthio_block_slot_get(&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);
|
||||
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;
|
||||
}
|
||||
|
@ -90,22 +97,22 @@ mp_float_t synthio_lfo_obj_tick_limited(synthio_lfo_slot_t *lfo_slot, mp_float_t
|
|||
return value;
|
||||
}
|
||||
|
||||
int32_t synthio_lfo_obj_tick_scaled(synthio_lfo_slot_t *lfo_slot, mp_float_t lo, mp_float_t hi) {
|
||||
mp_float_t value = synthio_lfo_obj_tick_limited(lfo_slot, lo, hi);
|
||||
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_lfo_assign_input(mp_obj_t obj, synthio_lfo_slot_t *slot, qstr arg_name) {
|
||||
if (mp_obj_is_type(obj, &synthio_lfo_type)) {
|
||||
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;
|
||||
}
|
||||
|
||||
if (!mp_obj_get_float_maybe(obj, &slot->value)) {
|
||||
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->value = mp_obj_get_float(obj);
|
||||
slot->obj = obj;
|
||||
}
|
||||
|
||||
|
@ -118,21 +125,21 @@ 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) {
|
||||
synthio_lfo_assign_input(arg, &self->rate, MP_QSTR_rate);
|
||||
synthio_block_assign_slot(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);
|
||||
synthio_block_assign_slot(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);
|
||||
synthio_block_assign_slot(arg, &self->offset, MP_QSTR_offset);
|
||||
}
|
||||
|
||||
bool common_hal_synthio_lfo_get_once(synthio_lfo_obj_t *self) {
|
||||
|
@ -143,7 +150,7 @@ 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) {
|
||||
return self->value;
|
||||
return self->base.value;
|
||||
}
|
||||
|
||||
mp_float_t common_hal_synthio_lfo_get_phase(synthio_lfo_obj_t *self) {
|
||||
|
|
|
@ -27,36 +27,43 @@
|
|||
#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_lfo_slot {
|
||||
mp_obj_t obj;
|
||||
typedef struct synthio_block_base {
|
||||
mp_obj_base_t base;
|
||||
uint8_t last_tick;
|
||||
mp_float_t value;
|
||||
} synthio_lfo_slot_t;
|
||||
} 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;
|
||||
|
||||
typedef struct synthio_lfo_obj {
|
||||
mp_obj_base_t base;
|
||||
|
||||
int32_t sample_rate;
|
||||
|
||||
uint8_t last_tick;
|
||||
synthio_block_base_t base;
|
||||
bool once;
|
||||
|
||||
synthio_lfo_slot_t rate, scale, offset;
|
||||
mp_float_t accum, value;
|
||||
synthio_block_slot_t rate, scale, offset;
|
||||
mp_float_t accum;
|
||||
|
||||
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);
|
||||
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_lfo_obj_tick_limited(synthio_lfo_slot_t *lfo_slot, mp_float_t lo, mp_float_t 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_lfo_obj_tick_scaled(synthio_lfo_slot_t *lfo_slot, mp_float_t lo, mp_float_t hi);
|
||||
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_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);
|
||||
// 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);
|
||||
|
|
|
@ -63,7 +63,7 @@ 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_in) {
|
||||
synthio_lfo_assign_input(value_in, &self->panning, MP_QSTR_panning);
|
||||
synthio_block_assign_slot(value_in, &self->panning, MP_QSTR_panning);
|
||||
}
|
||||
|
||||
mp_obj_t common_hal_synthio_note_get_amplitude(synthio_note_obj_t *self) {
|
||||
|
@ -71,7 +71,7 @@ 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_in) {
|
||||
synthio_lfo_assign_input(value_in, &self->amplitude, MP_QSTR_amplitude);
|
||||
synthio_block_assign_slot(value_in, &self->amplitude, MP_QSTR_amplitude);
|
||||
}
|
||||
|
||||
mp_obj_t common_hal_synthio_note_get_bend(synthio_note_obj_t *self) {
|
||||
|
@ -79,7 +79,7 @@ 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_in) {
|
||||
synthio_lfo_assign_input(value_in, &self->bend, MP_QSTR_bend);
|
||||
synthio_block_assign_slot(value_in, &self->bend, MP_QSTR_bend);
|
||||
}
|
||||
|
||||
mp_obj_t common_hal_synthio_note_get_ring_bend(synthio_note_obj_t *self) {
|
||||
|
@ -87,7 +87,7 @@ 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_in) {
|
||||
synthio_lfo_assign_input(value_in, &self->ring_bend, MP_QSTR_ring_bend);
|
||||
synthio_block_assign_slot(value_in, &self->ring_bend, MP_QSTR_ring_bend);
|
||||
}
|
||||
|
||||
mp_obj_t common_hal_synthio_note_get_envelope_obj(synthio_note_obj_t *self) {
|
||||
|
@ -177,7 +177,7 @@ STATIC uint32_t pitch_bend(uint32_t frequency_scaled, int32_t bend_value) {
|
|||
#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 panning = synthio_lfo_obj_tick_scaled(&self->panning, -ALMOST_ONE, ALMOST_ONE);
|
||||
int panning = synthio_block_slot_get_scaled(&self->panning, -ALMOST_ONE, ALMOST_ONE);
|
||||
int left_panning_scaled, right_panning_scaled;
|
||||
if (panning >= 0) {
|
||||
left_panning_scaled = 32768;
|
||||
|
@ -187,18 +187,18 @@ uint32_t synthio_note_step(synthio_note_obj_t *self, int32_t sample_rate, int16_
|
|||
left_panning_scaled = 32767 + panning;
|
||||
}
|
||||
|
||||
int amplitude = synthio_lfo_obj_tick_scaled(&self->amplitude, ZERO, ALMOST_ONE);
|
||||
int amplitude = synthio_block_slot_get_scaled(&self->amplitude, ZERO, ALMOST_ONE);
|
||||
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, -12, 12);
|
||||
int ring_bend_value = synthio_block_slot_get_scaled(&self->ring_bend, -12, 12);
|
||||
self->ring_frequency_bent = pitch_bend(self->ring_frequency_scaled, ring_bend_value);
|
||||
}
|
||||
|
||||
int bend_value = synthio_lfo_obj_tick_scaled(&self->bend, -12, 12);
|
||||
int bend_value = synthio_block_slot_get_scaled(&self->bend, -12, 12);
|
||||
uint32_t frequency_scaled = pitch_bend(self->frequency_scaled, bend_value);
|
||||
return frequency_scaled;
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
typedef struct synthio_note_obj {
|
||||
mp_obj_base_t base;
|
||||
|
||||
synthio_lfo_slot_t panning, bend, amplitude, ring_bend;
|
||||
synthio_block_slot_t panning, bend, amplitude, ring_bend;
|
||||
|
||||
mp_float_t frequency, ring_frequency;
|
||||
mp_obj_t waveform_obj, envelope_obj, ring_waveform_obj;
|
||||
|
|
|
@ -78,10 +78,9 @@ audioio_get_buffer_result_t synthio_synthesizer_get_buffer(synthio_synthesizer_o
|
|||
mp_obj_t iterable = mp_getiter(self->lfos, &iter_buf);
|
||||
mp_obj_t item;
|
||||
while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
|
||||
synthio_lfo_slot_t slot = { .obj = item };
|
||||
// does not error for invalid type, so it's OK that we don't police
|
||||
// list contents
|
||||
(void)synthio_lfo_obj_tick(&slot);
|
||||
synthio_block_slot_t slot;
|
||||
synthio_block_assign_slot(item, &slot, MP_QSTR_arg);
|
||||
(void)synthio_block_slot_get(&slot);
|
||||
}
|
||||
return GET_BUFFER_MORE_DATA;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue