summaryrefslogblamecommitdiffstats
path: root/src/synth_engine_v2.c
blob: ad1be7214a92e39e4ab1a3953c689f33936949d0 (plain) (tree)
1
2
3
4
5
6
7
8
9





                         
                 

                
                      





                                                                         
                             







                                                                         
                             







                                                                         
                             







                                                                         
                             







                                                                         
                             







                                                                         
                             







                                                                         
                             













































                                                                         
                                                                                  


                         





                             

                                                                             




























                                                                                                   
 
                      



                                          






                                                                                            





































                                                                                                  












                                                     












                                                                             
                                                         

                                          
                                 
                                       



                                 


















                                                                                   
 


                                                
 
                                 


                                               







                                                         




















                                                                                                                            
                          






                                                  

               
    
                                                        
 

                                                  
 
                             
                              
                      

                                





                       
                                                  
                   
                                                                                        
                                                                                                  






                                               
                                                                        

   














                                                   




                         






























                                                                               












                                                      

                                                   
                                      











                                        
                                                                
                      
                                    

   

                                               

                                                           
















                                                                                                                                                                                                                                                                                                                         

                
 


                                                       
                 
                                                                    


                                                                      











                                                                      







                                                                      
 








                           






                             





















                                              
                                                                 
                  
                     
    
                            
                    

                           
















                                                                         













                                                                                                              
                                  

                              
                                    


                             

                     
 









                                                                    
               





                           
                              







                                     

                    
  

                                 


                   
 





































































































































































                                                                         
#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 <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;
}

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, &note_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 <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)
{
  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; frame<framesPerBuffer; frame++ ) {
    // use iget inside
    get_frame(buffer, synth, frame);
  }

  smooth_buffer(buffer, framesPerBuffer, 0.1f);

  // output buffer
  for( unsigned long i=0; i<framesPerBuffer * 2; i += 2 ) {
    *out++ = buffer[i];
    *out++ = buffer[i+1];
  }
  // finalize_changes();
  for (int i = 0; i < synth->cci; 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);
}