/*
  MidiSynth, MIDI synthesiser, a repacement for the Acorn MIDI module

  synth.c - Synthesiser sample generators

  created  20/11/13
  1.0      14/06/14
*/

#include "main.h"
#include "kbd.h"

/*
 Synth generators
 ----------------------
This function runs at the sample rate, and executes all the synth generators.
It is responsible for most of the CPU loading, therefore speed is very important.
It returns the next audio sample.
*/
void synth_generators(syn_t *syn, int output[])
{
  int
    seed = syn->seed,
    step = 0x1d872b41;

  gen_t *g;

  for(g = &syn->gen[0]; g < &syn->gen[syn->num_gens]; g++)
  {
    int sw = g->switches;

    if(g->switches & (1<<ACTIVE))
    {
      // tone 1 generator
      int tone1 = (g->waveform1[g->tone1_pointer >> FIX] * g->tone1_volume) >> SCALE_BITS;
      g->tone1_pointer += g->tone1_inc; // store tone pointer

      // tone 2 generator
      int tone2 = (g->waveform2[g->tone2_pointer >> FIX] * g->tone2_volume) >> SCALE_BITS;
      g->tone2_pointer += g->tone2_inc + (tone1 * g->fm_depth); // store tone pointer

      // select tone output

      int tone = 0;
      tone1 = (tone1 * g->tone1_vol_vel) >> MIDI_PARAM_SCALE; // applied after FM mod

      if(sw & (1<<TONE1_OUT))
        tone += tone1;

      if(sw & (1<<TONE_RING_MOD))
        tone += (tone1 * tone2) >> (SCALE_BITS-1);

      if(sw & (1<<TONE2_OUT))
        tone += tone2;

      // noise generator
      int carry = seed & 0x80000000;
      seed <<= 1;
      if(carry)
        seed ^= step;

      int input = seed >> NOISE_SCALE;
      input = (input * g->noise_volume) >> SCALE_BITS;
      int sample = 0;

      // route tone output
      if(sw & (1<<FILTERED_TONE))
        input += tone; // filtered tone
      else
        sample += tone; // unfiltered tone

      // state variable filter
      int lp = g->z1 * g->filter_fc;
//      lp = g->z2 + (lp >> VLQ_BITS);
      lp = g->z2 + (lp / (1<<VLQ_BITS));
      int hp = g->z1 * g->filter_q;
//      hp = input - (hp >> VLQ_BITS) - lp;
      hp = input - (hp / (1<<VLQ_BITS)) - lp;
      int bp = g->filter_fc * hp;
//      bp = g->z1 + (bp >> VLQ_BITS);
      bp = g->z1 + (bp / (1<<VLQ_BITS));
      g->z1 = bp; // store filter history
      g->z2 = lp; // store filter history

      // select filter output
      if(sw & (1<<LOWPASS))
        sample += lp;
      if(sw & (1<<BANDPASS))
        sample += bp;
      if(sw & (1<<HIGHPASS))
        sample += hp;

      output[0] += (sample * g->left_vol)  >> (SYNTH_SCALE + MIDI_PARAM_SCALE);
      output[1] += (sample * g->right_vol) >> (SYNTH_SCALE + MIDI_PARAM_SCALE);
    }
  }
  syn->seed = seed; // store noise seed
}


