/* $Id: saol_sched.c,v 1.5 1997/11/05 15:02:34 eds Exp $ */
/* $Log: saol_sched.c,v $
 * Revision 1.5  1997/11/05  15:02:34  eds
 * Added vector parameters/return values/operators.
 *
 * Revision 1.4  1997/10/09  15:34:33  eds
 * Fixed "extend" bug.
 *
 * Revision 1.3  1997/10/01  15:33:49  eds
 * Fixed bug in turnoff events.
 * */
/*********************************************************************
  
  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 <stdlib.h>
#include <string.h>
#include <math.h>
#include "saol.h"
#include "saol_interp.h"
#include "saol_sched.h"
#include "y.tab.h"
/* #include "audio.h"*/
#include "aifif.h"
#include <malloc.h>

#define SGN(x) (x < 0 ? -1 : x == 0 ? 0 : 1) 

int soundOutQueue(short *t) {
  return 0;
}

int soundOutClose(int t) {
  return 0;
}

int soundOutOpen(int sr, int ch, int sz) {
  return 0;
}

void do_input(sa_decoder *sa);

void sched_run_kcycle(sa_decoder *sa) {
  int active_ev=0,files_open = 0,i;
  handle_list *hl;
  double *pf;
  instr_handle *h;
  event_list *pending,*old;
  double oldtime;
  
  /* stop if there's no active or scheduled events */
  for (hl = sa->sched->active; hl && !active_ev; hl=hl->next)
    if (hl->h->origin != ORIGIN_SEND)
      active_ev = 1;

  for (i = 0;i!=sa->inct;i++) {
    if (!sa->in[i].done) files_open = 1;
  }
  
  if (!active_ev && !sa->sched->pending && !files_open) {
    sa->sched->running = 0;
    return;
  }

  clear_busses(sa);
  
  /* deal with any pending events */
  
  pending = sa->sched->pending;

  do_input(sa); /* get global input data */
       
  while (pending && pending->ev->time <= sa->sched->time) {
    
    switch (pending->ev->type) {
    case INSTR_EVENT: 
      pf = event_actparam_list(pending->ev);
      h = new_instr_instance(sa,pending->ev->name,pf,0);
      if (pending->ev->label)
	h->label = strdup(pending->ev->label);
      
      break;
			
    case CONTROL_EVENT:
      if (pending->ev->label) { /* labelled control change */
	for (hl = sa->sched->active;hl;hl=hl->next)
	  if (!strcmp(hl->h->label,pending->ev->label))
	    set_host_value(hl->h,pending->ev->name,0,pending->ev->val[0]);
      } else {			/* global variable change */
	set_var_value_byname(sa->global_cx,pending->ev->name,0,pending->ev->val[0]);
      }
      break;
			
    case TURNOFF_EVENT:
      
      set_host_value(pending->ev->h,"released",0,1);
      pending->ev->h->turnoff_notified = 1;
      break;
			
    case TABLE_EVENT:
			
      if (!strcmp(pending->ev->name,"destroy")) /* destroy table */
	destroy_global_table(sa->global_cx,pending->ev->name);
      add_global_table(sa->global_cx,pending->ev->name,pending->ev->val);
      break;
			
    case END_EVENT:
      sa->sched->running = 0;
      break;
    }

    old = pending;
    pending=pending->next;
    
    sa->sched->pending = remove_event(sa->sched->pending,old);
  }

  /* run through each event in the current event list
     and give it a k-cycle then its a-time */

  for (hl=sa->sched->active;hl;hl=hl->next) {
    run_katime(hl->h->id,hl->h->cx,KSIG);
    run_katime(hl->h->id,hl->h->cx,ASIG);
  }

  /* make overall sound output */

  output_sound(sa);
  
  /* delete any turned-off instances */

  for (hl=sa->sched->active;hl;hl=hl->next) {
    if (hl->h->turnoff_notified)
      destroy_inst(sa,hl->h);
  }

  oldtime = sa->sched->time;
  sa->sched->time += 1/(double)sa->all->g->krate;
  if ((int)sa->sched->time - (int)oldtime) {
    printf("*");
  }
  else if ((int)(sa->sched->time*10) - (int)(oldtime*10)) {
    printf(".");
  }
  fflush(stdout);

}

void output_sound(sa_decoder *sa) {
  handle_list *hl;
  static double **sound = NULL;
  int i,j;
  
  if (!sa->outbuf) sa->outbuf = (short *)calloc(sizeof(short),sa->bufsize * sa->all->g->outchan);
  if (!sound) {
    sound = (double **)calloc(sa->all->g->outchan,sizeof(double *));
    for (i=0;i!=sa->all->g->outchan;i++) {
      sound[i] = (double *)calloc(sa->ksmps,sizeof(double));
    }
  }

  hl = sa->sched->active;
  if (hl) {
    for (i=0;i!=sa->all->g->outchan;i++)
      for (j=0;j!=sa->ksmps;j++)
	sound[i][j] = hl->h->output[i][j];
    
    for (hl=hl->next;hl;hl=hl->next) 
      for (i=0;i!=sa->all->g->outchan;i++)
	for (j=0;j!=sa->ksmps;j++)
	  sound[i][j] += hl->h->output[i][j];
  }
  else
    for (i=0;i!=sa->all->g->outchan;i++)
      for (j=0;j!=sa->ksmps;j++)
	sound[i][j] = 0;
    
    
  if (sa->textout) {
    printf("--------------\n");
    for (i=0;i!=sa->ksmps;i++) {
      printf("Time %.4f: ",sa->sched->time + (double)1./(double)sa->all->g->srate * i);
      for (j =0 ;j!=sa->all->g->outchan;j++) 
	printf("%.4f ",sound[j][i]);
      printf("\n");
    }
  }

  if (sa->audioout) 
    for (i=0;i!=sa->ksmps;i++) 
      for (j=0;j!=MIN(sa->all->g->outchan,2);j++) {
	sa->outbuf[sa->outbuf_ct++] = (short)(fabs(sound[j][i]) < 1 ?
					      (short)(sound[j][i] * 32767) : 32767 * SGN(sound[j][i]));
	if (sa->outbuf_ct == sa->bufsize) {
	  soundOutQueue(sa->outbuf);
	  sa->outbuf_ct = 0;
	}
      }
    
  if (sa->outfile)
    for (i=0;i!=sa->ksmps;i++) 
      for (j=0;j!=MIN(sa->all->g->outchan,2);j++) {
	sa->outbuf[sa->outbuf_ct++] = ((fabs(sound[j][i]) < 1.0) ?
				       (short)(sound[j][i] * 32767) : 32767 * SGN(sound[j][i]));
	if (sa->outbuf_ct == sa->bufsize) {
	  aifWriteFrames(sa->aifout,sa->outbuf,sa->ksmps);
	  sa->outbuf_ct = 0;
	}
      }
}

void finish_output(sa_decoder *sa) {
  if (sa->audioout) {
    while (sa->outbuf_ct < sa->bufsize)
      sa->outbuf[sa->outbuf_ct++] = 0;
    soundOutQueue(sa->outbuf);
    soundOutClose(1);
  }
  if (sa->outfile) {
    aifWriteFrames(sa->aifout,sa->outbuf,sa->outbuf_ct);
    aifClose(sa->aifout);
  }
}

instr_handle *register_inst(sa_decoder *sa,instr_decl *id, context *cx, double dur) {
  instr_handle *h;
  handle_list *new_hl;
  event *ev;
  int i;

  PROT_MAL(h,instr_handle,register_instr);
  h->origin = 0;
  h->label = NULL;
  h->cx = cx;
  h->id = id;
  h->turnoff_notified = 0;
  h->input = NULL;		/* attach input here */
  h->hvl = NULL;

  if (!(h->output = (asig_frame **)calloc(sizeof(asig_frame *),
					  cx->sa->all->g->outchan))) {
    runtime("Couldn't allocation space for output buffer in register_inst.");
  }
  for (i=0;i!=cx->sa->all->g->outchan;i++)
    if (!(h->output[i] = (asig_frame *)calloc(sizeof(asig_frame),cx->sa->ksmps))) {
      runtime("Couldn't allocate space for output buffer in register_inst.");
    }

  PROT_MAL(new_hl,handle_list,register_instr);
  new_hl->h = h;
  
  /* just put it first for now */
  
  if (!cx->sa->sched->active) {
    sa->sched->active = new_hl;
    new_hl->next = NULL;
  }
  else {
    new_hl->next = cx->sa->sched->active;
    cx->sa->sched->active = new_hl;
  }
  
  /* actually need to figure out where to put the new event based on
     sequencing */

  /* make a turnoff event */
  if (dur >= 0) {
    ev = make_event(TURNOFF_EVENT,sa->sched->time+(dur-0.0001)-1/(double)cx->sa->all->g->krate);
    ev->h = h;
    schedule_event(sa,ev);
  }
  return(h);
}

double bufval2(sa_decoder *sa, char *buf, int input, int frame, int chan) {
  /* get the value of frame #frame, chan #chan, from the buffer, which
     corresponds to input #input of the current decode process */
  int nbits, nchan;

  nchan = sa->in[input].nchan;
  nbits= sa->in[input].nbits;
  
  if (nbits == 16)
    return((double)*(short *)&buf[frame * nchan * 2 + chan * 2]);
  
  if (nbits == 32)
    return((double)*(int *)&buf[frame * nchan * 2 + chan * 2]);
  
  if (nbits == 8)
    return((double)*(char *)&buf[frame * nchan *2 + chan * 2]);

  return 0;
}

int buf_convert(char *buf, double **newbuf, int ct, int framect,
		sa_decoder *sa, int input) {
  /* turn the CT values in buf into KSMPS values in newbuf
     by processing the input data and interpolating/decimating
     if FRAMECT > CT, this is only a partial frame */
  double **temp;
  int i,j,k,f,c,st,en,cvtct;
  double x1,x2,sum,scale;
  
  /* first get values out of char * buffer */
  cvtct = floor(sa->ksmps * ct/framect + 0.001);
  
  temp = (double **)calloc(sa->in[input].nchan,sizeof(double *));
  for (i=0;i!=sa->in[input].nchan;i++) {
    temp[i] = (double *)malloc(ct * sizeof(double));
    scale = (double)(2 << (sa->in[input].nbits-2));
    for (j=0;j!=ct;j++)
      temp[i][j] = bufval2(sa,buf,input,j,i)/scale;
  }

  /* now SR convert (hack) */
  for (i=0;i!=sa->in[input].nchan;i++) {
    if (cvtct == ct)
      for (j=0;j!=cvtct;j++) 
	newbuf[i][j] = temp[i][j];
    if (cvtct > ct) {/* have to interpolate */
      for (j=0;j!=cvtct;j++) {
	f = (int)floor((double)j/(double)cvtct * ct + 0.001);
	if (j == cvtct-1) f--;
	x1 = temp[i][f];
	x2 = temp[i][f+1];
      	newbuf[i][j] = ((double)j/cvtct*ct - f)*(x2-x1) + x1;
      }
    }
    if (cvtct < ct) {/* have to decimate */
      for (j=0;j!=cvtct;j++) { /* boxcar window */
	st = (int)floor((double)j/(double)cvtct*ct);
        en = (int)ceil((j+1.0)/cvtct*ct);
	sum = 0;
	for (k=st;k!=en;k++)
	  sum += temp[i][k];
	newbuf[i][j] = sum/(st-en);
      }
    }
  }
  for (i=0;i!=sa->in[input].nchan;i++)
    free(temp[i]);
  free(temp);

  return(cvtct);
}

void do_input(sa_decoder *sa) {
  int i,j,k;
  int mx,numsamps,ct,mxbits,cvtct,size;
  char *buf = NULL;
  
  if (!sa->inct || !sa->inbus) return;

  mx = 0;
  if (!sa->inbuf) { /* allocate enough for widest input */
    for (i=0;i!=sa->inct;i++) {
      if (sa->in[i].nchan > mx)
	mx = sa->in[i].nchan;
    }
    sa->inbuf = (double **)calloc(mx,sizeof(double *));
    for (i=0;i!=mx;i++)
      sa->inbuf[i] = (double *)calloc(sa->ksmps,sizeof(double));
  }


  for (i=0;i!=sa->inct;i++) { /* turn on when it's time */
    if (sa->in[i].delay <= sa->sched->time && !sa->in[i].running &&
	!sa->in[i].done)
      sa->in[i].running = 1;
  }
  mxbits = 0;
  for (i=0;i!=sa->inct;i++) {
    size = sa->in[i].nchan * sa->in[i].nbits * sa->in[i].srate;
    if (sa->in[i].running && size > mxbits)
      mxbits = size;
  }
  if (mxbits)
    buf = (char *)malloc(mxbits/8 * sa->ksmps / sa->all->g->srate);

  for (i=0;i!=sa->inct;i++) { /* for each input */
    if (sa->in[i].running) {  /* if it's running */
      numsamps = floor(sa->ksmps * sa->in[i].srate / sa->all->g->srate+0.5);
      ct = aifReadFrames(sa->in[i].aif_in,buf,numsamps); /* get data */
      
      if (ct < numsamps) {  
	sa->in[i].done = 1;   /* tag it if done */
	sa->in[i].running = 0;
      }

      cvtct = buf_convert(buf,sa->inbuf,ct,numsamps,sa,i);

    }
    else
      cvtct = 0;                 /* not started, or done */
    
    for (j=0;j!=cvtct;j++)       /* copy any data we got into input bus */
      for (k=0;k!=sa->in[i].nchan;k++)
        if (sa->in[i].busch+k < sa->all->g->inchan) /* don't copy extras */
	  sa->inbus->val[sa->in[i].busch+k][j] = sa->inbuf[k][j];
    
    for (j=j;j!=sa->ksmps;j++)   /* fill the rest with zeros */
      for (k=0;k!=sa->in[i].nchan;k++)
	sa->inbus->val[sa->in[i].busch+k][j] = 0;
  }
  if (buf) free(buf);
}      
  
void zero_instr_output(instr_handle *h) {
  int i,j;

  for (i=0;i!=h->cx->sa->all->g->outchan;i++)
    for (j=0;j!=h->cx->sa->ksmps;j++)
      h->output[i][j] = 0;
}

void instr_output(context *cx, double *pf, int pf_ct) {
  int ct,i;
  bustable *bus;

  if (bus = cx->instr->id->route) { /* goes onto a bus */
    if (pf_ct == 1)
      for (i=0;i!=bus->width;i++)
	bus->val[i][cx->asample_ptr] += pf[0];
    else
      for (i=0;i!=cx->sa->all->g->outchan;i++)
	bus->val[i][cx->asample_ptr] += pf[i];
  } else {			/* goes to output */
    
    if (pf_ct == 1)
      for (i=0;i!=cx->sa->all->g->outchan;i++)
	cx->instr->output[i][cx->asample_ptr] += pf[0];
    else
      for (i=0;i!=cx->sa->all->g->outchan;i++)
	cx->instr->output[i][cx->asample_ptr] += pf[i];
  }
}

void bus_output(char *busname,context *cx, double *pf, int pf_ct) {
  int ct,i;
  bustable *bus;

  bus = get_bus(cx->sa->all->g->bus,busname);
  
  if (pf_ct == 1)
    for (i=0;i!=bus->width;i++)
      bus->val[i][cx->asample_ptr] += pf[0];
  else
    for (i=0;i!=cx->sa->all->g->outchan;i++)
      bus->val[i][cx->asample_ptr] += pf[i];
}

void clear_busses(sa_decoder *sa) {
  bustable *bus;
  int i,j;

  for (bus=sa->all->g->bus;bus;bus=bus->next)
    if (bus->val)
      for (i=0;i!=bus->width;i++)
	for (j=0;j!=sa->ksmps;j++)
	  bus->val[i][j] = 0;
}

void instr_turnoff(context *cx) {
  cx->instr->turnoff_notified = 1;
}

void instr_extend(context *cx, double time) {
  event_list *evl;
  event *ev;
  double newdur;
  
  /* find turnoff event for this note */
  if (evl = find_event_handle(cx->instr,TURNOFF_EVENT)) {
    ev = make_event(TURNOFF_EVENT,evl->ev->time + time);
    cx->sa->sched->pending = remove_event(cx->sa->sched->pending, evl);
  }
  else				/* none scheduled or already deleted */
    ev = make_event(TURNOFF_EVENT,cx->sa->sched->time + time);

  ev->h = cx->instr;
  if (time > 1/cx->sa->all->g->krate && cx->instr->turnoff_notified)
    cx->instr->turnoff_notified = 0; /* saved itself */

  schedule_event(cx->sa,ev);
}

double get_host_value(instr_handle *h, symbol *sym, int idx) {
  hostvar_list *hvl;
  char s[1000];

  for (hvl = h->hvl; hvl && hvl->sym != sym; hvl=hvl->next);
  if (!hvl) for (hvl = h->hvl; hvl && strcmp(hvl->name,sym->name); hvl=hvl->next);
  if (!hvl) {
    sprintf(s,"No such host variable '%s'",sym->name);
    runtime(s);
  }

  return(hvl->val[idx]);
}

int has_host_var(instr_handle *h, char *varname) {
  hostvar_list *hvl;
  symbol *sym;

  sym = get_sym_decl(h->id->sym,varname);
  if (!sym) return(0);
  
  for (hvl = h->hvl; hvl && hvl->sym != sym; hvl=hvl->next);
  return (hvl != NULL);
}

void set_host_value(instr_handle *h, char *varname, int idx, double val) {
  hostvar_list *hvl = NULL;
  symbol *sym;

  sym = get_sym_decl(h->id->sym,varname);
  
  if (sym) for (hvl = h->hvl; hvl && hvl->sym != sym; hvl=hvl->next);
  else for (hvl = h->hvl; hvl && strcmp(varname,hvl->name); hvl=hvl->next);
  
  if (!hvl) {
    printf("\nWarning: no such control '%s', or unused in instrument.\n",varname);
  }

  else hvl->val[idx] = val;
}


void new_host_var(instr_handle *h, char *name, int width, int type) {
  hostvar_list *hvl;
  int i;

  if (!width) width = h->inchan;
  PROT_MAL(hvl,hostvar_list,new_host_var);
  hvl->name = strdup(name);
  hvl->sym = get_sym_decl(h->id->sym,name);
  hvl->width = width;
  if (type != ASIG) {
    hvl->val = (double *)calloc(sizeof(double),width);
    hvl->asig = NULL;
  }
  else  {
    hvl->asig = (asig_frame **)calloc(sizeof(asig_frame *),width);
    for (i=0;i!=width;i++)
      hvl->asig[i] = NULL;
    hvl->val = NULL;
  }
  
  hvl->next = h->hvl;
  h->hvl = hvl;
}

#if 0
asig_frame *get_host_asig(instr_handle *h, symbol *sym, int idx) {
  hostvar_list *hvl;
  char s[1000];

  for (hvl = h->hvl; hvl && hvl->sym != sym; hvl=hvl->next);

  if (!hvl) {
    sprintf(s,"No such host variable '%s'",sym->name);
    runtime(s);
  }

  return(hvl->asig[idx]);
}

void set_host_asig(instr_handle *h, symbol *sym, int idx, asig_frame *val) {
  hostvar_list *hvl;

  char s[1000];
  
  for (hvl = h->hvl; hvl && hvl->sym != sym; hvl=hvl->next);
  
  if (!hvl) {
    sprintf(s,"No such host variable '%s'", sym->name);
    runtime(s);
  }

  hvl->asig[idx] = val;
}
#endif

void schedule_event(sa_decoder *sa, event *ev) {
  event_list *evl = sa->sched->pending,*new,*last;

  PROT_MAL(new,event_list,schedule_event);
  new->ev = ev;

  if (!evl || ev->time < evl->ev->time) {
    new->next = sa->sched->pending;
    sa->sched->pending = new;
    return;
  }
  while (evl && evl->ev->time <= ev->time) {
    last = evl;
    evl=evl->next;
  }
  new->next = evl;
  last->next = new;
}


void sched_add_events(sa_decoder *sa, event events[],int num) {
  int i;
  
  for (i=0;i!=num;i++) {
    events[i].time -= 0.0001;
    schedule_event(sa, &(events[i]));
  }
}

void open_inputs(sa_decoder *sa) {
  int i;
  char s[562];
  long pvb[16];
  int pvl = 0,chanct=0;

  if (sa->inct) {		/* open all the input files */
    sa->inchan = 0;
    sa->maxinsr = 0;
    for (i=0;i!=sa->inct;i++) {
      sa->in[i].aif_in = aifNew();
      
      if (aifOpenRead(sa->in[i].aif_in,sa->in[i].fn)) {
	sprintf(s,"Couldn't open '%s' for read.",sa->in[i].fn);
	runtime(s);
      }
      sa->in[i].running = 0;
      sa->in[i].done = 0;
      pvb[0] = AIF_P_CHANNELS;
      pvb[2] = AIF_P_SAMPSIZE;
      pvb[4] = AIF_P_SAMPRATE;
      aifGetParams(sa->in[i].aif_in,pvb,6);
      sa->in[i].nchan = pvb[1]; sa->inchan += sa->in[i].nchan;
      sa->in[i].busch = chanct;
      chanct += pvb[1];
      sa->in[i].nbits = pvb[3];
      sa->in[i].srate = (int)*(float *)&pvb[5];
      if (sa->maxinsr < sa->in[i].srate)
	sa->maxinsr = sa->in[i].srate;
    }
  }
}

void start_scheduler(sa_decoder *sa) {
  int i;
  char s[563];
  
  PROT_MAL(sa->sched,scheduler,start_scheduler);
  sa->sched->active = NULL;
  sa->sched->pending = NULL;
  sa->sched->time = 0;
  sa->sched->running = 1;

  if (sa->audioout) {
    sa->bufsize = sa->ksmps;
    if (!soundOutOpen(sa->all->g->srate,sa->all->g->outchan,sa->bufsize))
      exit(1);
  }
  if (sa->outfile) {
    long pvb[16];
    int pvl = 0;

    sa->bufsize = sa->ksmps * MIN(2,sa->all->g->outchan);
    sa->aifout = aifNew();
    pvb[pvl++] = AIF_P_SAMPRATE;  pvb[pvl++] = sa->all->g->srate;
    pvb[pvl++] = AIF_P_CHANNELS;  pvb[pvl++] = sa->all->g->outchan;
    pvb[pvl++] = AIF_P_SAMPSIZE;  pvb[pvl++] = 16;
    pvb[pvl++] = AIF_P_FILETYPE;  pvb[pvl++] = AIF_FT_AIFF;
    aifSetParams(sa->aifout,pvb,pvl);

    aifOpenWrite(sa->aifout,sa->outfile,UNK_LEN);
  }
  
  start_send_instrs(sa,sa->all->g->inchan);
  
}

void start_send_instrs(sa_decoder *sa,int inchan) {
  bustable *bus;
  instr_handle *h;
  int ct=0,i;
  send_list *sl;
  exprlist *el;
  namelist *nl;
  symbol *sym;
  double pf[256],val[1024];

  for (bus = sa->all->g->bus;bus;bus=bus->next) {
    if (!strcmp(bus->name,"input"))
      bus->width = inchan;
    bus->val = (double **)calloc(sizeof(double *),bus->width);
    for (i=0;i!=bus->width;i++)
      bus->val[i] = (double *)calloc(sizeof(double),sa->ksmps);
  }
  for (sl = sa->all->g->sends;sl && sl->s;sl=sl->next) {

    /* start instrument */
    
    pf[0] = -1;
    
    for (ct=1,el = sl->s->pfields;el && el->p;ct++,el=el->next)
      pf[ct] = eval_expr(&sa->global_cx,val,el->p,IVAR);

    ct = 0;
    for (nl = sl->s->busses,ct=0;nl && nl->n; nl=nl->next)
      ct += get_bus(sa->all->g->bus,nl->n->name)->width;
    
    h = new_instr_instance(sa,sl->s->instr,pf,ct);
    sl->s->h = h;
    h->origin = ORIGIN_SEND;

    /* attach input channels to instrument */
    
    h->input = (double **)calloc(sizeof(double *),ct);

    for (nl = sl->s->busses,ct=0;nl && nl->n; nl=nl->next)
      for (i=0;i!=get_bus(sa->all->g->bus,nl->n->name)->width;i++,ct++)
	h->input[ct] = get_bus(sa->all->g->bus,nl->n->name)->val[i];

  }
  
}

void make_global_context(sa_decoder *sa) {
  symtable *t;
  context *cx;
  int ct,i;
  char s[300];
  	
  PROT_MAL(sa->global_cx,context,make_global_context);
  cx = sa->global_cx;

  cx->instr = NULL;
  cx->localvars = sa->all->g->sym;
  cx->cop_storage = NULL;
  cx->sa = sa;
  /* cx->outp = (double *)calloc(sa->all->g->outchan,sizeof(double)); */

  if (sa->all->g->framesize) { /* otherwise, no vars */
    if (!(cx->framevals = (frameval *)calloc(sizeof(frameval), sa->all->g->framesize)))
      interror("calloc() failure in new_context()\n");
  }

  memset(cx->framevals,0,(sa->all->g->framesize) * sizeof(frameval));

  if (!sa->all->g->srate) {
    if (sa->inchan) {
      sa->all->g->srate = sa->maxinsr;
      sprintf(s,"No srate given, taking maximum input rate %d.",sa->maxinsr);
      warn_line(s,0);
    }
    else runtime("No sampling rate or input specified.");
  }

  if (!sa->all->g->krate) {
    sa->all->g->krate = 50;
    while (fabs(floor(sa->all->g->srate / (double)sa->all->g->krate) *
		sa->all->g->krate - (double)sa->all->g->srate) > 0.001)
      sa->all->g->krate++;
    sprintf(s,"No krate given, defaulting to %d.",sa->all->g->krate);
    warn_line(s,0);
  }

  sa->ksmps = sa->all->g->srate/sa->all->g->krate;

  for (ct=0,t=sa->all->g->sym;t;ct+=t->s->width,t=t->next) {
    if (t->s->type == TABLE)
      cx->framevals[ct].table = make_table(cx,t->s->table);
  }

}

double *event_actparam_list(event *ev) {
  double *p=NULL;
  int i;

  p = (double *)calloc(ev->numval+1,sizeof(double));
  p[0] = ev->dur;
  for (i=0;i!=ev->numval;i++)
    p[i+1] = ev->val[i];

  return(p);
}

event_list *remove_event(event_list *first, event_list *r) {
  event_list *hold,*p;

  if (first == r) {
    hold = first->next;
    /*    free(first->ev);
	  free(first); */
    return(hold);
  }

  
  for (hold=p=first;p != r;hold=p,p=p->next) ;
  hold->next = p->next;
  /* if (p->ev) free(p->ev);
     free(p);*/
  
  return(first);
}

event_list *find_event_handle(instr_handle *h, int type) {
  event_list *evl;

  evl = h->cx->sa->sched->pending;

  while (evl && (evl->ev->h != h || (type && evl->ev->type != type)))
    evl=evl->next;

  return evl;
}

event *make_event(int type, double time) {
  event *ev;
  
  PROT_MAL(ev,event,make_event);

  ev->name = NULL;
  ev->label = NULL;
  ev->time = time;
  ev->dur = -1;
  ev->val = (double *)malloc(sizeof(double));
  ev->val[0] = 0;
  ev->numval = 0;
  ev->h = NULL;
  ev->type = type;

  return(ev);
}

void add_global_table(context *cx, char *name, double *val) { }
void destroy_global_table(context *cx, char *name) { }
     
void destroy_inst(sa_decoder *sa, instr_handle *h) {
  int i;
  hostvar_list *hvl,*last;
  handle_list *hl,*lasthl = NULL;

  for (hl=sa->sched->active; hl->h != h; lasthl = hl,hl=hl->next) ;
  if (lasthl)
    lasthl->next = hl->next;
  else
    sa->sched->active = hl->next;
  
  if (h->label) free(h->label);
  free_context(h->cx);
  if (h->input) free(h->input);

  for (i=0;i!=sa->all->g->outchan;i++)
    free(h->output[i]);

  free(h->output);

  for (last = NULL,hvl=h->hvl;hvl;last=hvl,hvl=hvl->next) {
    if (last) free(last);
    if (hvl->val)
      free(hvl->val);
    if (hvl->asig) {
      for (i=0;i!=hvl->width;i++)
	free(hvl->asig[i]);
      free(hvl->asig);
    }
  }
  free(last);
}
     
void free_context(context *cx) {
  int ct,i;
  symtable *t;
  child_cx_list *ccx,*last;
  
  for (ct=0,t=cx->localvars;t;ct+=t->s->width,t=t->next) 
      if (t->s->type == TABLE && (!(t->s->imported || t->s->exported)))
	free(cx->framevals[ct].table);
    
  free(cx->framevals);

  for (ct=0;ct!=cx->instr->id->opsize;ct++) {
    if (cx->cop_storage[ct].local)
      free(cx->cop_storage[ct].local);
    if (cx->cop_storage[ct].dyn)
      free(cx->cop_storage[ct].dyn);
  }
    
  if (cx->cop_storage) free(cx->cop_storage);

  free(cx);
}
     
