/* 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.
 */

#ifndef __ENV_H
  #include "envelope.h"
#endif

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





void env_delete(env *this)
{
  free(this->events);
  free(this);
}

static int attack(env *this) {
  return (SAMPLE_MAX - this->start) * this->current_index / this->attack 
    + this->start;
  
}


static int release(env *this) {
  return -SAMPLE_MAX * (this->current_index - this->attack) / 
    this->release + SAMPLE_MAX;
}

void env_trigger(env * this)
{
  if (this->current_index < this->attack)
    this->start = attack(this);
  else if (this->current_index < (this->attack + this->release))
    this->start = release(this);
  else
    this->start = 0;
  
  this->current_index = 0;

}

void env_set_release(env *this,int release)
{
  this->release = release;
}

void env_set_attack(env *this,int attack)
{
  this->attack = attack;
}



void env_next_buffer(env *this) {
  this->next = 1;
}

extern sample_producer *filt_e;
sample *env_get_buffer(env *this)
{
  int i;
  sample *buffer;
  event *e;

  if (this->next) {
    this->next = 0;

    if (this->sp) {
      buffer = this->sp->get_buffer(this->sp);
      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 we are in the attack phase */
	  if(this->current_index < this->attack)
	    this->buffer[i] = attack(this) * buffer[i] / SAMPLE_MAX;
	  /* if we are in the release phase */
	  else if (this->current_index < (this->attack + this->release))
	    this->buffer[i] = release(this) * buffer[i] / SAMPLE_MAX;
	  else
	    this->buffer[i] = 0;

	  this->current_index++;
	}
    }

    else {
      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 we are in the attack phase */
	  if(this->current_index < this->attack)
	    this->buffer[i] = attack(this);
	  /* if we are in the release phase */
	  else if (this->current_index < (this->attack + this->release))
	    this->buffer[i] = release(this);
	  else
	    this->buffer[i] = 0;

	  this->current_index++;
	}
    }
  }

  #if 0
  if (this == filt_e)
    for (i=0; i < TBASS_BUFF_SIZE; i++)
      printf("%d\n", this->buffer[i]);
  #endif

  return this->buffer;
}

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

  if (this->sp)
    return &(this->sp);
  else
    return no_kids;
}

static char **get_header(env *this)
{
  static char *header[] = {
    "int $n_attack = ((env *)$t)->attack;",
    "int $n_release = ((env *)$t)->release;",
    NULL
  };
  return header;
}

static char **get_code(env *this)
{
  static char *code_nosp[] = {
    "if (((env *)$t)->current_index < $n_attack)",
    "  $o = (SAMPLE_MAX - ((env *)$t)->start)  * ((env *)$t)->current_index / $n_attack + ((env *)$t)->start;",
    "else if (((env *)$t)->current_index < ($n_attack + $n_release))",
    "  $o = -SAMPLE_MAX * (((env *)$t)->current_index - $n_attack) / $n_release + SAMPLE_MAX;",
    "else",
    "  $o = 0;",
    "((env *)$t)->current_index++;",
    NULL
  };

  static char *code_sp[] = {
    "if (((env *)$t)->current_index < $n_attack)",
    "  $o = ((SAMPLE_MAX - ((env *)$t)->start)  * ((env *)$t)->current_index /",
    "       $n_attack + ((env *)$t)->start) * $i0 / SAMPLE_MAX;",
    "else if (((env *)$t)->current_index < ($n_attack + $n_release))",
    "  $o = (-SAMPLE_MAX * (((env *)$t)->current_index - $n_attack) / $n_release + SAMPLE_MAX) *",
    "       $i0 / SAMPLE_MAX;",
    "else",
    "  $o = 0;",
    "((env *)$t)->current_index++;",
    NULL
  };

  if (this->sp)
    return code_sp;
  else
    return code_nosp;
}

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

env *env_new(int attack,int release,sample_producer *sp)
{
  int i;

  env *out           = (env *)malloc(sizeof(env));
  out->get_buffer    = env_get_buffer;
  out->next_buffer   = env_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->buffer	     = (sample *)malloc(sizeof(sample) * TBASS_BUFF_SIZE);
  out->schedule      = sp_schedule_event;
  out->trigger       = (void (*)(sample_producer *))env_trigger;
  out->events = (event **)malloc(sizeof(event *) 
				 * TBASS_BUFF_SIZE);
  for(i = 0; i < TBASS_BUFF_SIZE; i++)
    out->events[i] = NULL; 

  out->sp            = sp;
  out->unused	     = NULL;
  out->current_index = release + attack; /* this is x in the function */
  out->attack        = attack;
  out->release       = release;
  out->start	     = 0;
  return out;
}

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