#include "synth_engine.h" #include "synth_math.h" #include "lowpass.h" #include "filter.h" #include "control.h" #include "sound.h" #include "midi.h" #include "osc.h" #include #include #include 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; } 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; } 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; } 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; } 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; } 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; } 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; } void deactivate_midi_note(midi_note_t * note) { note->freq = 0; note->channel = -1; note->noteOn = -1; note->noteOff = -1; note->wvt_index = 0; note->lfo_index = 0; note->velocity = -1; note->elapsed = -1; note->noteOffSample = 0; note->active = 0; } int notes_active(synth_t *synth) { midi_note_t * note; int j; for (int i = 0; i < synth->midi_active_n; i++) { note = synth->midi_active[i]; if (!fix_adsr(&synth->adsr, (float)note->noteOn, (float)note->noteOff, note->elapsed, note->noteOffSample) && note->noteOff != 0) { deactivate_midi_note(note); j = i + 1; for (; j < synth->midi_active_n; j++) { synth->midi_active[j - 1] = synth->midi_active[j]; } synth->midi_active[j - 1] = NULL; synth->midi_active_n--; } } return synth->midi_active_n; } #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; #include "biquad_filter.c" biquad_filter_t biquad = {0}; void do_fliter(synth_t *synth, float *sample, unsigned int sample_rate, int frame) { 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); */ } 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); } } float get_max_sample(synth_t *synth, int test_size) { float osc_wave[test_size]; for (int i = 0; i < test_size; i++) { osc_wave[i] = 0; } for (int i = 0; i < synth->midi_active_n; i++) { midi_note_t * note = synth->midi_active[i]; midi_note_t note_dup; note_dup.freq = note->freq; note_dup.channel = note->channel; note_dup.noteOn = note->noteOn; note_dup.noteOff = note->noteOff; note_dup.velocity = note->velocity; note_dup.wvt_index = 0; note_dup.lfo_index = note->lfo_index; note_dup.elapsed = note->elapsed; note_dup.noteOffSample = note->noteOffSample; note_dup.adsr = note->adsr; note_dup.active = note->active; for (int i = 0; i < test_size; i++) { osc_wave[i] += synth->gen[synth->geni](note_dup.freq * 3, ¬e_dup, synth->x, SAMPLE_RATE); } } float max = 0; for (int i = 0; i < test_size; i++) { if (fabs(osc_wave[i]) > max) max = fabs(osc_wave[i]); } return max; } # include // 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) { float sample = 0.0f; midi_note_t * note; if (synth->midi_active_n == 0) return sample; float rms = 0; for (int i = 0; i < synth->midi_active_n; i++) { rms += synth->midi_active[i]->velocity * synth->midi_active[i]->velocity; } 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); synth->adsr.peak = CC_GET(adsr_peak); synth->adsr.d = CC_GET(adsr_d); synth->adsr.s = CC_GET(adsr_s); synth->adsr.r = CC_GET(adsr_r); for (int i = 0; i < synth->midi_active_n; i++) { note = synth->midi_active[i]; float adsr = fix_adsr(&synth->adsr, note->noteOn, note->noteOff, note->elapsed, note->noteOffSample); float targ_freq = note->freq * CC_GET(pitch); targ_freq = targ_freq + targ_freq * CC_GET(lfo_amp) * osc_sin(note->lfo_index); note->lfo_index = osc_sin_next(CC_GET(lfo_freq), note->lfo_index); sample += rms * note->velocity * adsr * synth->gen[synth->geni](targ_freq, note, synth->x, 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); // 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; return sample; } void add_to_delay(synth_t *synth, float sample) { synth->del[synth->deli++] = sample; if (synth->deli >= SAMPLE_RATE * 10) { synth->deli = 0; } } void 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]) synth->midi_active[i]->elapsed++; } } float prev = 0; void get_frame(void *outputBuffer, synth_t *synth, int frame) { 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; } sample = make_sample(synth, SAMPLE_RATE, frame); synth->counter++; 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) { tmp = synth->del[idx]; } else { tmp = synth->del[SAMPLE_RATE * 10 + idx]; } sample = clamp(sample + synth->cc_del_feedback.target * tmp, -1, 1); } 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, &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]; } } int sound_gen(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *synthData) { synth_t *synth = (synth_t*)synthData; float *out = (float*)outputBuffer; if (!synth->sound_active) return 0; //paContinue; float buffer[2 * FRAMES_PER_BUFFER]; (void) timeInfo; (void) statusFlags; (void) inputBuffer; clock_t begin = clock(); // get_changes(); for (int i = 0; i < synth->cci; i++) { cc_prep(synth->ccs[i]); } // fill buffer for( unsigned long frame=0; framecci; i++) { cc_fix(synth->ccs[i]); } clock_t end = clock(); double time_spent = (double)(end - begin) / CLOCKS_PER_SEC; if (time_spent > (double)FRAMES_PER_BUFFER / SAMPLE_RATE) { printf("To generate %d samples per second we need to generate a sample every 1 / %d second. Since we generate samples by batching them in groups of %d, each group should take longer than %f. This one took %f\n", SAMPLE_RATE, SAMPLE_RATE, FRAMES_PER_BUFFER, (float)FRAMES_PER_BUFFER / SAMPLE_RATE, time_spent); } return paContinue; } synth_t * init_synth(void) { synth_t * synth = (synth_t *)malloc(sizeof(synth_t)); if (!synth) return NULL; synth->cci = 0; // CC(SYNTH, NAME, MIN, MAX, STEP, DEF) 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); CC(synth->cc_adsr_a, "attack", 0, 3, 0.01f, 0.00); CC(synth->cc_adsr_peak, "peak", 0, 1, 0.01f, 1.00); CC(synth->cc_adsr_d, "decay", 0, 2, 0.01f, 0.3); CC(synth->cc_adsr_s, "sustain", 0, 1.0f, 0.01f, 0.7f); CC(synth->cc_adsr_r, "release", 0, 5, 0.01f, 0.2f); CC(synth->cc_del_time, "time", 0, 3, 0.01f, 0.5f); CC(synth->cc_del_feedback, "feedback", 0, 1, 0.01f, 0.5f); CC(synth->cc_gain, "gain", 0, 1, 0.01f, 0.5f); 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; synth->adsr.a = 0.00001f; synth->adsr.peak = 1.0f; synth->adsr.d = 0.3; synth->adsr.s = 0.7; 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; synth->midi_active_n = 0; for (int i = 0; i < MIDI_NOTES; i++) { synth->midi_active[i] = NULL; synth->midi_note[i].freq = 0; synth->midi_note[i].channel = -1; synth->midi_note[i].noteOn = -1; synth->midi_note[i].noteOff = -1; synth->midi_note[i].velocity = -1; synth->midi_note[i].elapsed = -1; synth->midi_note[i].active = 0; synth->midi_note[i].wvt_index = 0; synth->midi_note[i].lfo_index = 0; synth->midi_note[i].adsr = &(synth->adsr); } synth->octave = 3; synth->delay = 0; synth->del = (float *) calloc(sizeof(float), SAMPLE_RATE * 30); synth->deli = 0; 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; synth->gen[1] = gen1; synth->gen[2] = gen2; synth->gen[3] = gen3; synth->gen[4] = gen4; synth->gen[5] = gen5; synth->gen[6] = gen6; synth->geni = 3; synth->active = 0; lpf_init(); synth->fff = create_bw_low_pass_filter(2, SAMPLE_RATE, 400); synth->fff2 = create_bw_band_stop_filter(8, SAMPLE_RATE, 15000, 22000); 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); PaUtil_InitializeRingBuffer(&synth->viz.wave_buffer, sizeof(float), RING_SIZE, synth->viz.wave_buffer_data); // for (int i = 0; i < RING_SIZE; i++) synth->viz.fft_buffer_data[i] = 0; synth->viz.fft_buffer_data = (float *)calloc(sizeof(float), RING_SIZE); PaUtil_InitializeRingBuffer(&synth->viz.fft_buffer, sizeof(float), RING_SIZE, synth->viz.fft_buffer_data); synth->viz.tmp_buffer = (float *)calloc(sizeof(float), RING_SIZE * 8); synth->viz.wave_viz_buffer = (float *)calloc(sizeof(float), RING_SIZE * 8); synth->viz.fft_input_buffer = (float *)calloc(sizeof(float), RING_SIZE * 8); synth->viz.fft_output_buffer = (float *)calloc(sizeof(float), RING_SIZE * 8); synth->viz.fft_smooth_buffer = (float *)calloc(sizeof(float), RING_SIZE * 8); synth->viz.spectrum_enabled = 0; synth->viz.wave_enabled = 0; synth->viz.adsr_enabled = 0; synth->viz.adsr_graph_enabled = 1; synth->viz.osc_enabled = 0; synth->viz.freeze = 0; synth->viz.tmp_index = 0; 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; } 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); free(synth->viz.tmp_buffer); free(synth->viz.wave_viz_buffer); 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); 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); }