summaryrefslogblamecommitdiffstats
path: root/src/synth_engine.c
blob: 85870012e8e6acb4aaa04f59e9376a5e61c3bba7 (plain) (tree)
1
2
3
4
5
6
7
                         
                       


                    

                   

































                                                                                                                             








                                                                                    




                                                                          



                                                                     




                                                                          

                                                                
 
                                                   




                                                                          
                                                  

                                                            




































                                                                         

 





                                                                                         
                                        
 










                                                                       
 




                                        




                                                                                                                                           



                      
                       





                                                                                                        
                                    
 
                                                                                

                                             
 
                                

                             
                                             

                                           



                









                                            









                                                      
                  


                     
                 
                                                    


                         



                              
               
     
 
                               


                        
                              
               
     
 


                                                             
 
                         

                          





                                          
     


                         




                    






                                         





                           
  
                      
                          




                          




















                                              

                    
                  
                   
                    

                           
                   






                       

                    
                                     
                    


                                                              
                                                                         






                                           
                                                                                         
                                                                               
                                                                                     



                                      
                                                                                    
















                                                                                                           
 



                           




                                  


                                 
#include "synth_engine.h"
#include "synth_math.h"
#include "lowpass.h"
#include "filter.h"

#include <string.h>

float
sin_sample(float amp, float freq, unsigned long long phase, unsigned int sample_rate)
{
  return amp * sinf(2.0 * M_PI * freq * ((float)phase / (float)sample_rate));
}

float
saw_sample(float amp, float freq, unsigned long long phase, unsigned int sample_rate)
{
  return amp * (0.17 * (1.0 - (2.0 * M_PI * freq * fmod((float)phase, (float)(sample_rate / (freq)))) / (float)sample_rate));
}

float
sawX_sample(float amp, float freq, float sm, unsigned long long phase, unsigned int sample_rate)
{
  float dOutput = 0.0;
  for (float n = 1.0; n < sm; n++)
    dOutput += (sinf(n * 2.0 * M_PI * freq * ((float)phase / (float)sample_rate))) / n;
  return 0.5 * amp * dOutput;
}

float
sqr_sample(float amp, float freq, float duty_cycle, unsigned long long phase, unsigned int sample_rate)
{
  if (duty_cycle < 0.0001 || duty_cycle > 0.9999) {
    duty_cycle = 0.5;
  }

  return (fmod((float)phase / (float)sample_rate, 1.0 / freq) < duty_cycle * (1.0 / freq)) ? amp : -amp;
}

float
gen0(float f, unsigned long long phase, float x, unsigned int sample_rate)
{
  return sin_sample(1, f, phase, sample_rate);
  /* return sqr_sample(0.1, f,  0.3,             phase, sample_rate) */
  /*        + sqr_sample(0.1, f * 3.0 / 2.0 , 0.5, phase, sample_rate) */
  /*        + saw_sample(0.3, f,                   phase, sample_rate) */
  /*        + sin_sample(0.1, f,                   phase, sample_rate) */
  /*        + sin_sample(0.1, f * 5,               phase, sample_rate) */
  /*        /\* + sin_sample(0.1, freq * 50 * 1021,       phase, sample_rate) *\/ */
  /*        /\* + sin_sample(0.1, freq * 50 * 3531021,    phase, sample_rate) *\/ */
  /*        + sin_sample(0.1, f * 7,               phase, sample_rate); */
}

float
gen1(float f, unsigned long long phase, float x, unsigned int sample_rate)
{
  return saw_sample(1, f, phase, sample_rate);
  /* return sawX_sample(0.5, f, 5, phase, sample_rate) */
  /*        + saw_sample(0.3, 2 * f / 5, phase, sample_rate) */
  /*        + sin_sample(0.2, f * 5.0 / 7.0 , phase, sample_rate); */
}

float
gen2(float f, unsigned long long phase, float x, unsigned int sample_rate)
{
  /* return sin_sample(0.5, f * sqrt(2) , phase, sample_rate) */
  /*   + sin_sample(0.5, f, phase, sample_rate); */

  return sawX_sample(1, f, 15, phase, sample_rate);
}

float
gen3(float f, unsigned long long phase, float x, unsigned int sample_rate)
{
  return sqr_sample(1, f, .5, phase, sample_rate);
  /* return sawX_sample(0.7, f, 5, phase, sample_rate) */
  /*   + sin_sample(0.3, 4.0/17.0*f, phase, sample_rate); */
  /* return saw_sample(0.5, f * (1 + sqrt(5)) / 2, phase, sample_rate) */
  /*        + sin_sample(0.3, f * x, phase, sample_rate) */
  /*        + sqr_sample(0.2, f * x, 0.2 * x * x, phase, sample_rate); */
}

float
lfo(float f, unsigned long long phase, unsigned int sample_rate) {
  return sin_sample(1, f, phase, sample_rate);
}

int
notes_active(synth_t *synth)
{
  int flag = 0;
  
  for (int i = 0; i < MIDI_NOTES; i++) {
    if (!synth->midi_note[i].active)
      continue;

    if (!adsr_amplitude(&synth->adsr,
                        (float)synth->midi_note[i].noteOn,
                        (float)synth->midi_note[i].noteOff,
                        (float)synth->midi_note[i].elapsed)
        && synth->midi_note[i].noteOff != 0) {
      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;
    } else {
      flag++;
    }
  }

  return flag;
}

float
make_sample(unsigned long long phase, void *synthData, unsigned int sample_rate, int viz)
{
  synth_t *synth = (synth_t*)synthData;
  float sample = 0;

  /* should never be 0 if we are here */

  /* Notes */
  float n = notes_active(synth);
  float rms = 0;
  for (int i = 0; i < MIDI_NOTES; i++) {
    if (!synth->midi_note[i].active)
      continue;

    rms += synth->midi_note[i].velocity * synth->midi_note[i].velocity;
  }

  rms = sqrt(rms / n);

  for (int i = 0; i < MIDI_NOTES; i++) {
    if (!synth->midi_note[i].active)
      continue;

    sample += 0.2 * rms
              * adsr_amplitude(&synth->adsr,
                               synth->midi_note[i].noteOn,
                               synth->midi_note[i].noteOff,
                               synth->midi_note[i].elapsed)
              * synth->gen[synth->geni](synth->midi_note[i].freq + synth->freq_offset, synth->midi_note[i].elapsed, synth->x, sample_rate);
  }

  /* filter */
  if (synth->filter) {
    // ALLL THE FILTERS
    float cutoff = round(synth->cutoff) + 1;

    cutoff = cutoff + cutoff * (synth->lfo.amp) * lfo(synth->lfo.freq, synth->lfo.elapsed, sample_rate);

    if (cutoff == 0) cutoff = 0.001;
    LowPass_Update(synth->resonance, cutoff, sample_rate);
    sample = LowPass_Filter(sample);

    update_bw_low_pass_filter(synth->fff, SAMPLE_RATE,cutoff, synth->resonance);
    sample = bw_low_pass(synth->fff, sample);
  }

  sample = synth->gain * sample;
    
  // band stop for high freqs
  sample = bw_band_stop(synth->fff2, sample);
  
  if (synth->clamp) sample = clamp(sample);
  
  return sample;
}

void
add_to_viz(synth_t *synth, float sample)
{
  synth->viz.wave[synth->viz.wi++] = sample;

  if (synth->viz.wi >= VIZ_BUF) {
    synth->viz.wi = 0;
  }
}

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;

  (void) timeInfo;
  (void) statusFlags;
  (void) inputBuffer;

  float s = 0.0f;
  for( unsigned long i=0; i<framesPerBuffer; i++ ) {
    if (!synth->active) {
      *out++ = 0.0f;
      *out++ = 0.0f;

      add_to_viz(synth, 0.0f);

      synth->lfo.elapsed = 0;
      continue;
    }

    if (!notes_active(synth)) {
      synth->active = 0;
      *out++ = 0.0f;
      *out++ = 0.0f;
      add_to_viz(synth, 0.0f);
      continue;
    }

    s = make_sample(synth->n.elapsed, synth, SAMPLE_RATE, 0);
    *out++ = s;
    *out++ = s;

    synth->lfo.elapsed++;
    synth->adsr.elapsed++;
    synth->n.elapsed++;

    for (int i = 0; i < MIDI_NOTES; i++) {
      if (!synth->midi_note[i].active)
        continue;

      synth->midi_note[i].elapsed++;
    }

    // viz
    add_to_viz(synth, s);
  }

  return paContinue;
}


static void
StreamFinished( void* synthData )
{
  synth_t *synth = (synth_t *) synthData;
}

void
init_synth(synth_t * synth)
{
  synth->freq_offset = 0;
  synth->gain = 1;
  synth->x = 1;
  
  synth->adsr.a = 0.0;
  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->lfo.freq = 1.0f;
  synth->lfo.amp = 0.0f;
  synth->lfo.elapsed = 0;
  
  for (int i =0; i<MIDI_NOTES; i++) {
    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].adsr = &(synth->adsr);
  }

  synth->n.freq    = 0; 
  synth->n.noteOn  = 0; 
  synth->n.noteOff = 1; 
  synth->n.key     = 0; 
  synth->n.elapsed = 0; 

  synth->octave = 3;

  synth->poly = 0;
  synth->multi = 0;
  synth->filter = 1;
  synth->cutoff = 22000.0f;
  synth->resonance = 1.0f;
  synth->clamp = 1;

  synth->gen[0] = gen0;
  synth->gen[1] = gen1;
  synth->gen[2] = gen2;
  synth->gen[3] = gen3;
  synth->geni = 0;

  synth->active = 0;
  
  synth->viz.sample_rate_divider = 1;
  synth->viz.wi = 0;

  LowPass_Init();
  synth->fff = create_bw_low_pass_filter(2, SAMPLE_RATE, 400);
  synth->fff2 = create_bw_band_stop_filter(8, SAMPLE_RATE, 15000, 22000);

  Pa_Initialize();

  int i;
  const   PaDeviceInfo *deviceInfo;
  for( i=0; i< Pa_GetDeviceCount(); i++ ) {
    deviceInfo = Pa_GetDeviceInfo( i );
    if (!strcmp("HyperX Cloud II Wireless: USB Audio (hw:2,0)", deviceInfo->name)) break;
    printf("dev: %s || %f\n", deviceInfo->name, deviceInfo->defaultSampleRate);
    //if (!strcmp("HDA Intel PCH: ALC1220 Analog (hw:0,0)", deviceInfo->name)) break;
  }


  PaStreamParameters outputParameters;
  outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
  printf("-------\nSelected device: %s\n-------\n", Pa_GetDeviceInfo(outputParameters.device)->name);
  outputParameters.channelCount = 2;       /* stereo output */
  outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
  outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
  outputParameters.hostApiSpecificStreamInfo = NULL;

  Pa_OpenStream(&(synth->stream),
                NULL, /* no input */
                &outputParameters,
                SAMPLE_RATE,
                FRAMES_PER_BUFFER,
                paClipOff,      /* we won't output out of range samples so don't bother clipping them */
                sound_gen,
                synth );

  Pa_SetStreamFinishedCallback(synth->stream, &StreamFinished);
  Pa_StartStream(synth->stream);
}

void
free_synth(synth_t * synth)
{
  Pa_StopStream( synth->stream );
  Pa_CloseStream( synth->stream );
  Pa_Terminate();


  free_bw_low_pass(synth->fff);
  free_bw_band_stop(synth->fff2);
}