diff options
author | grm <grm@eyesin.space> | 2025-02-22 02:36:27 +0200 |
---|---|---|
committer | grm <grm@eyesin.space> | 2025-02-22 02:36:27 +0200 |
commit | 500b9a07b93d6cd3e771edc5698e06d163da60f1 (patch) | |
tree | 08aace91a7ec7600b254b986bf5458362dab33f3 /src/synth_engine_v2.c | |
parent | 04b3dbe0a339c42d7b2085bcd6149e9277d699a1 (diff) | |
download | synth-project-500b9a07b93d6cd3e771edc5698e06d163da60f1.tar.gz synth-project-500b9a07b93d6cd3e771edc5698e06d163da60f1.tar.bz2 synth-project-500b9a07b93d6cd3e771edc5698e06d163da60f1.zip |
a year of changes (web, better soundcard handling, biquad)
Diffstat (limited to 'src/synth_engine_v2.c')
-rw-r--r-- | src/synth_engine_v2.c | 378 |
1 files changed, 340 insertions, 38 deletions
diff --git a/src/synth_engine_v2.c b/src/synth_engine_v2.c index bfd1d4a..ad1be72 100644 --- a/src/synth_engine_v2.c +++ b/src/synth_engine_v2.c @@ -4,14 +4,17 @@ #include "filter.h" #include "control.h" #include "sound.h" +#include "midi.h" #include "osc.h" +#include <libconfig.h> #include <string.h> #include <time.h> float gen0(float f, midi_note_t * midi_note, float x, unsigned int sample_rate) { + (void)x; (void)sample_rate; float sample = osc_sin(midi_note->wvt_index); midi_note->wvt_index = osc_sin_next(f, midi_note->wvt_index); return sample; @@ -20,6 +23,7 @@ gen0(float f, midi_note_t * midi_note, float x, unsigned int sample_rate) float gen1(float f, midi_note_t * midi_note, float x, unsigned int sample_rate) { + (void)x; (void)sample_rate; float sample = osc_saw(midi_note->wvt_index); midi_note->wvt_index = osc_saw_next(f, midi_note->wvt_index); return sample; @@ -28,6 +32,7 @@ gen1(float f, midi_note_t * midi_note, float x, unsigned int sample_rate) float gen2(float f, midi_note_t * midi_note, float x, unsigned int sample_rate) { + (void)x; (void)sample_rate; float sample = osc_weird(midi_note->wvt_index); midi_note->wvt_index = osc_weird_next(f, midi_note->wvt_index); return sample; @@ -36,6 +41,7 @@ gen2(float f, midi_note_t * midi_note, float x, unsigned int sample_rate) float gen3(float f, midi_note_t * midi_note, float x, unsigned int sample_rate) { + (void)x; (void)sample_rate; float sample = osc_tri(midi_note->wvt_index); midi_note->wvt_index = osc_tri_next(f, midi_note->wvt_index); return sample; @@ -44,6 +50,7 @@ gen3(float f, midi_note_t * midi_note, float x, unsigned int sample_rate) float gen4(float f, midi_note_t * midi_note, float x, unsigned int sample_rate) { + (void)x; (void)sample_rate; float sample = osc_sound(midi_note->wvt_index); midi_note->wvt_index = osc_sound_next(f, midi_note->wvt_index); return sample; @@ -52,6 +59,7 @@ gen4(float f, midi_note_t * midi_note, float x, unsigned int sample_rate) float gen5(float f, midi_note_t * midi_note, float x, unsigned int sample_rate) { + (void)x; (void)sample_rate; float sample = osc_digisaw(midi_note->wvt_index); midi_note->wvt_index = osc_digisaw_next(f, midi_note->wvt_index); return sample; @@ -60,6 +68,7 @@ gen5(float f, midi_note_t * midi_note, float x, unsigned int sample_rate) float gen6(float f, midi_note_t * midi_note, float x, unsigned int sample_rate) { + (void)x; (void)sample_rate; float sample = osc_sqr(midi_note->wvt_index); midi_note->wvt_index = osc_sqr_next(f, midi_note->wvt_index); return sample; @@ -106,23 +115,60 @@ notes_active(synth_t *synth) } #define CC_GET(name) cc_iget(&synth->cc_##name, frame, FRAMES_PER_BUFFER) +#define CC_SET(name, value) synth->cc_##name.mod = value - synth->cc_##name.target float prev_sample = 0.0f; -float +#include "biquad_filter.c" + +biquad_filter_t biquad = {0}; + + +void do_fliter(synth_t *synth, float *sample, unsigned int sample_rate, int frame) { - if (synth->filter) { - // ALLL THE FILTERS - float cutoff = CC_GET(cutoff); - float reso = CC_GET(resonance); + synth->f_adsr.a = CC_GET(f_adsr_a); + synth->f_adsr.peak = CC_GET(f_adsr_peak); + synth->f_adsr.d = CC_GET(f_adsr_d); + synth->f_adsr.s = CC_GET(f_adsr_s); + synth->f_adsr.r = CC_GET(f_adsr_r); + + // ALLL THE FILTERS + float cutoff = CC_GET(cutoff); + float reso = CC_GET(resonance); + + if (synth->f_adsr_enabled) { + midi_note_t *note; + if (synth->midi_active_n != 0) { + float latest = 0; + for (int i = 0; i < synth->midi_active_n; i++) { + // reverse this and set latest to a big number for the first note played to take precedence + if (synth->midi_active[i]->noteOn > latest) { + latest = synth->midi_active[i]->noteOn; + note = synth->midi_active[i]; + } + } + + cutoff = 50 + cutoff * fix_adsr(&synth->f_adsr, + note->noteOn, + note->noteOff, + note->elapsed, + note->noteOffSample); + } + } + if (synth->filter) { if (cutoff == 0) cutoff = 0.001; lpf_update(reso, cutoff, sample_rate); *sample = lpf_filter(*sample); - update_bw_low_pass_filter(synth->fff, SAMPLE_RATE, cutoff, reso); - *sample = bw_low_pass(synth->fff, *sample); + /* update_bw_low_pass_filter(synth->fff, SAMPLE_RATE, cutoff, reso); */ + /* *sample = bw_low_pass(synth->fff, *sample); */ + } + if (synth->biquad) { + //if (synth->cc_cutoff.mod || synth->cc_resonance.mod) //RUN ONLY WHEN THERE ARE CHANGES + biquad_calculate_coefficients(&biquad, cutoff, reso, sample_rate, synth->biquad_type); + *sample = biquad_process(&biquad, *sample); } } @@ -161,6 +207,19 @@ get_max_sample(synth_t *synth, int test_size) return max; } +# include <stdint.h> // uint32_t + +float Q_rsqrt(float number) +{ + union { + float f; + uint32_t i; + } conv = { .f = number }; + conv.i = 0x5f3759df - (conv.i >> 1); + conv.f *= 1.5F - (number * 0.5F * conv.f * conv.f); + return conv.f; +} + float make_sample(synth_t * synth, unsigned int sample_rate, int frame) { @@ -174,7 +233,7 @@ make_sample(synth_t * synth, unsigned int sample_rate, int frame) rms += synth->midi_active[i]->velocity * synth->midi_active[i]->velocity; } - rms = sqrt(rms / (float)synth->midi_active_n); + rms = 1.0 / Q_rsqrt(rms / (float)synth->midi_active_n); //float max = get_max_sample(synth, 20); synth->adsr.a = CC_GET(adsr_a); @@ -202,15 +261,23 @@ make_sample(synth_t * synth, unsigned int sample_rate, int frame) sample_rate); } + /* filter */ do_fliter(synth, &sample, sample_rate, frame); + sample = CC_GET(gain) * sample; // band stop for high freqs //sample = bw_band_stop(synth->fff2, sample); - - if (synth->clamp) sample = clamp(sample, -1, 1); + + //if (synth->clamp) sample = clamp(sample, -1, 1); + + // autogain + if (synth->autogain && (sample >= 1 || sample <= -1)) { + synth->cc_gain.target *= 0.999; + } + //printf("CLICK! %f\n", fabsf(prev_sample) - fabsf(sample)); //if (fabsf(fabsf(prev_sample) - fabsf(sample)) > 0.03) printf("CLICK! (diff: %f)\n", fabsf(prev_sample) - fabsf(sample)); prev_sample = sample; @@ -232,6 +299,7 @@ increment_synth(synth_t *synth) { synth->lfo.elapsed++; synth->adsr.elapsed++; + synth->f_adsr.elapsed++; for (int i = 0; i < synth->midi_active_n; i++) { if (synth->midi_active[i]) @@ -239,23 +307,28 @@ increment_synth(synth_t *synth) } } +float prev = 0; + void -get_frame(void *outputBuffer, synth_t *synth, int i) +get_frame(void *outputBuffer, synth_t *synth, int frame) { - float *out = (float*)outputBuffer + 2 * i; - float s = 0.0f; + float *out = (float*)outputBuffer + (2 * frame); + float sample = 0.0f; if (!notes_active(synth)) { + synth->f_adsr.elapsed = 0; synth->active = 0; + // auto gain test + //synth->cc_gain.target = 1; } if (!synth->delay) { synth->counter = 0; } - s = make_sample(synth, SAMPLE_RATE, i); + sample = make_sample(synth, SAMPLE_RATE, frame); synth->counter++; - if (synth->counter >= (int)(synth->cc_del_time.target * SAMPLE_RATE)) { + if (synth->counter >= (unsigned long long)(synth->cc_del_time.target * SAMPLE_RATE)) { int idx = (synth->deli - (int)(synth->cc_del_time.target * SAMPLE_RATE)) % (SAMPLE_RATE * 10); float tmp; if (idx >= 0) { @@ -264,18 +337,60 @@ get_frame(void *outputBuffer, synth_t *synth, int i) tmp = synth->del[SAMPLE_RATE * 10 + idx]; } - s = clamp(s + synth->cc_del_feedback.target * tmp, -1, 1); + sample = clamp(sample + synth->cc_del_feedback.target * tmp, -1, 1); } - add_to_delay(synth, s); - *out++ = s; - *out++ = s; + add_to_delay(synth, sample); + + + + //sample = clamp(sample, -1, 1); + + *out++ = sample; + *out++ = sample; + + if (sample > 1.0f || sample < -1.0f) + printf("%f\n", sample); + if (prev != 0.0f && fabs(prev - sample) > 0.5f) { + printf("%.2f --> %.2f\n", prev, sample); + } + prev = sample; // move time increment_synth(synth); // viz - PaUtil_WriteRingBuffer(&synth->viz.wave_buffer, &s, 1); + PaUtil_WriteRingBuffer(&synth->viz.wave_buffer, &sample, 1); +} + +/* void */ +/* smooth_buffer1(float *buffer) */ +/* { */ +/* return; */ +/* } */ +void smooth_buffer(float *buffer, int frames_per_buffer, float smooth_factor) { + if (smooth_factor < 0.0f || smooth_factor > 1.0f) { + printf("Invalid smooth factor. It should be between 0 and 1.\n"); + return; + } + + float prev_sample_ch1 = buffer[0]; // First sample for channel 1 + float prev_sample_ch2 = buffer[1]; // First sample for channel 2 + + for (int i = 0; i < frames_per_buffer; i++) { + int ch1_index = 2 * i; // Index for channel 1 + int ch2_index = 2 * i + 1; // Index for channel 2 + + // Smooth channel 1 + buffer[ch1_index] = (1.0f - smooth_factor) * buffer[ch1_index] + + smooth_factor * prev_sample_ch1; + prev_sample_ch1 = buffer[ch1_index]; + + // Smooth channel 2 + buffer[ch2_index] = (1.0f - smooth_factor) * buffer[ch2_index] + + smooth_factor * prev_sample_ch2; + prev_sample_ch2 = buffer[ch2_index]; + } } @@ -292,7 +407,6 @@ sound_gen(const void *inputBuffer, void *outputBuffer, if (!synth->sound_active) return 0; //paContinue; float buffer[2 * FRAMES_PER_BUFFER]; - float buffer2[2 * FRAMES_PER_BUFFER]; (void) timeInfo; (void) statusFlags; @@ -305,14 +419,15 @@ sound_gen(const void *inputBuffer, void *outputBuffer, cc_prep(synth->ccs[i]); } // fill buffer - for( unsigned long i=0; i<framesPerBuffer; i++ ) { + for( unsigned long frame=0; frame<framesPerBuffer; frame++ ) { // use iget inside - get_frame(buffer, synth, i); + get_frame(buffer, synth, frame); } + smooth_buffer(buffer, framesPerBuffer, 0.1f); + // output buffer for( unsigned long i=0; i<framesPerBuffer * 2; i += 2 ) { - // use iget inside *out++ = buffer[i]; *out++ = buffer[i+1]; } @@ -330,13 +445,6 @@ sound_gen(const void *inputBuffer, void *outputBuffer, return paContinue; } -void -m_init_synth(synth_t * synth) -{ - synth = (synth_t *)malloc(sizeof(synth_t)); - -} - synth_t * init_synth(void) { @@ -345,8 +453,9 @@ init_synth(void) synth->cci = 0; // CC(SYNTH, NAME, MIN, MAX, STEP, DEF) - CC(synth->cc_cutoff, "cutoff", 10, 22000, 30, 5000); - CC(synth->cc_resonance, "resonance", 1, 10, .02, 1); + CC(synth->cc_cutoff, "cutoff", 50, 22000, 30, 5000); + //CC(synth->cc_resonance, "resonance", 1, 10, .02, 1); + CC(synth->cc_resonance, "resonance", 0.01, 10, .02, 0.5); CC(synth->cc_lfo_freq, "lfo_freq", 1, 1000, 2, 1); CC(synth->cc_lfo_amp, "lfo_amp", 0, 1, .01f, 0); CC(synth->cc_pitch, "pitch", -3, 4, 0.01f, 1); @@ -359,7 +468,14 @@ init_synth(void) CC(synth->cc_del_feedback, "feedback", 0, 1, 0.01f, 0.5f); CC(synth->cc_gain, "gain", 0, 1, 0.01f, 0.5f); - //synth->modi = 0; + CC(synth->cc_f_adsr_a, "fattack", 0, 3, 0.01f, 0.00); + CC(synth->cc_f_adsr_peak, "fpeak", 0, 1, 0.01f, 1.00); + CC(synth->cc_f_adsr_d, "fdecay", 0, 2, 0.01f, 0.3); + CC(synth->cc_f_adsr_s, "fsustain", 0, 1.0f, 0.01f, 0.7f); + CC(synth->cc_f_adsr_r, "frelease", 0, 5, 0.01f, 0.2f); + + // synth->modi = 0; + synth->autogain = 1; synth->x = 1; @@ -370,6 +486,13 @@ init_synth(void) synth->adsr.r = 0.4; synth->adsr.elapsed = 0; + synth->f_adsr.a = 0.00001f; + synth->f_adsr.peak = 1.0f; + synth->f_adsr.d = 0.3; + synth->f_adsr.s = 0.7; + synth->f_adsr.r = 0.4; + synth->f_adsr.elapsed = 0; + synth->lfo.freq = 1.0f; synth->lfo.amp = 0.0f; synth->lfo.elapsed = 0; @@ -394,9 +517,12 @@ init_synth(void) synth->delay = 0; synth->del = (float *) calloc(sizeof(float), SAMPLE_RATE * 30); synth->deli = 0; - synth->counter; + synth->counter = 0; + synth->f_adsr_enabled = 0; synth->filter = 1; + synth->biquad = 0; + synth->biquad_type = 'l'; synth->clamp = 1; synth->gen[0] = gen0; @@ -414,9 +540,6 @@ init_synth(void) synth->fff = create_bw_low_pass_filter(2, SAMPLE_RATE, 400); synth->fff2 = create_bw_band_stop_filter(8, SAMPLE_RATE, 15000, 22000); - synth->sound_active = 0; - init_sound(synth, sound_gen); - synth->viz.rate_divider = 15; // for (int i = 0; i < RING_SIZE; i++) synth->viz.wave_buffer_data[i] = 0; synth->viz.wave_buffer_data = (float *)calloc(sizeof(float), RING_SIZE); @@ -441,6 +564,16 @@ init_synth(void) synth->wvt_pos = 0; + + + synth->sound_active = 0; + synth->soundcard_id = get_soundcard_id("default"); + init_sound(synth, sound_gen, synth->soundcard_id); + + synth->midi = (midi_t *)malloc(sizeof(midi_t)); + synth->midi_device_id = get_midi_device_id("Midi Through Port-0"); + init_midi(synth->midi, synth); + return synth; } @@ -448,6 +581,7 @@ void free_synth(synth_t * synth) { destroy_sound(synth); + terminate_midi(synth->midi); free(synth->viz.wave_buffer_data); free(synth->viz.fft_buffer_data); @@ -456,6 +590,8 @@ free_synth(synth_t * synth) free(synth->viz.fft_input_buffer); free(synth->viz.fft_output_buffer); free(synth->viz.fft_smooth_buffer); + + free(synth->midi); free_bw_low_pass(synth->fff); free_bw_band_stop(synth->fff2); @@ -463,3 +599,169 @@ free_synth(synth_t * synth) free(synth->del); free(synth); } + +void +change_soundcard(synth_t *synth) +{ + destroy_sound(synth); + synth->sound_active = 0; + init_sound(synth, sound_gen, synth->soundcard_id); +} + +void +change_midi_device(synth_t *synth) +{ + terminate_midi(synth->midi); + free(synth->midi); + + synth->midi = (midi_t *)malloc(sizeof(midi_t)); + init_midi(synth->midi, synth); +} + +int +load_synth(synth_t *synth, const char *path) +{ + (void)path; + config_t cfg; + const char *str; + double FLOAT; + + config_init(&cfg); + + /* Read the file. If there is an error, report it and exit. */ + if(! config_read_file(&cfg, "TEST.cfg")) + { + fprintf(stderr, "%s:%d - %s\n", config_error_file(&cfg), + config_error_line(&cfg), config_error_text(&cfg)); + config_destroy(&cfg); + return(EXIT_FAILURE); + } + + /* Get the store name. */ + if(config_lookup_string(&cfg, "synth.name", &str)) + printf("LOADING: %s ----\n---\n", str); + else + fprintf(stderr, "No 'synth.name' setting in configuration file.\n"); + + config_lookup_int(&cfg, "synth.generator", &synth->geni); + + config_lookup_float(&cfg, "synth.adsr.a", &FLOAT); + synth->cc_adsr_a.target = FLOAT; + config_lookup_float(&cfg, "synth.adsr.peak", &FLOAT); + synth->cc_adsr_peak.target = FLOAT; + config_lookup_float(&cfg, "synth.adsr.d", &FLOAT); + synth->cc_adsr_d.target = FLOAT; + config_lookup_float(&cfg, "synth.adsr.s", &FLOAT); + synth->cc_adsr_s.target = FLOAT; + config_lookup_float(&cfg, "synth.adsr.r", &FLOAT); + synth->cc_adsr_r.target = FLOAT; + + config_lookup_int(&cfg, "synth.delay.enable", &synth->delay); + config_lookup_float(&cfg, "synth.delay.time", &FLOAT); + synth->cc_del_time.target = FLOAT; + config_lookup_float(&cfg, "synth.delay.feedback", &FLOAT); + synth->cc_del_feedback.target = FLOAT; + + config_lookup_int(&cfg, "synth.filter.enable", &synth->filter); + config_lookup_float(&cfg, "synth.filter.cutoff", &FLOAT); + synth->cc_cutoff.target = FLOAT; + config_lookup_float(&cfg, "synth.filter.resonance", &FLOAT); + synth->cc_resonance.target = FLOAT; + + config_lookup_float(&cfg, "synth.lfo.freq", &FLOAT); + synth->cc_lfo_freq.target = FLOAT; + config_lookup_float(&cfg, "synth.lfo.amp", &FLOAT); + synth->cc_lfo_amp.target = FLOAT; + + config_lookup_int(&cfg, "synth.autogain", &synth->autogain); + config_lookup_float(&cfg, "synth.gain", &FLOAT); + synth->cc_gain.target = FLOAT; + + config_destroy(&cfg); + return(EXIT_SUCCESS); +} + +int +save_synth(synth_t *synth, const char *path) +{ + (void)path; + + static const char *output_file = "TEST.cfg"; + + config_t cfg; + config_setting_t *root, *setting, *group, *adsr, *delay, *lfo, *filter; + + config_init(&cfg); + root = config_root_setting(&cfg); + + /* Add some settings to the configuration. */ + group = config_setting_add(root, "synth", CONFIG_TYPE_GROUP); + + setting = config_setting_add(group, "name", CONFIG_TYPE_STRING); + config_setting_set_string(setting, "example synth name"); + + setting = config_setting_add(group, "generator", CONFIG_TYPE_INT); + config_setting_set_int(setting, synth->geni); + + adsr = config_setting_add(group, "adsr", CONFIG_TYPE_GROUP); + setting = config_setting_add(adsr, "a", CONFIG_TYPE_FLOAT); + config_setting_set_float(setting, synth->cc_adsr_a.target); + setting = config_setting_add(adsr, "peak", CONFIG_TYPE_FLOAT); + config_setting_set_float(setting, synth->cc_adsr_peak.target); + setting = config_setting_add(adsr, "d", CONFIG_TYPE_FLOAT); + config_setting_set_float(setting, synth->cc_adsr_d.target); + setting = config_setting_add(adsr, "s", CONFIG_TYPE_FLOAT); + config_setting_set_float(setting, synth->cc_adsr_s.target); + setting = config_setting_add(adsr, "r", CONFIG_TYPE_FLOAT); + config_setting_set_float(setting, synth->cc_adsr_r.target); + + delay = config_setting_add(group, "delay", CONFIG_TYPE_GROUP); + setting = config_setting_add(delay, "enable", CONFIG_TYPE_INT); + config_setting_set_int(setting, synth->delay); + setting = config_setting_add(delay, "time", CONFIG_TYPE_FLOAT); + config_setting_set_float(setting, synth->cc_del_time.target); + setting = config_setting_add(delay, "feedback", CONFIG_TYPE_FLOAT); + config_setting_set_float(setting, synth->cc_del_feedback.target); + + filter = config_setting_add(group, "filter", CONFIG_TYPE_GROUP); + setting = config_setting_add(filter, "enable", CONFIG_TYPE_INT); + config_setting_set_int(setting, synth->filter); + setting = config_setting_add(filter, "cutoff", CONFIG_TYPE_FLOAT); + config_setting_set_float(setting, synth->cc_cutoff.target); + setting = config_setting_add(filter, "resonance", CONFIG_TYPE_FLOAT); + config_setting_set_float(setting, synth->cc_resonance.target); + + lfo = config_setting_add(group, "lfo", CONFIG_TYPE_GROUP); + setting = config_setting_add(lfo, "freq", CONFIG_TYPE_FLOAT); + config_setting_set_float(setting, synth->cc_lfo_freq.target); + setting = config_setting_add(lfo, "amp", CONFIG_TYPE_FLOAT); + config_setting_set_float(setting, synth->cc_lfo_amp.target); + + setting = config_setting_add(group, "autogain", CONFIG_TYPE_INT); + config_setting_set_int(setting, synth->autogain); + setting = config_setting_add(group, "gain", CONFIG_TYPE_FLOAT); + config_setting_set_float(setting, synth->cc_gain.target); + + + /* array = config_setting_add(root, "numbers", CONFIG_TYPE_ARRAY); */ + + /* for(i = 0; i < 10; ++i) */ + /* { */ + /* setting = config_setting_add(array, NULL, CONFIG_TYPE_INT); */ + /* config_setting_set_int(setting, 10 * i); */ + /* } */ + + /* Write out the new configuration. */ + if(! config_write_file(&cfg, output_file)) + { + fprintf(stderr, "Error while writing file.\n"); + config_destroy(&cfg); + return(EXIT_FAILURE); + } + + fprintf(stderr, "New configuration successfully written to: %s\n", + output_file); + + config_destroy(&cfg); + return(EXIT_SUCCESS); +} |