synthio: improve documentation, simplify synthio_lfo_obj_tick_scaled

This commit is contained in:
Jeff Epler 2023-05-17 11:11:02 -05:00
parent a682b42180
commit 3d2db5dbe0
No known key found for this signature in database
GPG Key ID: D5BF15AB975AB4DE
4 changed files with 21 additions and 14 deletions

View File

@ -37,16 +37,23 @@
//| //|
//| Every `rate` seconds, the output of the LFO cycles through its `waveform`. //| Every `rate` seconds, the output of the LFO cycles through its `waveform`.
//| The output at any particular moment is ``waveform[idx] * scale + offset``. //| 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. //| `rate`, `offset`, `scale`, and `once` can be changed at run-time. `waveform` may be mutated.
//| //|
//| An LFO only updates if it is actually associated with a playing Note, //| `waveform` must be a ``ReadableBuffer`` with elements of type `h` (16-bit signed integer).
//| including if it is indirectly associated with the Note via an intermediate //| Internally, the elements of `waveform` are scaled so that the input
//| LFO. //| 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.
//| //|
//| Using the same LFO as an input to multiple other LFOs or Notes is OK, but //| 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.""" //| the result if an LFO is tied to multiple Synthtesizer objects is undefined.
//|
//| In the current implementation, LFOs are updated every 256 samples. This
//| should be considered an implementation detail, though it affects how LFOs
//| behave for instance when used to implement an integrator (``l.offset = l``).
//| """
//| //|
//| def __init__( //| def __init__(
//| self, //| self,

View File

@ -90,9 +90,9 @@ mp_float_t synthio_lfo_obj_tick_limited(synthio_lfo_slot_t *lfo_slot, mp_float_t
return value; 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) { 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); 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)); 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) { void synthio_lfo_assign_input(mp_obj_t obj, synthio_lfo_slot_t *slot, qstr arg_name) {

View File

@ -55,8 +55,8 @@ typedef struct synthio_lfo_obj {
mp_float_t synthio_lfo_obj_tick(synthio_lfo_slot_t *lfo_slot); 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 // 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_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 // 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, int shift); int32_t synthio_lfo_obj_tick_scaled(synthio_lfo_slot_t *lfo_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 // 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); void synthio_lfo_assign_input(mp_obj_t obj, synthio_lfo_slot_t *lfo_slot, qstr arg_name);

View File

@ -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) #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]) { 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, 15); int panning = synthio_lfo_obj_tick_scaled(&self->panning, -ALMOST_ONE, ALMOST_ONE);
int left_panning_scaled, right_panning_scaled; int left_panning_scaled, right_panning_scaled;
if (panning >= 0) { if (panning >= 0) {
left_panning_scaled = 32768; 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; left_panning_scaled = 32767 + panning;
} }
int amplitude = synthio_lfo_obj_tick_scaled(&self->amplitude, ZERO, ALMOST_ONE, 15); int amplitude = synthio_lfo_obj_tick_scaled(&self->amplitude, ZERO, ALMOST_ONE);
left_panning_scaled = (left_panning_scaled * amplitude) >> 15; left_panning_scaled = (left_panning_scaled * amplitude) >> 15;
right_panning_scaled = (right_panning_scaled * amplitude) >> 15; right_panning_scaled = (right_panning_scaled * amplitude) >> 15;
loudness[0] = (loudness[0] * left_panning_scaled) >> 15; loudness[0] = (loudness[0] * left_panning_scaled) >> 15;
loudness[1] = (loudness[1] * right_panning_scaled) >> 15; loudness[1] = (loudness[1] * right_panning_scaled) >> 15;
if (self->ring_frequency_scaled != 0) { if (self->ring_frequency_scaled != 0) {
int ring_bend_value = synthio_lfo_obj_tick_scaled(&self->ring_bend, -12, 12, 15); int ring_bend_value = synthio_lfo_obj_tick_scaled(&self->ring_bend, -12, 12);
self->ring_frequency_bent = pitch_bend(self->ring_frequency_scaled, ring_bend_value); 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, 15); int bend_value = synthio_lfo_obj_tick_scaled(&self->bend, -12, 12);
uint32_t frequency_scaled = pitch_bend(self->frequency_scaled, bend_value); uint32_t frequency_scaled = pitch_bend(self->frequency_scaled, bend_value);
return frequency_scaled; return frequency_scaled;