synthio: improve documentation, simplify synthio_lfo_obj_tick_scaled
This commit is contained in:
parent
a682b42180
commit
3d2db5dbe0
|
@ -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,
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue