
/* $Id: saol_co_imp.c,v 1.7 1997/11/20 18:35:43 eds Exp $ */
/* $Log: saol_co_imp.c,v $
 * Revision 1.7  1997/11/20  18:35:43  eds
 * midicc() core opcode for current SASBF implementation.
 * Bug fixes in oscillators.
 *
 * Revision 1.13  1997/11/15 00:43:12  luked
 * This is the result of the manual merge from the vendor branch for the
 * release tagged Fribourg_after_integration. I (brian) have probably lost
 * some history, but oh well.
 *
 * This contains the integration work by Eric Scheirer (MIT).
 *
 * Revision 1.6  1997/11/11  22:25:39  eds
 * Fixed bugs in fir(), etc.
 *
 * Revision 1.5  1997/11/10  22:59:51  eds
 * Added fir(), iir(), iirt().
 *
 * Revision 1.4  1997/11/05  20:45:40  eds
 * Added kdump(), adump().
 *
 * Revision 1.3  1997/10/01  15:32:39  eds
 * Added firt() opcode.
 * */
/*********************************************************************
  
  This software module was originally developed by
  
  Eric D. Scheirer (MIT Media Laboratory)
  
  in the course of development of the MPEG-2 NBC/MPEG-4 Audio standard
  ISO/IEC 13818-7, 14496-1,2 and 3. This software module is an
  implementation of a part of one or more MPEG-2 NBC/MPEG-4 Audio tools
  as specified by the MPEG-2 NBC/MPEG-4 Audio standard.  ISO/IEC gives
  users of the MPEG-2 NBC/MPEG-4 Audio standards free license to this
  software module or modifications thereof for use in hardware or
  software products claiming conformance to the MPEG-2 NBC/ MPEG-4 Audio
  standards. Those intending to use this software module in hardware or
  software products are advised that this use may infringe existing
  patents. The original developer of this software module and his/her
  company, the subsequent editors and their companies, and ISO/IEC have
  no liability for use of this software module or modifications thereof
  in an implementation.
  
  This software module is hereby released into the public domain.
  
  ***********************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "saol_co_imp.h"
#include "saol_interp.h"
#include "saol_sched.h"
#include "y.tab.h"

#ifndef MAX
#define MAX(x,y) (x > y ? x : y)
#endif 

#ifdef _WIN32
double drand48() {
  return((double)rand()/RAND_MAX);
}

#endif

extern double get_table_value(table_storage *t,int idx);
extern int get_table_size(table_storage *t);
extern void set_table_value(table_storage *t, long i, double val);

/* math functions */

table_storage *co_param_table(actparam *pf,int which) {
  /* get the table which is parameter 'which' of the given context */

  return(pf[which-1].t);
}

double co_param(actparam *pf, int which) {
  /* get the value which is parameter 'which' of the given context */

  return(pf[which-1].val);
}

double co_int(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double x = co_param(pf,1);

  return((double)(int)(x));
}

double co_frac(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double x = co_param(pf,1);

  return(x - (int)(x));
}

double co_dbamp(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double x = co_param(pf,1);

  return(90 + log(x)/log(x) * 6);
}

double co_ampdb(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double x = co_param(pf,1);

  return(exp((x-90)/6 * log(2)));
}

double co_abs(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double x = co_param(pf,1);

  return(fabs(x));
}

double co_exp(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double x = co_param(pf,1);

  return(exp(x));
}

double co_log(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double x = co_param(pf,1);

  return(log(x));
}

double co_sqrt(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double x = co_param(pf,1);

  return(sqrt(x));
}

double co_sin(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double x = co_param(pf,1);

  return(sin(x));
}

double co_cos(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double x = co_param(pf,1);

  return(cos(x));
}

double co_atan(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double x = co_param(pf,1);

  return(atan(x));
}

double co_pow(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double x = co_param(pf,1);
  double y = co_param(pf,2);

  return(pow(x,y));
}

double co_log10(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double x = co_param(pf,1);

  return(log10(x));
}

double co_asin(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double x = co_param(pf,1);

  return(asin(x));
}

double co_acos(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double x = co_param(pf,1);

  return(acos(x));
}

double co_floor(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double x = co_param(pf,1);

  return(floor(x));
}

double co_ceil(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double x = co_param(pf,1);

  return(ceil(x));
}

double co_max(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  int i;
  double max;

  max = pf[0].val;
  for (i=1;i!=pf_ct;i++)
    if (pf[i].val > max) max = pf[i].val;
  
  return(max);

}

double co_min(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  int i;
  double min;

  min = pf[0].val;
  for (i=1;i!=pf_ct;i++)
    if (pf[i].val < min) min = pf[i].val;
  
  return(min);
}


/* pitch converters */
double co_octpch(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double pch = co_param(pf,1);
  int i = (int)floor(pch);
  double frac = pch-i;

  return(i + frac*100/12);
}

double co_pchoct(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double oct = co_param(pf,1);
  int i = (int)floor(oct);
  double frac = oct-i;

  return(i + frac * 12/100);
}

double co_cpspch(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double pch = co_param(pf,1);
  int i = (int)floor(pch);
  double frac = pch-i;
  double oct = (i + frac * 100/12);

  return(440 * pow(2,(oct-8.75)));  
}

double co_pchcps(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double cps = co_param(pf,1);
  double oct = log(cps/440)/log(2)+8.75;
  int i = (int)floor(oct);
  double frac = oct - i;
  return(i+frac * 12 / 100);
}

double co_cpsoct(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double oct = co_param(pf,1);

  return(440 * pow(2,(oct - 8.75)));
}

double co_octcps(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double cps = co_param(pf,1);

  return(log(cps/440)/log(2)+8.75);

  
}

double co_pchmidi(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double midi = co_param(pf,1);
  int i = (int)floor((midi-60)/12) + 8;
  double frac = (midi-((i-8)*12+60))/12;

  return(i + frac*12/100);
}

double co_midipch(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double pch = co_param(pf,1);
  int i = (int)floor(pch);
  double frac = pch-i;
  double oct = (i+frac * 100/12);

  return(60 + 12 * (i-8) + floor(frac*100 + 0.5));
}

double co_octmidi(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double midi = co_param(pf,1);
  int i = (int)floor((midi-60)/12) + 8;
  double frac = (midi-((i-8)*12+60))/12;

  return(i + frac);
}

double co_midioct(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double oct = co_param(pf,1);
  int i = (int)floor(oct);
  double frac = oct-i;

  return(60 + 12 * (i-8) + floor(frac*12 + 0.5));
}

double co_cpsmidi(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double x = co_param(pf,1);

  return(440 * pow(1.0594631,(x-69.0)));
}

double co_midicps(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double cps = co_param(pf,1);

  return((double)floor(log(cps/440)/log(2) * 12 + 69));
}


/* table operations */
double co_ftlen(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  table_storage *t = co_param_table(pf,1);
  
  return(get_table_size(t));
}

double co_ftloop(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  table_storage *t = co_param_table(pf,1);
  
  return((double)t->loop);
}

double co_ftloopend(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  table_storage *t = co_param_table(pf,1);

  return((double)t->loopend);
}

double co_ftsr(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  table_storage *t = co_param_table(pf,1);

  return((double)t->srate);
}

double co_ftbasecps(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  table_storage *t = co_param_table(pf,1);

  return(t->base);
}

double co_tableread(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  table_storage *t = co_param_table(pf,1);
  double idx = co_param(pf,2);
  long fl = (long)floor(idx);
  long cl = (long)ceil(idx);
  double x1 = get_table_value(t,fl);
  double x2 = get_table_value(t,cl);

  if (fl == cl) return(x1);
  else return((idx-fl) * (x2-x1)/(cl-fl) + x1); /* linear interpolation */
}

double co_tablewrite(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  table_storage *t = co_param_table(pf,1);
  double idx = co_param(pf,2);
  double val = co_param(pf,3);
  long i;

  i = (long)(idx + 0.5);	/* round */
  set_table_value(t,i,val);

  return 0;
   
}



double co_oscil(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  /* nb not currently anti-aliased interpolation/decimation */
  oscil_storage *local;
  table_storage *t = co_param_table(pf,1);
  double freq = co_param(pf,2);
  double loops;
  double idx, x1, x2;
  long fl,cl;
  
  if (pf_ct < 3) 
    loops = -1;
  else loops = co_param(pf,3);

  if (!(local = (oscil_storage *)op->local)) {
    PROT_MAL_CO(oscil_storage);
    local = (oscil_storage *)op->local;
    local->ph = 0; /* start at 0 phase */
    local->loops = 0;
  }

  if (local->loops == loops)
    return 0;
  
  if (rate == ASIG) {
		
    idx = local->ph * get_table_size(t);
    fl = (long)floor(idx);
    cl = (long)ceil(idx);
    x1 = get_table_value(t,fl);
		
    if (cl < get_table_size(t))
      x2 = get_table_value(t,cl);
    else
      x2 = get_table_value(t,0);
		
    local->ph += freq/sa->all->g->srate;
    if (local->ph >= 1) {
      local->loops++;
      local->ph = local->ph - (int)local->ph;
    }
    if (local->ph < 0) {
      local->loops++;
      local->ph = local->ph + (int)local->ph + 1;
    }

    if (cl == fl)
      return(x1);
    else
      return((idx-fl) * (x2-x1)/(cl-fl) + x1); /* linear interpolation */
  }
  else return 0;
} 

double co_koscil(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  /* nb not currently anti-aliased interpolation/decimation */
  koscil_storage *local;
  table_storage *t = co_param_table(pf,1);
  double freq = co_param(pf,2);
  double loops;
  double idx, x1, x2;
  long fl,cl;
  
  if (pf_ct < 3) 
    loops = -1;
  else loops = co_param(pf,3);

  if (!(local = (koscil_storage *)op->local)) {
    PROT_MAL_CO(koscil_storage);
    local = (koscil_storage *)op->local;
    local->ph = 0; /* start at 0 phase */
    local->loops = 0;
  }

  if (local->loops == loops)
    return 0;
  
		
  idx = local->ph * get_table_size(t);
  fl = (long)floor(idx);
  cl = (long)ceil(idx);
  x1 = get_table_value(t,fl);
  
  if (cl < get_table_size(t))
    x2 = get_table_value(t,cl);
  else
    x2 = get_table_value(t,0);

  if (rate == KSIG) {
    local->ph += freq/sa->all->g->krate;
    if (local->ph >= 1) {
      local->loops++;
      local->ph = local->ph - (int)local->ph;
    }
  }
  
  if (cl == fl)
    return(x1);
  else
    return((idx-fl) * (x2-x1)/(cl-fl) + x1); /* linear interpolation */
} 

double co_loscil(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  loscil_storage *local;
  table_storage *t = co_param_table(pf,1);
  double freq = co_param(pf,2);
  double basecps = co_param(pf,3);
  double loopstart = co_param(pf,4);
  double loopend = co_param(pf,5);
  double x1, x2;
  long fl,cl;

  if (!loopend || pf_ct < 5) loopend = t->loopend;
  if (!loopstart || pf_ct < 4) loopstart = t->loop;
  if (!basecps || pf_ct < 3) basecps = t->base;
  if (!basecps)
    runtime("Must provide base CPS for table in loscil.");

  if (!(local = (loscil_storage *)op->local)) {
    PROT_MAL_CO(loscil_storage);
    local = (loscil_storage *)op->local;
    local->idx = 0;
  }

    
  if ((loopend && local->idx >= loopend) ||
      local->idx >= get_table_size(t))
    local->idx = loopstart;

  fl = (long)floor(local->idx);
  cl = (long)ceil(local->idx);
  x1 = get_table_value(t,fl);
  
  if (cl < get_table_size(t))
    x2 = get_table_value(t,cl);
  else
    x2 = get_table_value(t,cl-get_table_size(t));

  local->idx += t->srate/sa->all->g->srate * freq / basecps;

  if (cl == fl)
    return(x1);
  else
    return((local->idx-fl) * (x2-x1)/(cl-fl) + x1); /* linear interpolation */

}

double co_doscil(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  /* nb not currently anti-aliased interpolation/decimation */
  doscil_storage *local;
  table_storage *t = co_param_table(pf,1);
  double x1, x2;
  long fl,cl;
  
  if (!(local = (doscil_storage *)op->local)) {
    PROT_MAL_CO(doscil_storage);
    local = (doscil_storage *)op->local;
    local->idx = 0;
  }

  if (local->idx + t->srate/sa->all->g->srate >= get_table_size(t))
    return 0;
  

  fl = (long)floor(local->idx);
  cl = (long)ceil(local->idx);
  x1 = get_table_value(t,fl);
  
  if (cl < get_table_size(t))
    x2 = get_table_value(t,cl);
  else
    x2 = get_table_value(t,0);

  if (rate == ASIG)
    local->idx += t->srate/sa->all->g->srate;

  if (cl == fl)
    return(x1);
  else
    return((local->idx-fl) * (x2-x1)/(cl-fl) + x1); /* linear interpolation */
}


/* signal generators */

double co_kline(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  kline_storage *local;
  int ct;

  double x1= co_param(pf,1);
  double dur1 = co_param(pf,2);
  double x2 = co_param(pf,3);
  if (!(local = (kline_storage *)op->local)) {
    PROT_MAL(op->local,kline_storage,co_kline);
    local = (kline_storage *)op->local;
    local->left = x1;
    local->right = x2;
    local->dur = dur1;
    local->varargs_pos = 3;
    local->durtime = 0;
    local->done = 0;
  }

  if (local->done) return local->right;
  
  if (rate == KSIG) {
    while (local->durtime >= local->dur) { /* need 'while' for 0 durations */
      if (local->varargs_pos < pf_ct) { /* varargs */
	local->left = local->right;
	local->dur = pf[local->varargs_pos].val;
	if (++local->varargs_pos == pf_ct)
	  runtime("Missing final value in call to 'kline'.");
	local->right = pf[local->varargs_pos].val;
	local->varargs_pos++;
	local->durtime = 0;
      }
      else {
	local->done = 1;
	return 0;
      }
    }

    local->durtime += 1/(double)sa->all->g->krate;
  }
  return((local->right-local->left) * (local->durtime)/local->dur + local->left);
}

double co_aline(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  aline_storage *local;
  int ct;

  double x1= co_param(pf,1);
  double dur1 = co_param(pf,2);
  double x2 = co_param(pf,3);

  if (!(local = (aline_storage *)op->local)) {
    PROT_MAL(op->local,aline_storage,co_aline);
    local = (aline_storage *)op->local;
    local->left = x1;
    local->right = x2;
    local->dur = dur1;
    local->varargs_pos = 3;
    local->durtime = 0;
    local->done = 0;
  }

  if (local->done) return 0;
  
  if (rate == ASIG) {
    while (local->durtime >= local->dur) { /* need 'while' for 0 durations */
      if (local->varargs_pos < pf_ct ) {
	local->left = local->right;
	local->dur = pf[local->varargs_pos].val;
	if (++local->varargs_pos == pf_ct)
	  runtime("Missing final value in call to 'aline'.");
	local->right = pf[local->varargs_pos].val;
	local->varargs_pos++;
	local->durtime = 0;
      }
      else {
	local->done = 1;
	return 0;
      }
    }
		
    local->durtime += 1/(double)sa->all->g->srate;
  }
  return((local->right-local->left) * (local->durtime)/local->dur + local->left);
}

double co_kexpon(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  kexpon_storage *local;
  int ct;
  double x1= co_param(pf,1);
  double dur1 = co_param(pf,2);
  double x2 = co_param(pf,3);

  if (!(local = (kexpon_storage *)op->local)) {
    PROT_MAL(op->local,kexpon_storage,co_kexpon);
    local = (kexpon_storage *)op->local;
    local->left = x1;
    local->right = x2;
    local->dur = dur1;
    local->varargs_pos = 3;
    local->durtime = 0;
    local->done = 0;
  }

  if (local->done) return local->right;
  
  while (local->durtime >= local->dur) { /* need 'while' for 0 durations */
    if (local->varargs_pos < pf_ct) {
      local->left = local->right;
      local->dur = pf[local->varargs_pos].val;
      if (++local->varargs_pos == pf_ct)
	runtime("Missing final value in call to 'kexpon'.");
      local->right = pf[local->varargs_pos].val;
      local->varargs_pos++;
      local->durtime = 0;
    }
    else {
      local->done = 1;
      return 0;
    }
  }

  if (rate == KSIG) local->durtime += 1/(double)sa->all->g->krate;
  return(exp((log(local->right)-log(local->left)) * (local->durtime)/local->dur + log(local->left)));
}

double co_aexpon(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  aexpon_storage *local;
  int ct;

  double x1= co_param(pf,1);
  double dur1 = co_param(pf,2);
  double x2 = co_param(pf,3);

  if (!(local = (aexpon_storage *)op->local)) {
    PROT_MAL(op->local,aexpon_storage,co_aexpon);
    local = (aexpon_storage *)op->local;
    local->left = x1;
    local->right = x2;
    local->dur = dur1;
    local->varargs_pos = 0;
    local->durtime = 0;
    local->done = 0;
  }

  if (local->done) return 0;
  
  while (local->durtime >= local->dur) { /* need 'while' for 0 durations */
    if (local->varargs_pos < pf_ct) {
      local->left = local->right;
      local->dur = pf[local->varargs_pos].val;
      if (++local->varargs_pos == pf_ct)
	runtime("Missing final value in call to 'aexpon'.");
      local->right = pf[local->varargs_pos].val;
      local->varargs_pos++;
      local->durtime = 0;
    }
    else {
      local->done = 1;
      return 0;
    }
  }

  if (rate == ASIG) local->durtime += 1/(double)sa->all->g->srate;
  return(exp((log(local->right)-log(local->left)) * (local->durtime)/local->dur + log(local->left)));
}

double co_kphasor(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  kphasor_storage *local;
  double freq = co_param(pf,1);
  double old;

  if (!(local = (kphasor_storage *)op->local)) {
    PROT_MAL_CO(kphasor_storage);
    local = (kphasor_storage *)op->local;
    local->ph = 0; /* start at 0 phase */
  }

  old = local->ph;
  if (rate == KSIG) local->ph += freq/sa->all->g->srate;
  if (local->ph >= 1) {
    local->ph = local->ph - (int)local->ph;
  }

  return(old);
}

double co_aphasor(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  aphasor_storage *local;
  double freq = co_param(pf,1),old;

  if (!(local = (aphasor_storage *)op->local)) {
    PROT_MAL_CO(aphasor_storage);
    local = (aphasor_storage *)op->local;
    local->ph = 0; /* start at 0 phase */
  }

  old = local->ph;
  
  if (rate == ASIG) local->ph += freq/sa->all->g->srate;
  if (local->ph >= 1) {
    local->ph = local->ph - (int)local->ph;
  }

  return(old);
}

double co_pluck(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  printf("co_pluck: not done yet.\n");
  return 0;
}

double co_buzz(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double cps = co_param(pf,1);
  double nharm = co_param(pf,2);
  double lowharm = co_param(pf,3);
  double rolloff = co_param(pf,4);
  buzz_storage *local;
  int i;
  double out=0,att;
  if (!nharm)			/* use nyquist */
    nharm = floor(sa->all->g->srate/2 / cps) - (lowharm-1);
  
  if (!(local = (buzz_storage *)op->local)) {
    PROT_MAL(op->local,buzz_storage,co_buzz);
    local = (buzz_storage *)op->local;
    local->ph = 0; /* start at 0 phase */
  }

  if (rate == ASIG) {
    out = 0;
    att = 1;
    for (i=(int)(lowharm+0.5);i<=nharm+lowharm-1;i++) {
      out += cos(2 * PI * local->ph * i) * att/nharm;
      att *= rolloff;
    }

    local->ph += cps/sa->all->g->srate;
    if (local->ph > 1) local->ph -= (int)local->ph;
		
  }

  return(out);
}

double co_fof(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  printf("co_fof: not done yet.\n");
  return 0;
}


/* noise generators */

double co_irand(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double amp = co_param(pf,1);

  return (drand48() - 0.5) * 2 * amp;
}

double co_krand(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double amp = co_param(pf,1);

  return (drand48() - 0.5) * 2 * amp;
}

double co_arand(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double amp = co_param(pf,1);

  return (drand48() - 0.5) * 2 * amp;
}

double co_ilinrand(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double min = co_param(pf,1);
  double max = co_param(pf,2);

  double x,y;
  x = drand48();
  y = drand48();

  return (MAX(x,y)) * (max-min) + min;
  
}

double co_klinrand(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double min = co_param(pf,1);
  double max = co_param(pf,2);

  double x,y;
  x = drand48();
  y = drand48();

  return (MAX(x,y)) * (max-min) + min;
}

double co_alinrand(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double min = co_param(pf,1);
  double max = co_param(pf,2);

  double x,y;
  x = drand48();
  y = drand48();

  return (MAX(x,y)) * (max-min) + min;
}

double co_iexprand(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double mean = co_param(pf,1);

  double x;
  x = drand48();

  return(-log(x)*mean);
}

double co_kexprand(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double mean = co_param(pf,1);

  double x;
  x = drand48();

  return(-log(x)*mean);
}

double co_aexprand(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double mean = co_param(pf,1);

  double x;
  x = drand48();

  return(-log(x)*mean);
}

double co_kpoissonrand(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double mean = co_param(pf,1);
  kpoissonrand_storage *local;

  if (!(local = (kpoissonrand_storage *)op->local)) {
    PROT_MAL(op->local,kpoissonrand_storage,co_kpoissonrand);
    local = (kpoissonrand_storage *)op->local;
    local->wait = (int)floor(-log(drand48()) * mean*sa->all->g->srate/sa->all->g->krate + 1);
  }

  if (rate == KSIG) local->wait--;
  if (!local->wait) {
    local->wait = (int)floor(-log(drand48()) * mean*sa->all->g->srate/sa->all->g->krate + 1);
    return 1;
  }
  return 0;
}

double co_apoissonrand(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double mean = co_param(pf,1);
  apoissonrand_storage *local;

  if (!(local = (apoissonrand_storage *)op->local)) {
    PROT_MAL(op->local,apoissonrand_storage,co_apoissonrand);
    local = (apoissonrand_storage *)op->local;
    local->wait = (int)floor(-log(drand48()) * mean*sa->all->g->srate + 1);
  }

  if (rate == ASIG) local->wait--;
  if (!local->wait) {
    local->wait = (int)floor(-log(drand48()) * mean*sa->all->g->srate + 1);
    return 1;
  }
  return 0;
}

double co_igaussrand(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double mean = co_param(pf,1);
  double var = co_param(pf,2);
  double x,y;

  /* Box-Muller method: see Num. Rec. pg 289 */

  x = drand48();
  y = drand48();

  return(sqrt(-2 * log(x)) * cos(2 * PI * y) * sqrt(var) + mean);
  
}

double co_kgaussrand(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double mean = co_param(pf,1);
  double var = co_param(pf,2);
  double x,y;

  /* Box-Muller method: see Num. Rec. pg 289 */

  x = drand48();
  y = drand48();

  return(sqrt(-2 * log(x)) * cos(2 * PI * y) * sqrt(var) + mean);
}

double co_agaussrand(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double mean = co_param(pf,1);
  double var = co_param(pf,2);
  double x,y;

  /* Box-Muller method: see Num. Rec. pg 289 */

  x = drand48();
  y = drand48();

  return(sqrt(-2 * log(x)) * cos(2 * PI * y) * sqrt(var) + mean);
}


/* filters */

double co_port(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  printf("co_port: not done yet.\n");
  return 0;
}

double co_hipass(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  real input = co_param(pf,1);
  real cut = co_param(pf,2);
  hipass_storage *local;
  real c,out;

  if (!(local = (hipass_storage *)op->local)) {
    PROT_MAL(op->local,hipass_storage,co_hipass);
    local = (hipass_storage *)op->local;

    local->oldcut = -1;
    local->a1 = local->a2 = local->a0 = 0;
    local->b1 = local->b2 = local->b0 = 0;
    local->d1 = 0;
    local->d2 = 0;
  }

  if (local->oldcut != cut) {	/* move filter */
    local->oldcut = cut;
    c = tan(PI * cut / sa->all->g->srate);
    local->b0 = 1 / (1 + sqrt(2.0) * c + c * c);
    local->b1 = -2 * local->b0;
    local->b2 = local->b0;
    local->a0 = 1;
    local->a1 = 2 * (c * c - 1) * local->b0;
    local->a2 = (1 - sqrt(2.0) * c + c * c) * local->b0;
  }

  out = input * local->b0 + local->d2;

  if (rate == ASIG) {
    local->d2 = local->d1 - local->a1 * out + local->b1 * input;
    local->d1 = -local->a2 * out + local->b2 * input;
  }
  
  return(out);
}

double co_lopass(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  real input = co_param(pf,1);
  real cut = co_param(pf,2);
  lopass_storage *local;
  real c,out;

  if (!(local = (lopass_storage *)op->local)) {
    PROT_MAL(op->local,lopass_storage,co_lopass);
    local = (lopass_storage *)op->local;

    local->oldcut = -1;
    local->a1 = local->a2 = local->a0 = 0;
    local->b1 = local->b2 = local->b0 = 0;
    local->d1 = 0;
    local->d2 = 0;
  }

  if (local->oldcut != cut) {	/* move filter */
    local->oldcut = cut;
    c = 1/tan(PI * cut / sa->all->g->srate);
    local->b0 = 1 / (1 + sqrt(2.0) * c + c * c);
    local->b1 = 2 * local->b0;
    local->b2 = local->b0;
    local->a0 = 1;
    local->a1 = 2 * (1 - c * c) * local->b0;
    local->a2 = (1 - sqrt(2.0) * c + c * c) * local->b0;
  }

  out = input * local->b0 + local->d2;

  if (rate == ASIG) {
    local->d2 = local->d1 - local->a1 * out + local->b1 * input;
    local->d1 = -local->a2 * out + local->b2 * input; 
  }

  return(out);
}


double co_bandpass(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  real input = co_param(pf,1);
  real cf = co_param(pf,2);
  real bw = co_param(pf,3);
  bandpass_storage *local;
  real c,d,out;

  if (!(local = (bandpass_storage *)op->local)) {
    PROT_MAL(op->local,bandpass_storage,co_bandpass);
    local = (bandpass_storage *)op->local;

    local->oldcf = -1;
    local->oldbw = -1;
    local->a1 = local->a2 = local->a0 = 0;
    local->b1 = local->b2 = local->b0 = 0;
    local->d1 = 0;
    local->d2 = 0;
  }

  if (local->oldcf != cf || local->oldbw != bw) { /* move filter */
    
    local->oldcf = cf;
    local->oldbw = bw;
    c = 1/tan(PI * bw / sa->all->g->srate);
    d = 2 * cos(2*PI*cf/sa->all->g->srate);
    local->b0 = 1 / (1 + c);
    local->b1 = 0;
    local->b2 = -local->b0;
    local->a0 = 1;
    local->a1 = -c * d * local->b0;
    local->a2 = (c - 1) * local->b0;
  }
  out = input * local->b0 + local->d2;

  if (rate == ASIG) {
    local->d2 = local->d1 - local->a1 * out + local->b1 * input;
    local->d1 = -local->a2 * out + local->b2 * input;
  }

  return(out);
}

double co_bandstop(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  real input = co_param(pf,1);
  real cf = co_param(pf,2);
  real bw = co_param(pf,3);
  bandstop_storage *local;
  real c,d,out;

  if (!(local = (bandstop_storage *)op->local)) {
    PROT_MAL(op->local,bandstop_storage,co_bandstop);
    local = (bandstop_storage *)op->local;

    local->oldcf = -1;
    local->oldbw = -1;
    local->a1 = local->a2 = local->a0 = 0;
    local->b1 = local->b2 = local->b0 = 0;
    local->d1 = 0;
    local->d2 = 0;
  }

  if (local->oldcf != cf || local->oldbw != bw) { /* move filter */
    local->oldcf = cf;
    local->oldbw = bw;
    c = tan(PI * bw / sa->all->g->srate);
    d = 2 * cos(2*PI*cf/sa->all->g->srate);
    local->b0 = 1 / (1 + c);
    local->b1 = -d * local->b0;
    local->b2 = local->b0;
    local->a0 = 1;
    local->a1 = local->b1;
    local->a2 = (1 - c) * local->b0;
  }
  out = input * local->b0 + local->d2;

  if (rate == ASIG) {
    local->d2 = local->d1 - local->a1 * out + local->b1 * input;
    local->d1 = -local->a2 * out + local->b2 * input;
  }

  
  return(out);
}

double co_biquad(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  real input = co_param(pf,1);
  real b0 = co_param(pf,2);
  real b1 = co_param(pf,3);
  real b2 = co_param(pf,4);
  real a1 = co_param(pf,5);
  real a2 = co_param(pf,6);
  biquad_storage *local;
  real out;
  
  if (!(local = (biquad_storage *)op->local)) {
    PROT_MAL(op->local,biquad_storage,co_biquad);
    local = (biquad_storage *)op->local;
    local->d1 = 0;
    local->d2 = 0;
  }
  
  out = input * b0 + local->d2;

  if (rate == ASIG) {
    local->d2 = local->d1 - a1 * out + b1 * input;
    local->d1 = -a2 * out + b2 * input;
  }
  return(out);
}

double co_fir(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  real input = co_param(pf,1);
  fir_storage *local;
  double out;
  int i;

  int ilen = pf_ct-1;
  
  if (!(local = (fir_storage *)op->local)) {
    PROT_MAL(op->local,fir_storage,co_firt);
    local = (fir_storage *)op->local;
    local->dline = NULL;
    local->dline = (double *)malloc(ilen * sizeof(double));
    for (i=0;i!=ilen;i++)
      local->dline[i] = 0;
    local->pt = 0;
    op->dyn = local->dline; /* so we can free it later */
  }
  
  if (rate == ASIG) local->dline[local->pt] = input;

  out = 0;
  for (i=0;i!=ilen;i++)
    out += local->dline[(local->pt + i) % ilen] * pf[i+1].val;

  if (++local->pt == ilen) local->pt = 0;
  return out;
}

double co_firt(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  real input = co_param(pf,1);
  table_storage *t = co_param_table(pf,2);
  real order = co_param(pf,3);
  firt_storage *local;
  double out;
  int i;

  int ilen = (int)(floor(order));
  if (!(local = (firt_storage *)op->local)) {
    PROT_MAL(op->local,firt_storage,co_firt);
    local = (firt_storage *)op->local;
    local->dline = NULL;
    local->dline = (double *)malloc(ilen * sizeof(double));
    for (i=0;i!=ilen;i++)
      local->dline[i] = 0;
    local->pt = 0;
    op->dyn = local->dline; /* so we can free it later */
  }
  
  if (rate == ASIG) local->dline[local->pt] = input;

  out = 0;
  for (i=0;i!=ilen;i++)
    out += local->dline[(local->pt + i) % ilen] * get_table_value(t,i);

  if (++local->pt == ilen) local->pt = 0;

  return out;
}

double co_iir(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  real input = co_param(pf,1);
  firt_storage *local;
  double out,fb;
  int i;

  int ilen = (pf_ct+1)/2;
  
  if (!(local = (firt_storage *)op->local)) {
    PROT_MAL(op->local,firt_storage,co_firt);
    local = (firt_storage *)op->local;
    local->dline = NULL;
    local->dline = (double *)malloc(ilen * sizeof(double));
    for (i=0;i!=ilen;i++)
      local->dline[i] = 0;
    local->pt = 0;
    op->dyn = local->dline; /* so we can free it later */
  }

  fb = 0;
  for (i=2;i < pf_ct;i+=2)
    fb += local->dline[(local->pt + i) % ilen] * pf[i].val;

  if (rate == ASIG) local->dline[local->pt] = input+fb;
 
  out = 0;
  for (i=1;i<pf_ct;i+=2)
    out += local->dline[(local->pt + i) % ilen] * pf[i].val;

  if (++local->pt == ilen) local->pt = 0;

  return out;
}

double co_iirt(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  real input = co_param(pf,1);
  table_storage *a = co_param_table(pf,2);
  table_storage *b = co_param_table(pf,3);
  real order = co_param(pf,4);
  firt_storage *local;
  double out,fb;
  int i;

  int ilen = (int)(floor(order));
  if (!(local = (firt_storage *)op->local)) {
    PROT_MAL(op->local,firt_storage,co_firt);
    local = (firt_storage *)op->local;
    local->dline = NULL;
    local->dline = (double *)malloc(ilen * sizeof(double));
    for (i=0;i!=ilen;i++)
      local->dline[i] = 0;
    local->pt = 0;
    op->dyn = local->dline; /* so we can free it later */
  }

  fb = 0;
  for (i=1;i!=ilen;i++)
    fb += local->dline[(local->pt + i) % ilen] * get_table_value(a,i);

  if (rate == ASIG) local->dline[local->pt] = input+fb;


  out = 0;
  for (i=0;i!=ilen;i++)
    out += local->dline[(local->pt-1 + i) % ilen] * get_table_value(b,i);

  if (++local->pt == ilen) local->pt = 0;
  return out;
}

/* spectral analysis */
double co_fft(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  real input = co_param(pf,1);
  table_storage *t = co_param_table(pf,2);
  int length,shift,start;
  int fftsize;
  table_storage *win;
  fft_storage *local;
  complex inb[8192];
  complex out[8192];
  int totalsize, i,pt;
  real *t2;

  if (pf_ct < 3)
    fftsize = 0;
  else 
    fftsize = (int)floor(co_param(pf,3) + 0.5);
  
  if (pf_ct < 4)
    length = 0;
  else
    length = (int)floor(co_param(pf,4) + 0.5);

  if (pf_ct < 5)
    shift = 0;
  else
    shift = (int)floor(co_param(pf,5) + 0.5);
  
  if (pf_ct < 6)
    win = NULL;
  else
    win = co_param_table(pf,6);

  if (shift < sa->ksmps)
    shift = sa->ksmps;

  if (length == 0)
    length = sa->ksmps;
  
  if (fftsize < length) fftsize = length;
  
  if (!(local = (fft_storage *)op->local)) {
    PROT_MAL(op->local,fft_storage,co_fft);
    local = (fft_storage *)op->local;

    /* we need to dynamically allocate two buffers, but
       there's only one handle for freeing them later,
       so allocate the memory in one chunk, and split it
       up. */
    
    totalsize = fftsize * sizeof(complex) + /* for basis buffer */
      2 * length * sizeof(real); /* for input samples */
    if (!(op->dyn = (void *)malloc(totalsize)))
      runtime("Couldn't allocate memory for fft.");
    if (fftsize > 8192)
      runtime("Maximum fft length is 8192.");
    local->basis = op->dyn;
    /* buf starts after fftsize complex values */
    local->buf = (real *)((complex *) op->dyn + fftsize);
    local->pt = 0;
    local->p2 = (IsPowerOfTwo(fftsize));
    local->basis = AssignBasis(local->basis,fftsize);
    local->ready = 0;
  }

  if (rate == ASIG) {
    /* buffer samples */
    local->buf[local->pt++] = input;
    local->framect++;
    
    if (local->pt == 2*length) {
      local->pt = 0;
    }

    if (local->framect == shift) {
      local->ready = 1;
      local->framept = local->pt;
      local->framect = 0;
    }
    
    return 0;
  }
    

  if (rate == KSIG) {
    if (local->ready) {
      /* time to do fft */

      start = (local->framept - length + 2*length) % (2*length);
      if (win) 
	for (i=0,pt=start;i!=length;i++,pt++) {
	  inb[i].re = local->buf[pt % (length*2)] * get_table_value(win,i);
	  inb[i].im = 0;
	}
      else 
	for (i=0,pt=start;i!=length;i++,pt++) {
	  inb[i].re = local->buf[pt % (length*2)];
	  inb[i].im = 0;
	}
      for (;i!=fftsize;i++)
	inb[i].re = inb[i].im = 0;
					
      if (local->p2) {
	FFT2real(inb,fftsize,1,local->basis); /* in place */
	for (i=0;i!=fftsize/2;i++) {
	  set_table_value(t,i*2,inb[i].re);
	  set_table_value(t,i*2+1,inb[i].im);
	}
	set_table_value(t,i*2,inb[i].re);
      }
      else {
	FFTarb(inb, out, fftsize, local->basis);
	for (i=0;i!=fftsize/2;i++) {
	  set_table_value(t,i*2,out[i].re);
	  set_table_value(t,i*2+1,out[i].im);
	}
	set_table_value(t,i*2,inb[i].re);
      }
					
      local->ready = 0;
      return 1;
    }
  }
	
  return 0;
}



double co_ifft(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  table_storage *t = co_param_table(pf,1);
  int length,shift;
  int fftsize;
  table_storage *win;
  ifft_storage *local;
  complex inb[8192],out[8192],*fftbuf;
  int i,update,totalsize;
  double temp;

  if (pf_ct < 2)
    fftsize = 0;
  else
    fftsize = (int)floor(co_param(pf,2) + 0.5);

  if (pf_ct < 3)
    length = 0;
  else 
    length = (int)floor(co_param(pf,3)+0.5);

  if (pf_ct < 4)
    shift = 0;
  else
    shift = (int)floor(co_param(pf,4)+0.5);

  if (shift < sa->ksmps)
    shift = sa->ksmps;
  
  if (length == 0)
    length = sa->ksmps;
	
  if (fftsize < length)
    fftsize = length;

  if (pf_ct < 5)
    win = NULL;
  else 
    win = co_param_table(pf,5);

  if (!(local = (ifft_storage *)op->local)) {
    PROT_MAL(op->local,ifft_storage,co_ifft);
    local = (ifft_storage *)op->local;
    totalsize = fftsize * sizeof(complex) + /* for basis buffer */
      2*length * sizeof(real);	/* for input samples */
    if (!(op->dyn = (void *)malloc(totalsize)))
      runtime("Couldn't allocate memory for ifft.");
    local->basis = op->dyn;
    local->buf = (real *)((complex *) op->dyn + fftsize);
    for (i=0;i!=2*length;i++)
      local->buf[i] = 0;
    local->pt = 0;
    local->ct = 0;
    local->p2 = (IsPowerOfTwo(fftsize));
    local->basis = AssignBasis(local->basis,fftsize);
  }

  if (rate == KSIG) 
    return 0;

  local->ct++;
  if (local->ct == shift) {	/* time for IFFT */
    local->ct = 0;
    inb[0].re = get_table_value(t,0);
    inb[0].im = 0;
    inb[fftsize/2].re = get_table_value(t,fftsize);
    inb[fftsize/2].im = 0;
    for (i=1;i!=fftsize/2;i++) {
      inb[fftsize-i].re = inb[i].re = get_table_value(t,i*2);
      inb[fftsize-i].im = -(inb[i].im = get_table_value(t,i*2+1));
    }
    if (local->p2) {
      FFT2torl(inb,fftsize,1,1,local->basis);
      fftbuf = inb;
    }
    else {
      FFTarb(inb,out,fftsize,local->basis);
      fftbuf = out;
    }
    for (i=0;i!=length;i++) {
      update = (local->pt + i) % (length*2);
      if (win)
	local->buf[update] += fftbuf[i].re * get_table_value(win,i);
      else
	local->buf[update] += fftbuf[i].re / fftsize;
    }
  }
  temp = local->buf[local->pt];
  local->buf[local->pt] = 0;
  local->pt++;
  if (local->pt == length*2)
    local->pt = 0;
  return(temp);
}


/* gain control */

double co_rms(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  rms_storage *local;
  double input = co_param(pf,1);
  double sum;
  int length = 0,i;

  if (pf_ct > 1)
    length = (int)floor(co_param(pf,2)+0.5);
  if (!length)
    length = (int)floor(sa->all->g->srate/sa->all->g->krate);

  if (!(local = (rms_storage *)op->local)) {
    PROT_MAL(op->local,rms_storage,co_rms);
    local = (rms_storage *)op->local;
    local->buf = (double *)malloc(length * sizeof(double));
    for (i=0;i!=length;i++)
      local->buf[i] = 0;
    local->pt = 0;
    local->last = 0;
    op->dyn = local->buf;	/* so we can free it later */
  }
	

  if (rate == KSIG) {		/* return value */ 
    sum = 0;
    for (i=0;i!=length;i++)
      sum += local->buf[i] * local->buf[i];
    local->last = sqrt(sum/length);
    return(local->last);
  }

  /* else ASIG */
	
  local->buf[local->pt++] = input*input;
  if (local->pt == length)
    local->pt = 0;
  return(local->last);
}

double co_gain(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  /* NB currently implemented incorrectly.  should be block-buffered */
  double input = co_param(pf,1);
  double pwr = co_param(pf,2);
  double sum;
  int length = 0,i;
  gain_storage *local;

  if (pf_ct > 2) 
    length = (int)floor(co_param(pf,3)+0.5);
  if (!length)
    length = (int)floor(sa->all->g->srate / sa->all->g->krate + 0.1);
	
  if (!(local = (gain_storage *)op->local)) {
    PROT_MAL(op->local,gain_storage,co_gain);
    local = (gain_storage *)op->local;
    local->buf = (double *)malloc(length * sizeof(double));
    for (i=0;i!=length;i++)
      local->buf[i] = 0;
    local->pt = 0;
    local->rms = 0;
    op->dyn = local->buf;	/* so we can free it later */
  }
	
  if (rate == ASIG) local->buf[local->pt++] = input*input;
  if (local->pt == length) {
    sum = 0;
    for (i=0;i!=length;i++)
      sum += local->buf[i];
    local->rms = sqrt(sum/length);
    local->pt = 0;
  }
  if (local->rms)
    return(input * pwr/local->rms);
  else 
    return(input);
}

double co_balance(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  /* NB currently implemented incorrectly.  should be block-buffered */
  double input = co_param(pf,1);
  double ref = co_param(pf,2);
  double insum,refsum;
  int length = 0,i;
  balance_storage *local;

  if (pf_ct > 2) 
    length = (int)(floor(co_param(pf,3) + 0.5));
  if (!length)
    length = (int)floor(sa->all->g->srate / sa->all->g->krate + 0.1);
	
  if (!(local = (balance_storage *)op->local)) {
    PROT_MAL(op->local,balance_storage,co_balance);
    local = (balance_storage *)op->local;
    local->inbuf = (double *)malloc(length * sizeof(double) * 2);
    local->refbuf = local->inbuf + length;

    for (i=0;i!=length;i++)
      local->inbuf[i] = local->refbuf[i] = 0;
    local->pt = 0;
    local->inrms = local->refrms = 0;
    op->dyn = local->inbuf; /* so we can free it later */
  }
	
  if (ASIG) {
    local->inbuf[local->pt] = input*input;
    local->refbuf[local->pt++] = ref*ref;
    if (local->pt == length) {
      insum = refsum = 0;
      for (i=0;i!=length;i++) {
	insum += local->inbuf[i];
	refsum += local->refbuf[i];
      }
      local->inrms = sqrt(insum/length);
      local->refrms = sqrt(refsum/length);
      local->pt = 0;
    }
    if (local->inrms)
      return(input * local->refrms/local->inrms);
    else 
      return(input);
  }
  return(0);

}


/* sample conversion */

double co_decimate(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double input = co_param(pf,1);
  decimate_storage *local;

  if (!(local = (decimate_storage *)op->local)) {
    PROT_MAL(op->local,decimate_storage,co_decimate);
    local = (decimate_storage *)op->local;
    local->last = 0;
  }

  if (rate == KSIG)
    return(local->last);
  else {
    local->last = input;
    return(0);
  }
}

double co_upsamp(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double input = co_param(pf,1);
  upsamp_storage *local;

  if (!(local = (upsamp_storage *)op->local)) {
    PROT_MAL(op->local,upsamp_storage,co_upsamp);
    local = (upsamp_storage *)op->local;
    local->last = 0;
    local->ct = 0;
  }

  if (local->last != input) {
    if (local->ct < sa->ksmps) 
      return(local->last + (input-local->last) / sa->ksmps * local->ct++);
    else {
      local->ct = 0;
      local->last = input;
    }
  }
  return(local->last);
}

double co_downsamp(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double input = co_param(pf,1);
  table_storage *win = NULL;
  downsamp_storage *local;
  int length = (int)floor(sa->all->g->srate / sa->all->g->krate + 0.1);
  int i;
  double sum;

  if (pf_ct > 1) {
    win = co_param_table(pf,2);
    if (win->size < length)
      runtime("Window for 'downsamp' must be at least one control period long.");
  }

  if (!(local = (downsamp_storage *)op->local)) {
    PROT_MAL(op->local,downsamp_storage,co_downsamp);
    local = (downsamp_storage *)op->local;
    local->buf = (double *)malloc(length * sizeof(double));
    for (i=0;i!=length;i++) local->buf[i]=0;
    local->pt = 0;
    op->dyn = local->buf;
  }

  if (rate == ASIG) {
    local->buf[local->pt++] = input;
    return(0);
  }
	
  if (rate == KSIG) {
    sum = 0;
    if (win)
      for (i=0;i!=length;i++)
	sum += local->buf[i] * get_table_value(win,i);
    else
      for (i=0;i!=length;i++)
	sum += local->buf[i] / length;

    local->pt= 0;
    return sum;
  }
  return 0;
}

double co_samphold(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double input = co_param(pf,1);
  double gate = co_param(pf,2);
  samphold_storage *local;

  if (!(local = (samphold_storage *)op->local)) {
    PROT_MAL(op->local,samphold_storage,co_samphold);
    local = (samphold_storage *)op->local;
    local->hold = 0;
  }
	
  if (gate)
    return(local->hold = input);
  else
    return(local->hold);
}

double co_delay(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double input = co_param(pf,1);
  double dly = co_param(pf,2);
  double out;
  delay_storage *local;
  int i;

  int idly = (int)(floor(dly * sa->all->g->srate + 0.5));
  if (!(local = (delay_storage *)op->local)) {
    PROT_MAL(op->local,delay_storage,co_delay);
    local = (delay_storage *)op->local;
    local->dline = (double *)malloc(idly * sizeof(double));
    for (i=0;i!=idly;i++)
      local->dline[i] = 0;
    local->pt = 0;
    op->dyn = local->dline; /* so we can free it later */
  }

  if (idly == 0)
    return(input);
  else {
    out = local->dline[local->pt];
    if (rate == ASIG) local->dline[local->pt++] = input;
    
    if (local->pt == idly) local->pt = 0;
    return(out);
  }
}

double co_delay1(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double input = co_param(pf,1);
  double out;
  delay1_storage *local;
  
  if (!(local = (delay1_storage *)op->local)) {
    PROT_MAL(op->local,delay1_storage,co_delay1);
    local = (delay1_storage *)op->local;
    local->last = 0;
  }
  out = local->last;
  if (rate == ASIG) local->last = input;

  return(out);
}

double co_fdelay(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double input = co_param(pf,1);
  double dly = co_param(pf,2);
  double maxdly = co_param(pf,3);
  double out;
  int fl, cl, i;
  double outpt;
  fdelay_storage *local;
  int imxdly;

  imxdly = (int)floor(maxdly * sa->all->g->srate + 0.5);

  if (!(local = (fdelay_storage *)op->local)) {
    PROT_MAL(op->local,fdelay_storage,co_fdelay);
    local = (fdelay_storage *)op->local;
    local->dline = (double *)malloc(imxdly * sizeof(double));
    for (i=0;i!=imxdly;i++)
      local->dline[i] = 0;
    local->pt = 0;
    op->dyn = local->dline; /* so we can free it later */
  }

  if (dly >= imxdly)
    runtime("Fractional delay larger than specified maximum.");

  /* just linear interp for now */

  outpt = local->pt - dly; if (outpt < 0) outpt += imxdly;
  
  fl = (int)floor(outpt);
  cl = (int)ceil(outpt);

  if (fl == cl) out = local->dline[fl];
  else out = (outpt - fl) * (local->dline[cl]-local->dline[fl]) /
	 (cl-fl) + local->dline[fl];

  if (rate == ASIG) local->dline[local->pt++] = input;
  if (local->pt >= imxdly) local->pt = 0;
  return out;
}

double co_comb(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double input = co_param(pf,1);
  double time = co_param(pf,2);
  double gain = co_param(pf,3);
  double out;
  comb_storage *local;
  int itime = (int)(floor(time * sa->all->g->srate + 0.5));
  int i;

  if (!(local = (comb_storage *)op->local)) {
    PROT_MAL(op->local,comb_storage,co_comb);
    local = (comb_storage *)op->local;
    local->dline = (double *)malloc(itime * sizeof(double));
    local->pt = 0;
    for (i=0;i!=itime;i++)
      local->dline[i] = 0;
    op->dyn = local->dline;
  }

  out = gain * (input + local->dline[local->pt]);
  if (rate == ASIG) local->dline[local->pt++] = out;
  if (local->pt == itime) local->pt = 0;

  return out;
}

double co_allpass(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double input = co_param(pf,1);
  double time = co_param(pf,2);
  double gain = co_param(pf,3);
  double out;
  allpass_storage *local;
  int itime = (int)(floor(time * sa->all->g->srate + 0.5));
  int i;

  if (!(local = (allpass_storage *)op->local)) {
    PROT_MAL(op->local,allpass_storage,co_allpass);
    local = (allpass_storage *)op->local;
    local->dline = (double *)malloc(itime * sizeof(double));
    local->pt = 0;
    for (i=0;i!=itime;i++)
      local->dline[i] = 0;
    op->dyn = local->dline;
  }

  out = -gain * input + local->dline[local->pt];
  if (rate == ASIG) local->dline[local->pt++] = gain * out + input;
  if (local->pt == itime) local->pt = 0;

  return out;
}

/* NON-NORMATIVE OPCODES */

/* ie, these are not in the standard */

double co_kdump(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  int i;

  printf("KDUMP (%.4f): ",sa->sched->time);
  for (i=0;i!=pf_ct;i++)
    printf("%.4f ",pf[i].val);

  printf("\n");
  return 0;
}

double co_adump(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  int i;

  printf("ADUMP (%.5f): ",sa->sched->time);
  for (i=0;i!=pf_ct;i++)
    printf("%.4f ",pf[i].val);
  printf("\n");
  return 0;
  
}

/* LSD midiCC add */
double co_midicc(sa_decoder *sa, opval *op, actparam *pf, int pf_ct, long rate) {
  double channel = co_param(pf,1);
  double contnum = co_param(pf,2);
  double contvalue = co_param(pf,3);

  int i;

  if (!sa->MIDICCINIT)
    {
      sa->MIDICCINIT = TRUE;
      for (i = 0; i < NUM_MIDI_CHANS; i++)
	{
	  /* Contiuous Controller Initializations go here */
	  sa->midicc[i][128] = 8192;	/* Pitch bend set to none */
	  sa->midicc[i][7] = 127;
	  sa->midicc[i][11] = 127;
	  sa->midicc[i][10] = 56;		/* Pan set to center */
	}
    }
	
  if (rate == IVAR)
    sa->midicc[(int)channel][(int)contnum] = contvalue;
	
  return 1;
}
