From 7478f829d21659e81843d8a7343f11d3c9985c44 Mon Sep 17 00:00:00 2001 From: Dan Ellis Date: Fri, 20 Sep 2024 18:14:38 -0400 Subject: [PATCH] src/amy.c,oscillators.c: Make Karplus-Strong (KS) work again. --- src/amy.c | 84 ++++++++++++++++++++++------------------------- src/oscillators.c | 30 ++++++++++++----- 2 files changed, 62 insertions(+), 52 deletions(-) diff --git a/src/amy.c b/src/amy.c index 45809bd..bbbd4f3 100644 --- a/src/amy.c +++ b/src/amy.c @@ -889,6 +889,10 @@ void osc_note_on(uint16_t osc, float initial_freq) { //printf("Note on: osc %d wav %d filter_freq_coefs=%f %f %f %f %f %f\n", osc, synth[osc].wave, // synth[osc].filter_logfreq_coefs[0], synth[osc].filter_logfreq_coefs[1], synth[osc].filter_logfreq_coefs[2], // synth[osc].filter_logfreq_coefs[3], synth[osc].filter_logfreq_coefs[4], synth[osc].filter_logfreq_coefs[5]); + // take care of fm & ks first -- no special treatment for bp/mod +#if AMY_KS_OSCS > 0 + if(synth[osc].wave==KS) ks_note_on(osc); +#endif if(synth[osc].wave==SINE) sine_note_on(osc, initial_freq); if(synth[osc].wave==SAW_DOWN) saw_down_note_on(osc, initial_freq); if(synth[osc].wave==SAW_UP) saw_up_note_on(osc, initial_freq); @@ -1049,51 +1053,43 @@ void play_event(struct delta d) { //synth[d.osc].amp_coefs[COEF_CONST] = *(float *)&d.data; // these could be decoupled, later synth[d.osc].velocity = *(float *)&d.data; synth[d.osc].status = AUDIBLE; - // take care of fm & ks first -- no special treatment for bp/mod - if(synth[d.osc].wave==KS) { - #if AMY_KS_OSCS > 0 - ks_note_on(d.osc); - #endif - } else { - // an osc came in with a note on. - // start the bp clock - synth[d.osc].note_on_clock = total_samples; //esp_timer_get_time() / 1000; - - // if there was a filter active for this voice, reset it - if(synth[d.osc].filter_type != FILTER_NONE) reset_filter(d.osc); - // For repeatability, start at zero phase. - synth[d.osc].phase = 0; + // an osc came in with a note on. + // start the bp clock + synth[d.osc].note_on_clock = total_samples; //esp_timer_get_time() / 1000; + + // if there was a filter active for this voice, reset it + if(synth[d.osc].filter_type != FILTER_NONE) reset_filter(d.osc); + // For repeatability, start at zero phase. + synth[d.osc].phase = 0; - // restart the waveforms - // Guess at the initial frequency depending only on const & note. Envelopes not "developed" yet. - float initial_logfreq = synth[d.osc].logfreq_coefs[COEF_CONST]; - if (AMY_IS_SET(synth[d.osc].midi_note)) { - // synth[d.osc].logfreq_coefs[COEF_CONST] = 0; - initial_logfreq += synth[d.osc].logfreq_coefs[COEF_NOTE] * logfreq_for_midi_note(synth[d.osc].midi_note); - } - // If we're coming out of note-off, set the freq history for portamento. - //if (AMY_IS_SET(synth[d.osc].note_off_clock)) - // msynth[d.osc].last_logfreq = initial_logfreq; - // Now we've tested that, we can reset note-off clocks. - AMY_UNSET(synth[d.osc].note_off_clock); // Most recent note event is not note-off. - AMY_UNSET(synth[d.osc].zero_amp_clock); - - float initial_freq = freq_of_logfreq(initial_logfreq); - osc_note_on(d.osc, initial_freq); - // trigger the mod source, if we have one - if(AMY_IS_SET(synth[d.osc].mod_source)) { - synth[synth[d.osc].mod_source].phase = synth[synth[d.osc].mod_source].trigger_phase; - - synth[synth[d.osc].mod_source].note_on_clock = total_samples; // Need a note_on_clock to have envelope work correctly. - if(synth[synth[d.osc].mod_source].wave==SINE) sine_mod_trigger(synth[d.osc].mod_source); - if(synth[synth[d.osc].mod_source].wave==SAW_DOWN) saw_up_mod_trigger(synth[d.osc].mod_source); - if(synth[synth[d.osc].mod_source].wave==SAW_UP) saw_down_mod_trigger(synth[d.osc].mod_source); - if(synth[synth[d.osc].mod_source].wave==TRIANGLE) triangle_mod_trigger(synth[d.osc].mod_source); - if(synth[synth[d.osc].mod_source].wave==PULSE) pulse_mod_trigger(synth[d.osc].mod_source); - if(synth[synth[d.osc].mod_source].wave==PCM) pcm_mod_trigger(synth[d.osc].mod_source); - if(synth[synth[d.osc].mod_source].wave==CUSTOM) custom_mod_trigger(synth[d.osc].mod_source); - } - + // restart the waveforms + // Guess at the initial frequency depending only on const & note. Envelopes not "developed" yet. + float initial_logfreq = synth[d.osc].logfreq_coefs[COEF_CONST]; + if (AMY_IS_SET(synth[d.osc].midi_note)) { + // synth[d.osc].logfreq_coefs[COEF_CONST] = 0; + initial_logfreq += synth[d.osc].logfreq_coefs[COEF_NOTE] * logfreq_for_midi_note(synth[d.osc].midi_note); + } + // If we're coming out of note-off, set the freq history for portamento. + //if (AMY_IS_SET(synth[d.osc].note_off_clock)) + // msynth[d.osc].last_logfreq = initial_logfreq; + // Now we've tested that, we can reset note-off clocks. + AMY_UNSET(synth[d.osc].note_off_clock); // Most recent note event is not note-off. + AMY_UNSET(synth[d.osc].zero_amp_clock); + + float initial_freq = freq_of_logfreq(initial_logfreq); + osc_note_on(d.osc, initial_freq); + // trigger the mod source, if we have one + if(AMY_IS_SET(synth[d.osc].mod_source)) { + synth[synth[d.osc].mod_source].phase = synth[synth[d.osc].mod_source].trigger_phase; + + synth[synth[d.osc].mod_source].note_on_clock = total_samples; // Need a note_on_clock to have envelope work correctly. + if(synth[synth[d.osc].mod_source].wave==SINE) sine_mod_trigger(synth[d.osc].mod_source); + if(synth[synth[d.osc].mod_source].wave==SAW_DOWN) saw_up_mod_trigger(synth[d.osc].mod_source); + if(synth[synth[d.osc].mod_source].wave==SAW_UP) saw_down_mod_trigger(synth[d.osc].mod_source); + if(synth[synth[d.osc].mod_source].wave==TRIANGLE) triangle_mod_trigger(synth[d.osc].mod_source); + if(synth[synth[d.osc].mod_source].wave==PULSE) pulse_mod_trigger(synth[d.osc].mod_source); + if(synth[synth[d.osc].mod_source].wave==PCM) pcm_mod_trigger(synth[d.osc].mod_source); + if(synth[synth[d.osc].mod_source].wave==CUSTOM) custom_mod_trigger(synth[d.osc].mod_source); } } else if(synth[d.osc].velocity > 0 && *(float *)&d.data == 0) { // new note off // DON'T clear velocity, we still need to reference it in decay. diff --git a/src/oscillators.c b/src/oscillators.c index ad3bec3..2882fc0 100644 --- a/src/oscillators.c +++ b/src/oscillators.c @@ -573,20 +573,25 @@ SAMPLE render_ks(SAMPLE * buf, uint16_t osc) { SAMPLE max_value = 0; if(freq >= 55) { // lowest note we can play uint16_t buflen = (uint16_t)(AMY_SAMPLE_RATE / freq); - for(uint16_t i=0;i max_value) max_value = value; + SAMPLE value = SMULR7(synth[osc].sample, amp); + buf[i] += value; + if (i == 0) { + max_value = value; + } else { + if (value > max_value) max_value = value; + else if (-value > max_value) max_value = -value; + } } } + //fprintf(stderr, "render_ks time %u osc %d freq %.1f amp %.3f maxval %.3f\n", total_samples, osc, freq, S2F(amp), S2F(max_value)); return max_value; } @@ -596,11 +601,20 @@ void ks_note_on(uint16_t osc) { uint16_t buflen = (uint16_t)(AMY_SAMPLE_RATE / freq); if(buflen > MAX_KS_BUFFER_LEN) buflen = MAX_KS_BUFFER_LEN; // init KS buffer with noise up to max - for(uint16_t i=0;i