synthio: Perform vibrato in pitch, not as frequency ratio

Now the vibrato 'units' are 1.0 = one octave, 1/12 = one semitone,
1/1200 = one cent. Before, the units were somewhat arbitrary and were not
perceptually "symmetrical" around the base frequency.

For vibrato_depth = 1/12 and base frequency of 440,

before: pitch from 403.33 to 476.67Hz, not corresponding to any notes
after: pitch from 415.30 to 466.16Hz, corresponding to G# and A#
This commit is contained in:
Jeff Epler 2023-05-06 21:34:48 -05:00
parent a53c0ed066
commit 9a9f3229fa
No known key found for this signature in database
GPG Key ID: D5BF15AB975AB4DE
2 changed files with 38 additions and 3 deletions

View File

@ -118,7 +118,12 @@ MP_PROPERTY_GETSET(synthio_note_amplitude_obj,
//| tremolo_depth: float
//| """The tremolo depth of the note, from 0 to 1"""
//| """The tremolo depth 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) {
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
return mp_obj_new_float(common_hal_synthio_note_get_tremolo_depth(self));
@ -154,7 +159,12 @@ MP_PROPERTY_GETSET(synthio_note_tremolo_rate_obj,
(mp_obj_t)&synthio_note_set_tremolo_rate_obj);
//| vibrato_depth: float
//| """The vibrato depth of the note, from 0 to 1"""
//| """The vibrato depth of the note, from 0 to 1
//|
//| A depth of 0 disables vibrato. A depth of 1 corresponds to a vibrato of ±1
//| octave. A depth of (1/12) = 0.833 corresponds to a vibrato of ±1 semitone,
//| and a depth of .00833 corresponds to one musical cent.
//| """
STATIC mp_obj_t synthio_note_get_vibrato_depth(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_vibrato_depth(self));

View File

@ -151,10 +151,35 @@ uint32_t synthio_note_envelope(synthio_note_obj_t *self) {
return self->amplitude_scaled;
}
// Perform a pitch bend operation
//
// bend_value is in the range [0, 65535]. "no change" is 32768. The bend unit is 32768/octave.
//
// compare to (frequency_scaled * pow(2, (bend_value-32768)/32768))
// a 13-entry pitch table
#define BEND_SCALE (32768)
#define BEND_OFFSET (BEND_SCALE)
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;
}
uint32_t bend_value_semitone = (uint32_t)bend_value * 24; // 65536/semitone
uint32_t semitone = bend_value_semitone >> 16;
uint32_t fractone = bend_value_semitone & 0xffff;
uint32_t f_lo = pitch_bend_table[semitone];
uint32_t f_hi = pitch_bend_table[semitone + 1]; // table has 13 entries, indexing with semitone=12 is OK
uint32_t f = ((f_lo * (65535 - fractone) + f_hi * fractone) >> 16) + BEND_OFFSET;
return (frequency_scaled * (uint64_t)f) >> (15 + down);
}
uint32_t synthio_note_step(synthio_note_obj_t *self, int32_t sample_rate, int16_t dur, uint16_t *loudness) {
int tremolo_value = synthio_lfo_step(&self->tremolo_state, dur);
int vibrato_value = synthio_lfo_step(&self->vibrato_state, dur);
*loudness = (*loudness * tremolo_value) >> 15;
uint32_t frequency_scaled = ((uint64_t)self->frequency_scaled * vibrato_value) >> 15;
uint32_t frequency_scaled = pitch_bend(self->frequency_scaled, vibrato_value);
return frequency_scaled;
}