/*
 * osc.c - oscillator code
 */

#include <stdio.h>
#include <stdlib.h>
#include <strings.h>

#include "synth.h"
#include "osc.h"

int osc_init(oscillator * osc, waveform * wav) {
  osc->wav = wav; 
  osc->wavpos = 0;
  osc->wavfrac = 0;
  osc->freq = 0;
  osc->volume = 0;
  osc->flags = OSC_OFF;
  return 0;
}

int osc_on(oscillator * osc, unsigned long freq, unsigned char velocity) {
  osc->wavpos = 0;
  osc->wavfrac = 0;
  osc->freq = freq;
  osc->volume = velocity << (FREQSHIFT - 7);
  osc->flags = OSC_ON;

  if (osc->freq == 0 || osc->wav->freq == 0)
    osc->flags |= OSC_RAWSAMPLE;

  if (osc->wav->repeatstart == osc->wav->repeatend)
    osc->flags |= OSC_ONESHOT;

  return 0;
}

int osc_off(oscillator * osc) {
  osc->volume = 0;
  osc->freq = 0;
  osc->flags = OSC_OFF;
  return 0;
}


int osc_generate(oscillator * osc, unsigned long len, long * outbuf) {
  unsigned long i, inc, incfrac;
  long s1, s2, s3;

  if (osc->volume == 0 || osc->flags == OSC_OFF) 
    return 0;

  if (osc->flags & OSC_RAWSAMPLE) {
    /* RAW samples don't need any pitch shifting */
    inc = 1;
    incfrac = 0;
  }
  else {
    /* Pitch shifted samples */
    inc = osc->freq / osc->wav->freq;
    incfrac = osc->freq / (osc->wav->freq >> FREQSHIFT);
    incfrac &= 0xffff;
  }

  /* Generate samples, handle looping etc. */
  for (i=0; i<len; i++) {
    s1 = osc->wav->samples[osc->wavpos];
    s2 = osc->wav->samples[osc->wavpos + 1];
    s3 = s1 + (((s2 - s1) * ((long) osc->wavfrac)) >> FREQSHIFT);
    outbuf[i] += ((long) s3 * osc->volume) >> FREQSHIFT;
    
    osc->wavfrac += incfrac; 
    osc->wavpos += (inc + (osc->wavfrac >> FREQSHIFT));
    osc->wavfrac &= 0xffff;
    osc->wavpos = osc->wavpos % osc->wav->length;
  }

  return 0;
}


