/* Freebirth
 * Copyright (C) 1999 topher lafata <topher@topher.com>,
 *		      Jake Donham <jake@bitmechanic.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program (see COPYING); if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <assert.h>
#include "oscillator.h"


#define RATE 44100

static sample sin_table[RATE];
static sample sqr_table[RATE]; 
static sample saw_table[RATE];

static int tables_calculated = 0;

#define SAW_ZERO 11025
#define SIN_ZERO 0
#define SQR_ZERO 0

#define MAX_FREQ_OFFSET 32768
static int current_zero;

/*--------------private stuff-------------------------*/


void _fill_buffer(osc *this)
{
  int i, phase_factor;
  event *e;
  sample *buf = this->buffer;
  sample *tbl = this->table;

  for( i = 0; i < TBASS_BUFF_SIZE;i++)
    {
      e = this->events[i];
      if(e){
	e->fire(e,(sample_producer*)this);

	this->events[i] = NULL;
      }
      phase_factor = this->phase_offset / this->freq;
      buf[i] = tbl[this->current_index + phase_factor];
      this->current_index += this->freq + 
	(this->freq * this->freq_offset / MAX_FREQ_OFFSET);

      if(this->current_index + phase_factor >= RATE )
	{
	  this->current_index  = this->current_index + phase_factor - RATE;
	}

    }
}


void _print_buffer(osc *this)
  {
    int i;
    sample *buf = this->buffer;
    for(i = 0; i < TBASS_BUFF_SIZE;i++)
      {
	printf("%d\n",*buf++);

      }


  }




/*------------------end private stuff ----------------------*/

/*funtions to fill in the tables */

void init_tables()
{

  int h = 0; /* harmonic number for sqr wave */
  int i = 0;
  double scale = (2.0 * M_PI) / RATE;

  /* for saw table was 16000 and -16000*/
  double step = (double)( (double)(32760* 2) / (RATE / 2) ); 
  double val  = -32760;

  /* init sine table with 1hz wave*/
  for(i = 0;i < RATE; i++)
    sin_table[i] = (sample)(32767 * sin(i*scale));

  /* init square table with 1hz wave consisting of first 9 partials*/ 
  for(h = 1; h <= 9; h += 2)
    {
      for(i = 0;i < RATE; i++)
	{
	  sqr_table[i] += (int) (32767 * (((double)1 / h) * sin((2.0 * M_PI) * h * i / RATE)));
	}
    }

  
      /*sample pulse = 8000;
      if(i > RATE/2)
	pulse = -pulse;
	sqr_table[i] = pulse; */



  /* init saw table with 1hz wave    */
  for(i = 0;i < RATE/2; i++, val += step)
    {
      saw_table[i] = (sample)val;
      saw_table[RATE - i] = (sample)val;

    }

  saw_table[RATE / 2] = 32760;
  
  tables_calculated = 1;
}


/*oscillator object functions */

void osc_set_type(osc * this,int type)
{
  switch(type)
    {
    case SIN:
      this->table = sin_table;
      current_zero = SIN_ZERO;
      break;
    case SQR:
      this->table = sqr_table;
      current_zero = SQR_ZERO;
      break;
    case SAW:
      this->table = saw_table;
      current_zero = SAW_ZERO;
      break;
    default:
      this->table = sin_table;

    }
}

void osc_set_frequency(osc *this, int freq)
{
  this->freq = freq;

}

void osc_trigger(osc *this)
{
  /*do nothing...maybe reset the phase */
  /*what does it mean for an osc to be triggered */
  /*  this->current_index = this->phase_offset;   */

}

void osc_next_buffer(osc *this) {
  this->next = 1;
}

sample *osc_get_buffer(osc * this)
{
  if (this->next) {
    this->next = 0;
    _fill_buffer(this);
  }
  return this->buffer;
}

void osc_set_phase_offset(osc *this, int offset)
{
  this->phase_offset = offset;

}

void osc_set_freq_offset(osc *this, int offset)
{
  this->freq_offset = offset;

}

void osc_delete(osc * this)
{
  free(this->buffer);
  free(this->events);
  free(this);
}


/* specific oscillator events */
typedef struct event_freq_change{
  int seq_handle;
  void(* fire)(event *this, sample_producer *target);
  int freq;
} event_freq_change;


void fire_freq_change(event_freq_change *this, osc *target) {
  osc_set_frequency(target,this->freq);
}

event *event_freq_change_new(int seq_handle,int freq)
{
  event_freq_change *out = (event_freq_change *)
    malloc(sizeof (event_freq_change));
  out->seq_handle = seq_handle;
  out->fire = (void (*)(event *, sample_producer *))fire_freq_change;
  out->freq = freq;
  return (event *)out;
}





static sample_producer **get_children(osc *this)
{
  static sample_producer *no_kids[] = { NULL };
  return no_kids;
}

static char **get_header(osc *this)
{
  static char *header[] = {
    "int $n_phase_offset = ((osc *)$t)->phase_offset;",
    "double $n_freq_offset = ((osc *)$t)->freq_offset;",
    "int $n_freq = ((osc *)$t)->freq;",
    "int $n_phase_factor = $n_phase_offset / $n_freq;",
    "sample *$n_table = ((osc *)$t)->table;",
    "int $n_i = ((osc *)$t)->current_index;",
    NULL
  };
  return header;
}

static char **get_code(osc *this)
{
  static char *code[] = {
    "$o = $n_table[$n_i + $n_phase_factor];",
    "$n_i += $n_freq + ($n_freq * $n_freq_offset) / MAX_FREQ_OFFSET;",
    "if ($n_i + $n_phase_factor >= RATE)",
    "  $n_i = $n_i + $n_phase_factor - RATE;",
    NULL
  };
  return code;
}

static char **get_footer(osc *this)
{
  static char *footer[] = {
    "((osc *)$t)->current_index = $n_i;",
    NULL
  };
  return footer;
}

osc *osc_new(int freq, int type)
{
  int i;

    
  osc *out  = (osc *)malloc(sizeof(osc));
  out->get_buffer = osc_get_buffer;
  out->next_buffer = osc_next_buffer;
  out->get_children	= get_children;
  out->get_header	= get_header;
  out->get_code		= get_code;
  out->get_footer	= get_footer;
  out->schedule   = sp_schedule_event;
  out->trigger    = ( void (*)(sample_producer *))osc_trigger;
  out->events = (event **)malloc(sizeof(event *) 
				 * TBASS_BUFF_SIZE);
  for(i = 0; i < TBASS_BUFF_SIZE; i++)
    out->events[i] = NULL;


  out->freq = freq;
  out->buffer = (sample *)malloc(sizeof(int) * TBASS_BUFF_SIZE);


  if(!tables_calculated)
    init_tables();

  switch(type)
    {
    case SIN:
      out->table = sin_table;
      current_zero = SIN_ZERO;
      break;
    case SQR:
      out->table = sqr_table;
      current_zero = SQR_ZERO;
      break;
    case SAW:
      out->table = saw_table;
      current_zero = SAW_ZERO;
      break;
    }

  out->current_index = 0;
  out->phase_offset  = 0;
  out->freq_offset   = 0;
  return out;
}

/*
  Local Variables:
  mode: font-lock
  End:
*/







