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

seq *seq_new(int length,int bpm,int time_division, void (*callback)(int step))
{

  int i;
  seq *out           = (seq *)malloc(sizeof(seq));
  out->length        = length;
  out->current_step  = -1;
  out->bpm           = bpm;
  
  /* samples per beat */
  out->spb           = 44100 * 60 / (out->bpm * time_division); 
  out->time_division = time_division;
  out->current_sample_offset = out->spb;
  out->callback              = callback;
  
  out->victims = (sample_producer **)malloc(sizeof(sample_producer*));
  out->event_map = (event_list **)malloc(sizeof(event_list *) * out->length);
  for(i = 0; i < out->length; i++)
    out->event_map[i] = event_list_new();
  out->current_victim_id = 0;
  return out;
}

void seq_delete(seq *this)
{
  /* this needs to be done */
  /* more for style coherence then anything else */
}

void seq_reset(seq *this)
{
this->current_step = -1;

}
void seq_set_bpm(seq *this, int bpm)
{
  this->bpm =  bpm;
  this->spb = 44100 * 60 / (bpm * this->time_division);

}

int seq_register_victim(seq * this, sample_producer *sp)
{
  int id;
  
  id = this->current_victim_id;
  
  this->victims[this->current_victim_id++] = sp;
  this->victims = realloc(this->victims,sizeof(sample_producer *) * 
			  (this->current_victim_id + 1));

  return id;
  
}

void seq_register_event(seq *this, int step, event *e)
{
  event_list_add(this->event_map[step],e);
}

void seq_delete_event(seq *this, int step, event *e)
{
  event_list_remove(this->event_map[step], e);
}

void seq_step(seq *this)
{
  this->current_sample_offset += TBASS_BUFF_SIZE;

  /*if we are at a beat boundry */
  if(this->current_sample_offset >= this->spb)
    {
      int trigger_offset,i;
      event_list *curr_step_el;

      /*increment the step(beat) */
      this->current_step++;

      /* if it is bigger then the length reset it to the first step*/
      if (this->current_step >= this->length)
	this->current_step = 0;
      
      /* flash the LED as is this case */
      this->callback(this->current_step);

      /* rectify the current offset in the current step */
      this->current_sample_offset -= this->spb;
      
      /* figure out the offset in the OSC buffer where it has 
	 to change frequency */
      trigger_offset = TBASS_BUFF_SIZE - this->current_sample_offset;

      	
      curr_step_el = this->event_map[this->current_step];
      for(i = 0; i < curr_step_el->length; i++)
	{
	  sample_producer *sp; 
	  event *e;
	  e	= curr_step_el->events[i];
	  sp = this->victims[e->seq_handle];
	  sp->schedule(sp,e,trigger_offset);
	}
	  
	
	  
    }
}
  
char **seq_get_header(seq *this) {
  static char *header[] = {
    "int seq_off = sequencer->current_sample_offset;",
    "int seq_spb = sequencer->spb;",
    "event_list *el;",
    "int j;",
    NULL
  };
  return header;
}

char **seq_get_code(seq *this) {
  static char *code[] = {
    "seq_off++;",
    "if (seq_off >= seq_spb) {",
    "  sequencer->current_step++;",
    "  if (sequencer->current_step >= sequencer->length)",
    "    sequencer->current_step = 0;",
    "  sequencer->callback(sequencer->current_step);",
    "  seq_off -= seq_spb;",
    "  el = sequencer->event_map[sequencer->current_step];",
    "  for (j=0; j < el->length; j++) {",
    "    event *e = el->events[j];",
    "    e->fire(e, sequencer->victims[e->seq_handle]);",
    "  }",
    "}",
    NULL
  };
  return code;
}

char **seq_get_footer(seq *this) {
  static char *footer[] = {
    "sequencer->current_sample_offset = seq_off;",
    NULL
  };
  return footer;
}


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