/* 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 "raw_wave.h"
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

static char *get_full_path(char *fn)
{
  char *full_fn;
  char *fb_samples;

  if (fn == NULL || fn[0] == '/') return fn;
  fb_samples = getenv("FB_SAMPLES");
  if (fb_samples == NULL) fb_samples = FB_SAMPLES;
  sprintf(full_fn = (char *)malloc(strlen(fb_samples) + 1 + strlen(fn) + 1),
	  "%s/%s", fb_samples, fn);
  return full_fn;
}

void _fill_table(raw_wave *this)
{
  /*There is now a memory leak here i think because I am
    not freeing the old table before replacing it with the
    new one */ /* FIXED !! i believe */
  FILE *in;
  int i;
  short *tmp;
  int block_size = 8192;
  int current_block = 0;
  int count         = 0;
  
  in = fopen(this->filename,"r");
  /* PATCH BY: adam sjogren asjo@diku.dk */
  if( in==NULL )
    {
      fprintf(stderr, "Couldn't open sample: %s\n", this->filename);
      this->length=0;
      return;
    }
  if( this->table != NULL)
    {
      free(this->table);
    }
  this->length = 0; /*in the event that we are repopulating the table */
  this->current_index = 0;

  tmp = (short *)malloc(sizeof(short) * block_size);
  while((count = fread(tmp + (block_size * current_block),
		       sizeof(short),
		       block_size,
		       in))
	== block_size) {
    this->length += count;
    tmp  = realloc(tmp, sizeof(short) * 
		   block_size * (current_block + 2));
    current_block++;

  }
  this->length += count;
  this->table = (sample *)malloc(sizeof(sample) * this->length);
  for(i = 0; i < this->length;i++)
    this->table[i] = (sample)tmp[i];
  
  fclose(in);
  free(tmp);
}


void raw_wave_next_buffer(raw_wave *this) {
  this->next = 1;
}

sample *raw_wave_get_buffer(raw_wave *this)
{
    int i;
    event *e;
 
    if (this->next) {
      this->next = 0;
      for(i = 0;i < TBASS_BUFF_SIZE; i++)
	{
	  e = this->events[i];
	  if(e){
	    e->fire(e,(sample_producer*) this);
	    this->events[i] = NULL;
	  }

	  if(this->current_index >= this->length)
	    this->buffer[i] = 0;
	  else
	    {
	      /*this is a collapse of the whole interpolate
		and decimate pitch changing scheme */
	      sample s1,s2;
	      double    ci_w;
	      double    ci_f;
	      ci_f = modf(this->current_index, &ci_w);

	      s1 = this->table[(int)ci_w];
	      s2 = this->table[(int)ci_w + 1];
	      this->buffer[i] = (s2 - s1)*ci_f + s1;
	      this->current_index += this->pitch;

	
	    }

	
	}
    }
  return this->buffer;  
}
  
 void raw_wave_trigger(raw_wave *this)
{

  this->current_index = 0;

}

void raw_wave_set_pitch(raw_wave *this, double p)
{
  this->pitch = p;

}

void raw_wave_set_sample_file(raw_wave* this, char *filename)
{
  FILE *in;
  if (this->filename != 0) free(this->filename);
  this->filename = get_full_path(filename);
  in = fopen(this->filename,"r");
  if( in==NULL )
    {
      fprintf(stderr, "Couldn't open sample: %s\n", filename);
      return;
    } 
  _fill_table(this); 
}

typedef struct event_pitch_change{
  int seq_handle;
  void(* fire)(event *this, sample_producer *target);
  double pitch;
} event_pitch_change;

void fire_pitch_change(event_pitch_change *this, raw_wave *target)
{
  raw_wave_set_pitch(target,this->pitch);
}

event *event_pitch_change_new(int seq_handle, double pitch)
{
  event_pitch_change  *out = (event_pitch_change *)
    malloc(sizeof(event_pitch_change));
  out->seq_handle = seq_handle;
  out->fire = (void (*)(event *, sample_producer *))fire_pitch_change;
  out->pitch = pitch;
  return (event *)out;
}

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

static char **get_header(raw_wave *this)
{
  static char *header[] = {
    "sample *$n_table = ((raw_wave *)$t)->table;",
    "int $n_length = ((raw_wave *)$t)->length;",
    "sample $n_s1, $n_s2;",
    "double $n_ci_w, $n_ci_f, $n_pitch = ((raw_wave *)$t)->pitch;",
    NULL
  };
  return header;
}

static char **get_code(raw_wave *this)
{
  static char *code[] = {
    "if (((raw_wave *)$t)->current_index >= $n_length)",
    "  $o = 0;",
    "else {",
    "  $n_ci_f = modf(((raw_wave *)$t)->current_index, &$n_ci_w);",
    "  $n_s1 = $n_table[(int)$n_ci_w];",
    "  $n_s2 = $n_table[(int)$n_ci_w +1];",
    "  ((raw_wave *)$t)->current_index += $n_pitch;",
    "  $o = ($n_s2 - $n_s1) * $n_ci_f + $n_s1;",
    "}",
    NULL
  };
  return code;
}

static char **get_footer(raw_wave *this)
{
  static char *footer[] = {
    NULL
  };
  return footer;
}

raw_wave * raw_wave_new(char *filename)
{
  int i;
  raw_wave *out = (raw_wave *)malloc(sizeof(raw_wave));

  /* begin sample_producer interface */
  out->get_buffer = raw_wave_get_buffer;
  out->next_buffer = raw_wave_next_buffer;
  out->get_children	= get_children;
  out->get_header	= get_header;
  out->get_code		= get_code;
  out->get_footer	= get_footer;
  out->next = 0;
  out->schedule   = sp_schedule_event;
  out->trigger    = (void (*)(sample_producer *))raw_wave_trigger;
  out->events = (event **)malloc(sizeof(event *) 
				 * TBASS_BUFF_SIZE);
  for(i = 0; i < TBASS_BUFF_SIZE; i++)
    out->events[i] = NULL; 
  /* end sample_producer interface */
  /* set table to zero here check if it is zero in */
  /* _fill_table */
  out->table = 0;
  out->filename = 0;
  raw_wave_set_sample_file(out, filename);

  out->buffer   = (sample *)malloc(sizeof(sample) * TBASS_BUFF_SIZE);
  out->current_index = out->length;
  out->pitch = 1;

  return out;
}



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



