/* $Id: saol_interp.c,v 1.9 1997/11/20 18:37:03 eds Exp $ */
/* $Log: saol_interp.c,v $
 * Revision 1.9  1997/11/20  18:37:03  eds
 * SASBF integration.
 *
 * Revision 1.4  1997/11/15 00:43:18  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.8  1997/11/05  20:10:01  eds
 * Added tablemaps.
 *
 * Revision 1.7  1997/11/05  15:00:27  eds
 * Added vector parameters/return values/operators.
 *
 * Revision 1.6  1997/11/03  15:20:06  eds
 * Fixed free() bug in the INSTR statement.
 *
 * Revision 1.5  1997/10/03  15:11:00  eds
 * Added sfsynth() statement.
 *
 * Revision 1.4  1997/09/17  21:18:44  eds
 * Fixed input bug even BETTER.
 *
 * Revision 1.3  1997/09/17  16:34:57  eds
 * Fixed bug about passing 'input' as parameter to core opcode.
 *
 * Revision 1.2  1997/09/17  14:10:40  eds
 * Added spatialize statement.
 * */
/*********************************************************************

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 <string.h>
#include "saol.h"
#include "y.tab.h"
#include "saol_interp.h"
#include "saol_sched.h"
#include "saol_co_imp.h"
#include <malloc.h>

extern void spatialize(context *cx, double *pl, int p_ct);
void sfsynth(context *cx, long rate, double bank, double note, double vel,
	     char *outbus, char *revbus, char *chorbus,
	     double preset, double channel, double midibank);

double eval_block(context *cx, block *b, long rate) {
  /* this is where the action is! */
  int ind;
  long checkrate;
  double pl[256];
  int ct =0,i;
  double val[1024];
  exprlist *el;

  double bank, note, vel, preset, channel, midibank;
  char *outbus, *revbus, *chorbus;
  
  for (;b;b=b->next) {
 		
    switch ((int)b->st->type) {
			
			
    case EQ:
      /* only need to do this at the rate of the expression */
      if (b->st->rate == rate || b->st->rate == SPECIALOP ) {
	if (b->st->lvalue && b->st->lvalue->op == ARRAYREF) {
	  eval_expr(&cx,val,b->st->lvalue->left,rate);
	  ind = val[0] +0.5;
	}
	else
	  ind = 0;

	eval_expr(&cx,val,b->st->expr,rate);
	
	if ( b->st->lvalue && (b->st->rate == rate ||
	    (b->st->rate == SPECIALOP && rate == KSIG))) /* do assign */
	  if (b->st->lvalue->width != 1)
	    for (i=0;i!=b->st->lvalue->width;i++)
	      if (b->st->expr->width != 1)
		set_var_value(cx,b->st->lvalue,ind+i,val[i]);
	      else
		set_var_value(cx,b->st->lvalue,ind+i,val[0]);
	  else
	    set_var_value(cx,b->st->lvalue,ind,val[0]);
      }
      
      break;
			
    case IF:
      if (b->st->rate >= rate) {
	
	eval_expr(&cx,val,b->st->expr,rate);
	
	if (val[0])
	  eval_block(cx,b->st->b,rate);
	
      }
      /*if (b->st->rate == SPECIALOP && rate == ASIG)
	eval_expr(&cx,b->st->expr,rate);*/
      
      break;
      
    case ELSE:
      if (b->st->rate >= rate) {
	
	eval_expr(&cx,val,b->st->expr,rate);
	if (val[0])
	  eval_block(cx,b->st->b,rate);
	else
	  eval_block(cx,b->st->elseb,rate);
      }
      break;
      
    case WHILE:
      if (b->st->rate == rate) {
	while (eval_expr(&cx,val,b->st->expr,rate))
	  eval_block(cx,b->st->b,rate);
      }
      
      break;
      
    case OUTPUT:
      if (b->st->rate == ASIG && rate == ASIG) {
	
	/* allow whole-array output? */
	
	for (el = b->st->params,ct=0;el;el=el->next) {
	  eval_expr(&cx,val,el->p,rate);
	  for (i=0;i!=el->p->width;i++)
	    pl[ct++] = val[i];
	  
	  /* multiple outputs? */
	}	  
	instr_output(cx,pl,ct);
	  
      }
      break;
      
    case SPATIALIZE:
      if (b->st->rate == ASIG && rate == ASIG) {
	for (el = b->st->params,ct=0;el;el=el->next,ct++)
	  pl[ct] = eval_expr(&cx,val,el->p,rate);
	
	spatialize(cx,pl,ct);
      }
      break;
      
    case SFSYNTH:
      /*      if (b->st->rate == rate) { */
	bank = eval_expr(&cx,val,b->st->params->p,rate);
	note = eval_expr(&cx,val,b->st->params->next->p,rate);
	vel = eval_expr(&cx,val,b->st->params->next->next->p,rate);
	
	outbus = b->st->sfbusses->n->name;
	if (b->st->sfbusses->next)
	  revbus = b->st->sfbusses->next->n->name;
	else revbus = NULL;
	if (b->st->sfbusses->next && b->st->sfbusses->next->next)
	  chorbus = b->st->sfbusses->next->next->n->name;
	else chorbus = NULL;
	
	if (b->st->sfextra && b->st->sfextra->p)
	  preset = eval_expr(&cx,val,b->st->sfextra->p,rate);
	else
	  preset = cx->preset;
	
	if (b->st->sfextra && b->st->sfextra->next && b->st->sfextra->next->p)
	  channel = eval_expr(&cx,val,b->st->sfextra->next->p,rate);
	else
	  channel = cx->channel;
	
	if (b->st->sfextra && b->st->sfextra->next && b->st->sfextra->next->next &&
	    b->st->sfextra->next->next->p)
	  midibank = eval_expr(&cx,val,b->st->sfextra->next->next->p,rate);
	else
	  midibank = cx->midibank;
	
	sfsynth(cx,rate,bank,note,vel,outbus,revbus,chorbus,preset,channel,midibank);
	/*       } */
      break;
        
    case OUTBUS:
      if (rate == ASIG) {
	
	/* allow whole-array output? */
	
	for (el = b->st->params;el;el=el->next) {
	  eval_expr(&cx,val,el->p,rate);
	  for (i=0;i!=el->p->width;i++)
	    pl[ct++] = val[i];
	}
	bus_output(b->st->bus,cx,pl,ct);
	  
      }
      break;
			
    case RETURN:
			
      /* rate check? */
      return(eval_expr(&cx,val,b->st->expr,rate));
			
    case TURNOFF:
      if (rate == KSIG) {
	instr_turnoff(cx);
      }
      break;
			
    case EXTEND:
      if (rate == KSIG || (b->st->rate == SPECIALOP && rate == KSIG)) {
	instr_extend(cx,eval_expr(&cx,val,b->st->expr,rate));
      }
      if (b->st->rate == SPECIALOP && rate == ASIG)
	eval_expr(&cx,val,b->st->expr,rate);
      
      break;
			
    case INSTR:
      if (b->st->rate == rate || (b->st->rate == SPECIALOP && rate >= KSIG)) {
	exprlist *el;
	instr_handle *h;
				
	for (el = b->st->params;el;el=el->next)
	  pl[ct++] = eval_expr(&cx,val,el->p,rate);
				
	if (rate == KSIG)
	  h = new_instr_instance(cx->sa,b->st->iname,pl,0);
				
      }
      break;
    }
  }
  return 0;
}


double eval_expr(context **cx,double *val,expr *p,long rate) {
  exprlist *el;
  double ret;
  int i,i1,i2,i3;
  double val1[1024],val2[1024],val3[1024];
  
  switch (p->op) {
  case IDENT:
    for (i=0;i != p->width;i++) val[i] = get_var_value(*cx,p,i);
    return(val[0]);
  case NUMBER:
    return(val[0] = p->d->cval);
  case STRCONST:
    interror("can't evaluate string constant!");
    break;
  case ARRAYREF:
    for (i=0;i!=p->width;i++)
      val[i] = get_var_value(*cx,p,(int)(eval_expr(cx,val1,p->left,rate)+i+0.5));
    return(val[0]);
  case OPARRAY:
    eval_oparray(*cx,val,p,rate);
    return(val[0]);
  case OPCALL:
    eval_opcode(*cx,val,p,rate);
    return(val[0]);
  case NOT:
    eval_expr(cx,val1,p->left,rate);
    for (i=0;i!=p->width;i++) val[i] = !val1[i];
    return(val[0]);
  case UMINUS:
    eval_expr(cx,val1,p->left,rate);
    for (i=0;i!=p->width;i++) val[i] = -val1[i];
    return(val[0]);
  case LP:
    return(val[0] = eval_expr(cx,val,p->left,rate));
  case Q:
    eval_expr(cx,val1,p->left,rate);
    eval_expr(cx,val2,p->right,rate);
    eval_expr(cx,val3,p->extra,rate);
    for (i=0;i!=p->width;i++) {
      if (p->left->width == 1) i1 = 0; else i1 = i;
      if (p->right->width == 1) i2 = 0; else i2 = i;
      if (p->extra->width == 1) i3 = 0; else i3 = i;
      val[i] = val1[i1] ? val2[i2] : val3[i3];
    }
    return(val[0]);
  case LEQ:
    eval_expr(cx,val1,p->left,rate);
    eval_expr(cx,val2,p->right,rate);
    for (i=0;i!=p->width;i++) {
      if (p->left->width == 1) i1 = 0; else i1 = i;
      if (p->right->width == 1) i2 = 0; else i2 = i;
      val[i] = val1[i1] <= val2[i2];
    }
    return(val[0]);
  case GEQ:
    eval_expr(cx,val1,p->left,rate);
    eval_expr(cx,val2,p->right,rate);
    for (i=0;i!=p->width;i++) {
      if (p->left->width == 1) i1 = 0; else i1 = i;
      if (p->right->width == 1) i2 = 0; else i2 = i;
      val[i] = val1[i1] >= val2[i2];
    }
    return(val[0]);
  case EQEQ:
    eval_expr(cx,val1,p->left,rate);
    eval_expr(cx,val2,p->right,rate);
    for (i=0;i!=p->width;i++) {
      if (p->left->width == 1) i1 = 0; else i1 = i;
      if (p->right->width == 1) i2 = 0; else i2 = i;
      val[i] = val1[i1] == val2[i2];
    }
    return(val[0]);
  case NEQ:
    eval_expr(cx,val1,p->left,rate);
    eval_expr(cx,val2,p->right,rate);
    for (i=0;i!=p->width;i++) {
      if (p->left->width == 1) i1 = 0; else i1 = i;
      if (p->right->width == 1) i2 = 0; else i2 = i;
      val[i] = val1[i1] != val2[i2];
    }
    return(val[0]);
  case GT:
    eval_expr(cx,val1,p->left,rate);
    eval_expr(cx,val2,p->right,rate);
    for (i=0;i!=p->width;i++) {
      if (p->left->width == 1) i1 = 0; else i1 = i;
      if (p->right->width == 1) i2 = 0; else i2 = i;
      val[i] = val1[i1] > val2[i2];
    }
    return(val[0]);
  case LT:
    eval_expr(cx,val1,p->left,rate);
    eval_expr(cx,val2,p->right,rate);
    for (i=0;i!=p->width;i++) {
      if (p->left->width == 1) i1 = 0; else i1 = i;
      if (p->right->width == 1) i2 = 0; else i2 = i;
      val[i] = val1[i1] < val2[i2];
    }
    return(val[0]);
  case AND:
    eval_expr(cx,val1,p->left,rate);
    eval_expr(cx,val2,p->right,rate);
    for (i=0;i!=p->width;i++) {
      if (p->left->width == 1) i1 = 0; else i1 = i;
      if (p->right->width == 1) i2 = 0; else i2 = i;
      val[i] = val1[i1] && val2[i1];
    }
    return(val[0]);
  case OR:
    eval_expr(cx,val1,p->left,rate);
    eval_expr(cx,val2,p->right,rate);
    for (i=0;i!=p->width;i++) {
      if (p->left->width == 1) i1 = 0; else i1 = i;
      if (p->right->width == 1) i2 = 0; else i2 = i;
      val[i] = val1[i1] || val2[i2];
    }
    return(val[0]);
  case PLUS:
    eval_expr(cx,val1,p->left,rate);
    eval_expr(cx,val2,p->right,rate);
    for (i=0;i!=p->width;i++) {
      if (p->left->width == 1) i1 = 0; else i1 = i;
      if (p->right->width == 1) i2 = 0; else i2 = i;
      val[i] = val1[i1] + val2[i2];
    }
    return(val[0]);
  case MINUS:
    eval_expr(cx,val1,p->left,rate);
    eval_expr(cx,val2,p->right,rate);
    for (i=0;i!=p->width;i++) {
      if (p->left->width == 1) i1 = 0; else i1 = i;
      if (p->right->width == 1) i2 = 0; else i2 = i;
      val[i] = val1[i1] - val2[i2];
    }
    return(val[0]);
  case STAR:
    eval_expr(cx,val1,p->left,rate);
    eval_expr(cx,val2,p->right,rate);
    for (i=0;i!=p->width;i++) {
      if (p->left->width == 1) i1 = 0; else i1 = i;
      if (p->right->width == 1) i2 = 0; else i2 = i;
      val[i] = val1[i1] * val2[i2];
    }
    return(val[0]);
  case SLASH:
    eval_expr(cx,val1,p->left,rate);
    eval_expr(cx,val2,p->right,rate);
    for (i=0;i!=p->width;i++) {
      if (p->left->width == 1) i1 = 0; else i1 = i;
      if (p->right->width == 1) i2 = 0; else i2 = i;
      val[i] = val1[i1] / val2[i2];
    }
    return(val[0]);
  }
  return 0;			/* not reached */
}

double eval_oparray(context *cx, double *val, expr *opcall,long rate) {
  exprlist *el;
  opval *op;
  actparam param[256];
  double rval;
  int ct = 0,i,j;
  double idx;
  
  /* look up local context storage */

  /* memset(param,0,sizeof(actparam) * 256); */

  idx = eval_expr(&cx,val,opcall->left,rate);
  
  op = &(cx->cop_storage[opcall->oparray_defn->offset+(int)idx]);

  for (i=0,el=opcall->params;i!=opcall->actparam_ct;el=el->next)
    if (el->p->op == IDENT && cx->framevals[el->p->d->sym->offset].table &&
	el->p->d->sym->width)
      param[ct++].t = cx->framevals[el->p->d->sym->offset].table;
    else if (el->p->op == ARRAYREF && el->p->d->sym->tablemap) {
      param[ct++].t = resolve_tablemap(cx,el->p,rate);
    } else {
      eval_expr(&cx,val,el->p,rate);
      for (j=0;j!=el->p->width;j++)
	param[ct++].val = val[j];
    }

  /* evaluate core opcode */
  rval = opcall->co_ptr->code(cx->sa,op,param,ct,rate);

  val[0] = rval;
  return(rval);
}

table_storage *resolve_tablemap(context *cx, expr *p, long rate) {
  double val[1024];
  namelist *nl;
  int j;
  char s[342];
  symbol *sym;
  
  eval_expr(&cx,val,p->left,rate); 
  for (nl = p->d->sym->tablemap, j=0;j < val[0] && nl && nl->n;
       nl=nl->next, j++);
  if (!nl || !nl->n) {
    sprintf(s,"Tablemap reference out of bounds ('%s', max %d, index %.0f)",
	    p->d->sym->name,j-1,val[0]);
    runtime(s);
  }
  sym = get_sym_decl(cx->localvars,nl->n->name);
  return(cx->framevals[sym->offset].table);
}

double eval_opcode(context *cx, double *val, expr *opcall,long rate) {
  exprlist *el;
  actparam param[256];
  double rval;
  opval *op;
  int ct = 0,i,j;
  
  /* look up local context storage */

  /* memset(param,0,sizeof(actparam) * 256); */
    
  op = &(cx->cop_storage[opcall->op_offset]);
  
  for (i=0,ct=0,el=opcall->params;i!=opcall->actparam_ct;i++,el=el->next)
    if (el->p->op == IDENT && el->p->d->sym->width &&
	cx->framevals[el->p->d->sym->offset].table)
	
      param[ct++].t = cx->framevals[el->p->d->sym->offset].table;
    else if (el->p->op == ARRAYREF && el->p->d->sym->tablemap)
      param[ct++].t = resolve_tablemap(cx,el->p,rate);
    else {
      eval_expr(&cx,val,el->p,rate);
      for (j=0;j!=el->p->width;j++)
	param[ct++].val = val[j];
    }

  /* evaluate block wrt new context */
  rval = opcall->co_ptr->code(cx->sa,op,param,ct,rate);

  val[0] = rval;
  return(rval);
}

void set_context_pfields(context *cx, double *pf) {
  symtable *t;
  int i=0;

  for (t=cx->localvars; t && t->s->binding == STANDARD_NAME; t=t->next);

  for (i=1 ; t && t->s->binding == FORMALPARAM; i++,t=t->next)
    cx->framevals[t->s->offset].val = pf[i];
}

void push_context(context *cx, long rate) {
  symtable *t;
  int idx;
  frameval *fv;
  int i;
  
  /* first, any standard names */

  for (t=cx->localvars;
       t && t->s->binding == STANDARD_NAME;
       t=t->next) {
    if (t->s->type == rate && strcmp(t->s->name,"input"))
      for (i=0;i!=t->s->width;i++)
	cx->framevals[t->s->offset+i].val =
	  get_host_value(cx->instr,t->s,i);
  }

  for ( ; t && t->s->binding == FORMALPARAM; t=t->next);
  /* skip pfields */

  /* now get global variables */
  
  /* for each local variable */
  for (; t; t = t->next) {
				
    /* if it's an imported variable */
    if (t->s->imported &&
	/* and we're at the variable's rate */
	((t->s->type == rate) ||
	 /* or the variable is a table and we're in i-time */
	 (t->s->type == TABLE && rate == IVAR))) {
	/* then copy in the global variable or host variable */
	if (t->s->glink) 	/* global variable */
	  for (i=0;i!=t->s->width;i++) {
	    /* copy each channel */
	    fv = &cx->sa->global_cx->framevals[t->s->glink->offset+i];
	    cx->framevals[t->s->offset+i].val = fv->val;
	    cx->framevals[t->s->offset+i].table = fv->table; /* VIOLATION */
	  }
       	else			/* shared with host */
	  for (i=0;i!=t->s->width;i++)
	    cx->framevals[t->s->offset+i].val =
	      get_host_value(cx->instr,t->s,i);
    }
  }
}

void pop_context(context *cx, long rate) {
  int i;
  symtable *t;
  frameval *fv;
  
  for (t=cx->localvars;
       t && t->s->binding == STANDARD_NAME;
       t=t->next);
  for (;t && t->s->binding == FORMALPARAM; t=t->next);
  
  /* only need to export variables (and tables) */
  
  /* for each local variable */
  for (; t; t=t->next) {
    
    /* if it's an exported variable */
    if (t->s->exported && (t->s->type == rate))
      
      if (t->s->glink)		/* global variable */
	for (i=0;i!=t->s->width;i++) {
	  /* copy each channel */
	  fv = &cx->sa->global_cx->framevals[t->s->glink->offset];
	  fv->val = cx->framevals[t->s->offset+i].val;
	  /* don't need to do tables */
	}
      else			/* shared with host */
	for (i=0;i!=t->s->width;i++) 
	  set_host_value(cx->instr,t->s->name,i,
			 cx->framevals[t->s->offset+i].val);
  }
  
}


context *new_context(sa_decoder *sa, instr_decl *id, int inchan) {
  int ct = 0; 
  symtable *ptr;
  context *cx;
  int i;
	
  PROT_MAL(cx,context,new_context);

  cx->instr = NULL;
  cx->localvars = id->sym;
  cx->cop_storage = NULL;
  cx->inchan = inchan;
  cx->sa = sa;
  cx->channel = cx->preset = cx->midibank = 0;
  /*   cx->outp = (double *)calloc(sa->all->g->outchan,sizeof(double)); */

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

  memset(cx->framevals,0,(id->framesize) * sizeof(frameval));
  
  if (id->opsize) { /* o/w, no core opcodes */
    if (!(cx->cop_storage = (opval *)calloc(sizeof(opval), id->opsize)))
      interror("calloc() failure in new_context()\n");
  }

  memset(cx->cop_storage,0,id->opsize * sizeof(opval)); 
  
  /* register_context(cx) ? */
  return(cx);
}


table_storage *make_table(context *cx, tabledef *td) {
  actparam tv[1024];
  double val[1024];
  int ct;
  exprlist *el;
  double retn;
  table_storage *t;
	
  for (el = td->params,ct=0; el; el=el->next,ct++) {
    el->p->width = 1;
    if (el->p->op == IDENT &&
	get_table_decl(cx->localvars,el->p->d->iname))
      tv[ct].ref = el->p->d->iname;
    else if (el->p->op == STRCONST)
      tv[ct].ref = el->p->d->csval;
    else 
      tv[ct].val = eval_expr(&cx,val,el->p,IVAR);
  }
	
  t = gen_table(td->gen,tv,ct);
  t->name = td->name;
  return(t);
}

frameval *get_frameval(context *cx, expr *p, int index) {
  char s[1000];
  symbol *sym;
  int ct=0;
	
  sym = p->d->sym;
  if (!sym) {
    sprintf(s,"Wavetable '%s' undeclared.\n",p->d->iname);
    runtime(s);
  }
	
  if (sym->width <= index || index < 0) {
    sprintf(s,"Array '%s' out of bounds (max %d, index %d)",
	    p->d->iname, sym->width-1, index);
    runtime(s);
  }
  return(&(cx->framevals[sym->offset+index]));
}

void set_frameval(context *cx, expr *p, int index, frameval *val) {
  symbol *sym;
  int ct=0;
  char s[1000];
	
  sym = p->d->sym;
  
  if (sym->width <= index || index < 0) {
    sprintf(s,"Array '%s' out of bounds (max %d, index %d)",
	    p->d->iname, sym->width-1, index);
    runtime(s);
  }
  else {
    cx->framevals[sym->offset+index].val = val->val;
    cx->framevals[sym->offset+index].table = val->table;
  }
}

void set_var_value(context *cx, expr *p, int index, double val) {
  frameval *fv = get_frameval(cx,p,index);
	
    fv->val = val;
	
}

void set_var_value_byname(context *cx, char *vname, int index, double val) {
  /* only used for host setting variables */
  symtable *t;
  char s[800];
	
  for (t = cx->localvars;strcmp(t->s->name,vname);t=t->next);
  if (!t) {
    sprintf(s,"No such variable '%s' in instrument '%s'.",
	    vname,cx->instr->id->name);
    runtime(s);
  }
	
  else cx->framevals[t->s->offset + index].val = val;
}

double get_var_value(context *cx, expr *p, int index) {
  /* we know p->d is a terminal */
  frameval *fv;

  if (!p->d->sym->width) /* input */
    if (cx->instr->input) return cx->instr->input[index][cx->asample_ptr];
    else return 0;
  
  fv = get_frameval(cx,p,index);

  return(fv->val);
}

instr_handle *new_instr_instance(sa_decoder *sa, char *iname, double *pf,int inchan) {
  /* allocate new context for instr; run i-time; register with scheduler */
  /* nb first value in pf parameter list is the duration */
  symbol *sym;
  instr_decl *id;
  context *cx;
  instr_handle *h;
	
  sym = get_instr_decl(sa->all->g->sym,iname); /* look up instr in symbol table */
  if (!sym) {
    printf("Unknown instrument '%s', continuing...\n",iname);
  } else {
		
    id = (instr_decl *)(sym->defn); /* get its declaration */
    cx = new_context(sa,id,inchan); /* make the context */
    set_context_pfields(cx,pf);
    cx->localvars = id->sym;
		
    /* NB first argument is duration */
    h = register_inst(sa,id,cx,pf[0]); /* tell the scheduler about it */
    h->origin = ORIGIN_INSTR;
    h->inchan = inchan;
    cx->instr = h;		/* save its handle */
		
    add_default_host_vars(h);
    add_imported_host_vars(h);
    set_host_value(h,"time",0,sa->sched->time);
    set_host_value(h,"dur",0,pf[0]);
    
    run_itime(id,cx);	/* and run its i-time stuff */
    return(h);
  }
  return NULL;
}


void add_default_host_vars(instr_handle *h) {
  int i;
	
  for (i=0;i!=NUM_STD_NAMES;i++)
    new_host_var(h,std_names[i].name,std_names[i].width,std_names[i].type);
  set_host_value(h,"s_rate",0,h->cx->sa->all->g->srate);
  set_host_value(h,"k_rate",0,h->cx->sa->all->g->krate);
  set_host_value(h,"inchan",0,h->inchan);
  set_host_value(h,"outchan",0,h->cx->sa->all->g->outchan);
  set_host_value(h,"released",0,0);
  set_host_value(h,"stolen",0,0);
  set_host_value(h,"cpuload",0,0);
}

void add_imported_host_vars(instr_handle *h) {
  /* add host variables declared as 'imported' in this instrument */
  symtable *t;

  for (t = h->cx->localvars; t && t->s; t=t->next) {
    if (t->s->imported && !t->s->glink && !t->s->table) {
      new_host_var(h,t->s->name,t->s->width, t->s->type);
      set_host_value(h,t->s->name,0,0);
    }
  }
}


void run_itime(instr_decl *id, context *cx) {
  /* run the i-time things in instrument ID */
  int ct=0;
  symtable *t;
	
	
  push_context(cx,IVAR);

  for (t = cx->localvars;t && t->s;t=t->next)
    if (t->s->type == TABLE && !t->s->imported)
      cx->framevals[t->s->offset].table = make_table(cx,t->s->table);
	
  /* eval the i-time code wrt the new context */
  eval_block(cx,id->code,IVAR);
	
  /* copy out global variables */
	
  pop_context(cx,IVAR);
}

void run_katime(instr_decl *id, context *cx,long rate) {
  int i;

  push_context(cx,rate);
	
  if (rate == KSIG) {		/* run once */
    eval_block(cx,id->code,KSIG);
  }
  else {			/* run many times; the sample pointer tells
				   get_ and set_var_value where to read/put
				   audio samples */
    zero_instr_output(cx->instr);
    for (i=0;i!=cx->sa->ksmps;i++) {
      cx->asample_ptr = i;
      eval_block(cx,id->code,ASIG);
    }
  }
  
  pop_context(cx,rate);
}
