#include "snd.h"

#define DAC_BUFFER_SIZE 256

static int dac_chans = 0;
static int dac_running = 0;
static int dac_decay = 0;

int dac_is_running(void) {return(dac_running);}

static short dac_buffer[DAC_BUFFER_SIZE];
static float revin[DAC_BUFFER_SIZE];

static float reverb_factor = 1.09;
static float reverb_decay = 1.0;
static float lp_coeff = 0.7;
static float volume = 1.0;
static int revchans = 0;
static int revdecay = 0;


/* this version of sin 2.5 times as fast as math lib's */
#define SINE_SIZE 2050
#define SINE_SCALE 2048.0
#define SINE_MOD 2048

static float *c_sine_table;
static float c_sine_table_scaler = (float)(SINE_SCALE/two_pi);
static int c_sine_table_is_inited = 0;

void c_init_sine (void) /* not static, despite lint because declared in cmus_prototypes.h */
{
  int i;
  float phase,incr;
  if (c_sine_table_is_inited == 0)
    {
      c_sine_table = (float *)calloc(SINE_SIZE,sizeof(float));
      incr = (float)(1.0/c_sine_table_scaler);
      for (i=0,phase=0.0;i<SINE_SIZE;i++,phase+=incr)
	{
	  c_sine_table[i]=(float)sin((double)phase);
	}
      c_sine_table_is_inited = 1;
    }
}

static float c_sin(float phase)
{
  float mag_phase,frac;
  int index;
  float *sint;
  if (phase < 0.0)
    {
      index=(int)(phase*inverse_pi);
      phase+=(float)(((1-index)*(two_pi)));
    }
  mag_phase = phase*c_sine_table_scaler;
  index = (int)mag_phase;
  frac = mag_phase - index;
  index = index%SINE_MOD;
  sint = c_sine_table+index;
  return ((*sint + (frac*(*(sint+1)-*sint))));
}


/* -------------------------------- SGI Audio -------------------------------- */
#ifdef SGI

#include <audio.h>

static ALconfig rt_config;
static ALport rt_audio_p;

static void check_quad (int channels)
{
  long sr[2];
  /* if quad, make sure we are set up for it, else make sure we aren't (perhaps the latter is unnecessary) */
  sr[0] = AL_CHANNEL_MODE;
  ALgetparams(AL_DEFAULT_DEVICE,sr,2);
  if ((channels == 4) && (sr[1] != AL_4CHANNEL))
    {
      sr[1] = AL_4CHANNEL;
      ALsetparams(AL_DEFAULT_DEVICE,sr,2);
    }
  else
    {
      if ((channels != 4) && (sr[1] != AL_STEREO))
	{
	  sr[1] = AL_STEREO;
	  ALsetparams(AL_DEFAULT_DEVICE,sr,2);
	}
    }
}

static int open_dac (int srate, int channels)
{
  long sr[2];
  unsigned size = 4096;
  lock_recording_audio();
  sr[0]=AL_OUTPUT_RATE;
  sr[1]=srate;
  ALsetparams(AL_DEFAULT_DEVICE,sr,2);
  rt_config = ALnewconfig(); 
  ALsetsampfmt(rt_config,AL_SAMPFMT_TWOSCOMP);
  ALsetwidth(rt_config,AL_SAMPLE_16); 
  ALsetchannels(rt_config,channels);
  dac_chans = channels;
  check_quad(channels);
  ALsetqueuesize(rt_config,size);
  rt_audio_p = ALopenport("dac","w",rt_config);
  dac_running = 1;
  return(0);
}

void close_dac (void)
{
  while (ALgetfilled(rt_audio_p) > 0) sginap(1);
  ALcloseport(rt_audio_p);
  ALfreeconfig(rt_config);
  dac_running = 0;
  unlock_recording_audio();
}

void write_dac(short *buf, int samps, int cadr)
{
  ALwritesamps(rt_audio_p,buf,samps);
}

#endif

/* -------------------------------- Sun Audio -------------------------------- */
/* borrowed from xwave/src/audio.c primarily, with glances at sox/sunaudio.c and AFsp's SunPlay.c */

#ifdef SUN

#include <sys/audioio.h>
/* this may also be sun/audioio.h (not SYSV?) */

#define DAC_NAME "/dev/audio"

/* Solaris 2.5.1 ultra sparc has:
	/dev/audio -> /dev/sound/0
	/dev/audioctl -> /dev/sound/0ctl
	there is no /dev/dsp
*/

#define AUDIODEV_ENV "AUDIODEV"

static int audio_fd = 0;

void close_dac (void) 
{
  close(audio_fd);
  dac_running = 0;
  /* if we get input to work (snd-xrec.c), remember to lock/unlock recorder */
}

static int open_dac (int srate, int channels) 
{
  struct audio_info sfd_info;
  char *dev_name;

  /* apparently the default is "/dev/audio", but this can be overriden by AUDIODEV_ENV? */
  if (getenv(AUDIODEV_ENV) != NULL) dev_name = getenv(AUDIODEV_ENV); else dev_name = DAC_NAME;

  audio_fd = open(dev_name,O_WRONLY,0);           /* WRONLY used by xwave and SunPlay */
  if (audio_fd == -1) {fprintf(stderr,"can't open %s",dev_name); return(-1);}
  dac_running = 1;
  dac_chans = channels;
  AUDIO_INITINFO(&sfd_info);                      /* initialize */
  ioctl(audio_fd,AUDIO_GETINFO,&sfd_info);        /* is this redundant? */
  /* there's alo sfd_info.play.gain */
  sfd_info.play.sample_rate = srate;              /* what's available here? */
  sfd_info.play.channels = channels;
  sfd_info.play.precision = 16;                   /* also 8 */
  sfd_info.play.encoding = AUDIO_ENCODING_LINEAR; /* also _ULAW and _ALAW */
  ioctl(audio_fd, AUDIO_SETINFO, &sfd_info);
}

void write_dac(short *buf, int samps, int cadr) 
{
  write(audio_fd,buf,samps*2);                    /* also read */
}

#endif


#if HAVE_OSS

/* -------------------------------- Open Sound Systems (Voxware) Audio -------------------------------- */
/* 
 * borrowed from code by Michael Edwards 
 */

#include <sys/ioctl.h>

#ifdef LINUX
  #include <sys/soundcard.h>
#else
  #include <soundcard.h>
#endif

#define DAC_NAME "/dev/dsp"
#define FRAGMENTS 2
#define FRAGMENT_SIZE 10

static int audio_fd;

static int open_dac (int srate, int channels)
{
  int format,stereo,buffer_info;
  format = AFMT_S16_LE;
  dac_chans = channels;
  if (channels == 2) stereo = 1; else stereo = 0;
  if ((audio_fd = open(DAC_NAME,O_WRONLY,0)) == -1) return(-1);
  /* ioctl(audio_fd,SNDCTL_DSP_RESET,0); */
  buffer_info = (FRAGMENTS<<16) | (FRAGMENT_SIZE);
  if (ioctl(audio_fd,SNDCTL_DSP_SETFRAGMENT,&buffer_info) == -1) {fprintf(stdout,"could not reset fragment size!"); fflush(stdout);}
  /* choose format (always 16-bit linear here), srate, chans */
  if (ioctl(audio_fd,SNDCTL_DSP_SETFMT,&format) == -1) {close(audio_fd); return(-2);}
  if (format != AFMT_S16_LE) {close(audio_fd); return(-3);}
  if (ioctl(audio_fd,SNDCTL_DSP_STEREO,&stereo) == -1) {close(audio_fd); return(-4);}
  if ((channels == 2) && (stereo == 0)) {close(audio_fd); return(-5);}
  /* what srates are available in this case? */
  if (ioctl(audio_fd,SNDCTL_DSP_SPEED,&srate) == -1) {close(audio_fd); return(-6);}
  dac_running = 1;
  return(0);
}

void close_dac (void)
{
  close(audio_fd);
  dac_running = 0;
}

void write_dac(short *buf, int samps, int cadr)
{
  write(audio_fd,buf,samps*2);
}
#endif


#if defined(NEXT) || defined(BEOS)
static int open_dac (int srate, int channels) {}
void close_dac (void) {}
void write_dac(short *buf, int samps, int cadr) {}
#endif

/* -------------------------------- end special case dac stuff -------------------------------- */


static int play_list_members = 0;
static int max_active_slot = -1;

#define half_pi_flt .0000479369
#define two_pi_flt .0000958738

typedef struct {
  int s20;
  int s50;
  int rmp;
  float amp;
  int len;
  int cur_out;
  int cur_in;
  int in_spd;
  int ctr;
  int out_spd;
  float *b;
  int *buf;
  int bufptr;
  int buflen;
  int bufbeg;
} spd_info;

static spd_info *make_expand(snd_info *sp,float sampling_rate,float initial_ex)
{
  spd_info *spd;
  float segment_length = sp->exp_seglen;
  float ramp_time = sp->exp_ramp;
  float segment_scaler = .6;
  float output_hop = sp->exp_hop;
  float accuracy = 10.0;
  spd = (spd_info *)calloc(1,sizeof(spd_info));
  spd->cur_out = 0;
  spd->len = ceil(segment_length * sampling_rate);
  spd->rmp = ramp_time * spd->len;
  spd->amp = segment_scaler;
  spd->out_spd = output_hop * sampling_rate;
  spd->in_spd = (float)spd->out_spd / initial_ex;
  spd->s20 = sampling_rate/(accuracy*20);
  spd->s50 = sampling_rate/(accuracy*50);
  spd->ctr = 0;
  spd->b = (float *)calloc(spd->len,sizeof(float));
  spd->buflen = spd->len;
  spd->bufptr = 0;
  spd->buf = (int *)calloc(spd->buflen,sizeof(int));
  spd->bufbeg = 0;
  spd->cur_in = 0;
  return(spd);
}
static void free_spd_info(spd_info *spd)
{
  if (spd)
    {
      if (spd->b) free(spd->b);
      if (spd->buf) free(spd->buf);
      free(spd);
    }
}


typedef struct {
  int order;
  float *a;            /* coeffs */
  float **d;           /* chan state(s)? */
} flt_info;

typedef struct {
  int loc,size;
  float scaler;
  float *line;
} cmb;

typedef struct {
  int loc,size;
  float sf,sb;
  float *line;
} alp;

typedef struct {
  float a0,b1,y1;
} op;

typedef struct {
  cmb *comb1,*comb2,*comb3,*comb4,*comb5,*comb6;
  alp *allpass1,*allpass2,*allpass3,*allpass4,*allpass5,*allpass6,*allpass7,*allpass8;
  op *onep;
} rev_info;

static rev_info *global_reverb = NULL;
static int global_reverbing = 0;

typedef struct {
  float cur_index;
  float cur_amp;
  float cur_srate;
  float cur_exp;
  float cur_rev; /* rev scaler -- len is set at initialization */
  float contrast_amp;
  int expanding,reverbing,filtering; /* these need lots of preparation, so they're noticed only at the start */
  int chans,chan;
  int slot;
  int *lst; 
  int *nxt;
  float *x;
  int *vals;
  float *fvals;
  float rev_in;
  snd_fd **chn_fds;
  spd_info **spds;
  flt_info *flt;
  region_info *ri;
  snd_info *sp; /* needed to see button callback changes etc */
} dac_info;

static void free_flt(dac_info *dp) 
{
  /* was a no-op?? 19-Dec-97 */
  flt_info *f;
  int i;
  f = dp->flt;
  if (f)
    {
      free(f->a);
      if (f->d)
	{
	  for (i=0;i<dp->chans;i++) free(f->d[i]);
	  free(f->d);
	}
      free(f);
      dp->flt = NULL;
    }
}

static rev_info *make_reverb(snd_info *sp, float sampling_rate, int chans);
static flt_info *design_filter(int order, float *env);

static flt_info *load_filter(int order, env *e)
{
  flt_info *f;
  int i;
  if ((e->pts*2) < order) order = e->pts*2;
  f = (flt_info *)calloc(1,sizeof(flt_info));
  f->order = order;
  f->a = (float *)calloc(order,sizeof(float));
  for (i=0;i<order;i++) f->a[i] = e->data[i];
  return(f);
}

static void free_dac_info (dac_info *dp)
{
  int i;
  free(dp->lst);
  free(dp->nxt);
  free(dp->x);
  free(dp->vals);
  free(dp->fvals);
  for (i=0;i<dp->chans;i++)
    {
      free_snd_fd(dp->chn_fds[i]);
    }
  free(dp->chn_fds);
  if (dp->spds) 
    {
      for (i=0;i<dp->chans;i++)
	{
	  free_spd_info(dp->spds[i]);
	}
      free(dp->spds);
    }
  if (dp->flt) free_flt(dp);
  free(dp);
}

float list_interp(float x, float *e, int pts)
{
  if (pts == 0) return(0.0);
  if ((x <= e[0]) || (pts == 1)) return(e[1]);
  if (e[2] > x)
    {
      if (e[1] == e[3]) return(e[1]);
      return(e[1]+(x-e[0])*(e[3]-e[1])/(e[2]-e[0]));
    }
  return(list_interp(x,(float *)(e+2),pts-1));
}


static dac_info *make_dac_info(snd_info *sp, int chans, snd_fd **fds)
{
  dac_info *dp;
  int i;
  flt_info *f;
  float last_x,step,x;
  int order;
  env *e;
  float *data = NULL;
  dp = (dac_info *)calloc(1,sizeof(dac_info));
  dp->lst = (int *)calloc(chans,sizeof(int));
  dp->nxt = (int *)calloc(chans,sizeof(int));
  dp->vals = (int *)calloc(chans,sizeof(int));
  dp->fvals = (float *)calloc(chans,sizeof(float));
  dp->x = (float *)calloc(chans,sizeof(float));
  dp->chan = 0;
  if (sp)
    {
      dp->expanding = sp->expanding;
      dp->filtering = sp->filtering;
      dp->reverbing = sp->reverbing;
      dp->contrast_amp = sp->contrast_amp;
      if (dp->expanding) 
	{
	  dp->spds = (spd_info **)calloc(chans,sizeof(spd_info *));
	  for (i=0;i<chans;i++)
	    {
	      dp->spds[i] = make_expand(sp,(float)snd_SRATE(sp),sp->expand);
	    }
	}
      if (dp->reverbing)
	{
	  reverb_decay = sp->revlen;
	  if (!global_reverb) global_reverb = make_reverb(sp,(float)snd_SRATE(sp),chans);
	}
      if (dp->filtering)
	{
	  order = sp->filter_order;
	  e = sp->filter_env;
	  if (!e) dp->filtering = 0;
	  else
	    {
	      if (sp->filter_is_envelope)
		{
		  data = (float *)calloc(order,sizeof(float));
		  last_x = e->data[(e->pts-1)*2];
		  step = last_x/((float)order-1);
		  for (i=0,x=0.0;i<order;i++,x+=step) data[i] = list_interp(x,e->data,e->pts);
		  f = design_filter(order,data);
		  free(data);
		}
	      else f = load_filter(order,e);
	      f->d = (float **)calloc(chans,sizeof(float *));
	      for (i=0;i<chans;i++) f->d[i] = (float *)calloc(order,sizeof(float));
	      dp->flt = f;
	    }
	}
    }
  dp->chn_fds = fds;
  dp->sp = sp;
  dp->chans = chans;
  return(dp);
}

static int dac_max_sounds = 0;
static dac_info **play_list = NULL;

void stop_playing(void *dp1)
{
  snd_info *sp;
  int i;
  chan_info *ncp;
  dac_info *dp = (dac_info *)dp1;
  if (!dp1) return;
  sp = dp->sp;
  if (sp) 
    {
      sp->playing_mark = NULL;
      sp->playing = NULL;
      if (sp->cursor_follows_play)
	{
	  for (i=0;i<sp->nchans;i++) 
	    {
	      ncp = sp->chans[i];
	      handle_cursor(ncp,cursor_moveto(ncp,ncp->original_cursor));
	    }
	}
    }
  if (play_list) play_list[dp->slot] = NULL;
  play_list_members--;
  if (sp) reflect_play_stop(sp); else {reflect_play_region_stop(dp->ri); free(dp->ri); dp->ri = NULL;}
  if (dp->slot == max_active_slot) max_active_slot--;
  free_dac_info(dp);
}

static int find_slot_to_play(snd_state *ss)
{
  int i;
  for (i=0;i<ss->max_sounds;i++) {if (!play_list[i]) return(i);}
  return(-1);
}

void start_playing(void *ptr, int start)
{
  int slot,chans = 0,i,direction,beg = 0,chan,channels;
  dac_info *dp;
  snd_info *sp;
  snd_state *ss = NULL;
  dac_manager *dac_m;
  chan_info *cp,*ncp;
  region_info *ri;
  snd_fd **fds;
  c_init_sine();
  sp = NULL;
  cp = NULL;
  ri = NULL;
  chan = 0;
  channels = 1;
  switch (snd_pointer_type(ptr))
    {
    case SND_INFO:
      sp = (snd_info *)ptr;
      ss = sp->state;
      if (sp->grouping)
	{
	  channels = sp->grouping>>8;  /* DAC channels, not current "sound" channels */
	  chan = sp->grouping&0xff;
	}
      chans = sp->nchans;
      break;
    case CHAN_INFO: 
      cp = (chan_info *)ptr; 
      sp = cp->sound;
      ss = sp->state;
      chans = 1; 
      break;
    case REGION_INFO:
      ri = (region_info *) ptr;
      if (!(ri->rg)) sp = region_sound(ri->n); else sp=NULL;
      ss = (snd_state *)(ri->ss);
      chans = region_chans(ri->n);
      break;
    }
  if ((sp) && (sp->cursor_follows_play)) /* sp can be nil if ptr is region */
    {
      for (i=0;i<sp->nchans;i++) 
	{
	  ncp = sp->chans[i];
	  ncp->original_cursor = ncp->cursor;
	  handle_cursor(ncp,cursor_moveto(ncp,start));
	}
    }
  if (dac_max_sounds < ss->max_sounds)
    {
      if (!play_list) play_list = (dac_info **)calloc(ss->max_sounds,sizeof(dac_info *));
      else 
	{
	  play_list = (dac_info **)realloc(play_list,ss->max_sounds*sizeof(dac_info *));
	  for (i=dac_max_sounds;i<ss->max_sounds;i++) play_list[i] = NULL;
	}
      dac_max_sounds = ss->max_sounds;
    }
  slot = find_slot_to_play(ss);
  if (slot == -1) return;
  play_list_members++;
  fds = (snd_fd **)calloc(chans,sizeof(snd_fd *));
  dp = make_dac_info(sp,chans,fds);
  if (chans > channels) channels = chans;
  dp->chan = chan;
  play_list[slot] = dp;
  dp->slot = slot;
  if (max_active_slot < slot) max_active_slot = slot;
  if (sp)
    {
      sp->playing = dp;
      dp->cur_srate = sp->srate*sp->play_direction;
      dp->cur_amp = sp->amp;
      dp->cur_index = sp->contrast;
      dp->cur_exp = sp->expand;
      dp->cur_rev = sp->revscl;
      if (sp->play_direction == 1) 
	{
	  direction = READ_FORWARD; 
	  beg = start;
	}
      else 
	{
	  direction = READ_BACKWARD;
	  if (start == 0) beg = current_ed_samples(sp->chans[0])-1; else beg = start;
	}
    }
  else direction = READ_FORWARD;
  switch (snd_pointer_type(ptr))
    {
    case SND_INFO:  
      for (i=0;i<chans;i++)
	{
	  cp = sp->chans[i];
	  fds[i] = init_sample_read(beg,cp,direction);
	}
      break;

    case CHAN_INFO: 
      fds[0] = init_sample_read(beg,cp,direction);
      break;

    case REGION_INFO:
      ri->dp = dp;
      dp->ri = ri;
      for (i=0;i<chans;i++)
	{
	  fds[i] = init_region_read(ss,ri->n,i,direction);
	}
      break;
    }
  if (!dac_running)
    {
      ss->play_start_time = 0; /* not redundant */
      ss->play_start_time = main_TIME(ss);
      if (sp) start_record_and_replay(ss,sp);
      dac_m = (dac_manager *)calloc(1,sizeof(dac_manager));
      dac_m->slice = 0;
      dac_m->ss = ss;
      if (sp) dac_m->srate = snd_SRATE(sp); else dac_m->srate = snd_SRATE(any_selected_sound(ss));
      dac_decay = ss->reverb_decay * dac_m->srate;
      dac_m->channels = channels;
      set_play_in_progress(ss,dac_m);
    }
}

#define ADD_NEXT_SAMPLE(val,sf)  {if (sf->data > sf->last) val += next_sound(sf); else val += (*sf->data++);}
#define SCALE_NEXT_SAMPLE(val,sf,amp)  {if (sf->data > sf->last) val += (amp*next_sound(sf)); else val += (amp*(*sf->data++));}

static void speed_1(dac_info *dp, float sr, int chan)
{
  int move,i;
  if (sr > 0.0) 
    {
      dp->vals[chan] = dp->lst[chan] + dp->x[chan] * (dp->nxt[chan] - dp->lst[chan]);
      dp->x[chan] += sr;
      move = (int)(dp->x[chan]);
      if (move != 0)
	{
	  dp->x[chan] -= move;
	  for (i=0;i<move;i++)
	    {
	      dp->lst[chan] = dp->nxt[chan];
	      NEXT_SAMPLE(dp->nxt[chan],dp->chn_fds[chan]);
	    }
	}
    }
  else
    {
      dp->vals[chan] = dp->lst[chan] + dp->x[chan] * (dp->nxt[chan] - dp->lst[chan]);
      dp->x[chan] -= sr;
      move = (int)(dp->x[chan]);
      if (move != 0)
	{
	  dp->x[chan] -= move;
	  for (i=0;i<move;i++)
	    {
	      dp->lst[chan] = dp->nxt[chan];
	      PREVIOUS_SAMPLE(dp->nxt[chan],dp->chn_fds[chan]);
	    }
	}
    }
}

static void speed(dac_info *dp, float sr)
{
  int chan;
  for (chan=0;chan<dp->chans;chan++) speed_1(dp,sr,chan);
}

static void load_speed_buf(dac_info *dp, float sr, int beg, int end, spd_info *spd, int chan)
{
  int i;
  for (i=beg;i<end;i++)
    {
      speed_1(dp,sr,chan);
      spd->buf[spd->bufptr] = dp->vals[chan];
      spd->bufptr++;
      if (spd->bufptr >= spd->buflen) {spd->bufbeg += spd->buflen; spd->bufptr = 0;}
    }
}

static unsigned long randx = 1;
#define INVERSE_MAX_RAND 0.0000305185

void c_srand(int val) {randx = val;} /* not static because declared in cmus_prototypes.h */

static int ci_frandom(int amp)
{
  int val;
  randx=randx*1103515245 + 12345;
  val=(unsigned int)(randx >> 16) & 32767;
  return(amp * (((float)val)*INVERSE_MAX_RAND));
}

static void expand(dac_info *dp, float sr, float ex)
{ /* from Mixer, used in "Leviathan", 1986 */
  spd_info *spd;
  snd_fd *sf;
  int end,i,j,jump,chan,speeding,loc,trig_time,iloc,k,ramp_time;
  float curamp,incr,saved_val;
  snd_info *sp;
  sp=dp->sp;
  speeding = ((sp->play_direction != 1) || (sp->srate != 1.0) || (dp->cur_srate != 1.0));
  for (chan=0;chan<dp->chans;chan++)
    {
      spd = dp->spds[chan];
      dp->vals[chan] = spd->b[spd->ctr];
      spd->ctr++;
      if (spd->ctr >= spd->cur_out)
	{
	  saved_val = dp->vals[chan];
	  end = spd->len - spd->cur_out;
	  for (i=0,j=spd->cur_out;i<end;i++,j++) spd->b[i] = spd->b[j];
	  for (i=end;i<spd->len;i++) spd->b[i] = 0.0;
	  ramp_time = spd->rmp;
	  incr = spd->amp / (float)ramp_time;  /* precomputable */
	  trig_time = spd->len - ramp_time;
	  if (spd->cur_out > 0)
	    {
	      jump = spd->in_spd + ci_frandom(spd->s20);
	      spd->cur_in += jump;
	    }
	  else jump = spd->buflen;
	  sf = dp->chn_fds[chan];
	  if (speeding) 
	    load_speed_buf(dp,sr,0,jump,spd,chan);
	  else 
	    {
	      for (i=0;i<jump;i++) 
		{
		  NEXT_SAMPLE(spd->buf[spd->bufptr],sf); 
		  spd->bufptr++;
		  if (spd->bufptr >= spd->buflen) {spd->bufbeg += spd->buflen; spd->bufptr = 0;}
		}
	    }
	  for (i=0,k=0,curamp=0.0,loc=spd->cur_in;i<spd->len;i++,k++,loc++)
	    {
	      if (i<ramp_time) curamp+=incr; else if (i>trig_time) curamp-=incr;
	      iloc = loc - spd->bufbeg;
	      if (iloc >= 0) spd->b[k] += curamp * (float)(spd->buf[iloc]);
	      else spd->b[k] += curamp * (float)(spd->buf[iloc+spd->buflen]);
	    }
	  spd->ctr -= spd->cur_out;
	  spd->cur_out = spd->out_spd + ci_frandom(spd->s50);
	  spd->in_spd = (float)spd->out_spd / ex;
	  dp->vals[chan] = saved_val;
	}
    }
}

static flt_info *design_filter(int order, float *env)
{ /* env = evenly sampled freq response, has order samples */
  int n,m,n2,i,j,jj;
  float am,q,xt = 0.0,dc;
  flt_info *f;
  f = (flt_info *)calloc(1,sizeof(flt_info));
  f->order = order;
  f->a = (float *)calloc(order,sizeof(float));
  n=order;
  m=(n+1)/2;
  am=0.5*(n+1);
  q=two_pi/(float)n;
  n2=n/2;
  dc = env[0];
  if (dc == 0.0)
    {
      for (j=0,jj=n-1;j<m;j++,jj--)
	{
	  xt=env[0]*0.5;
	  for (i=1;i<m;i++)
	    {
#ifdef NEXT
	      xx = env[i]; /* UNBELIEVABLE!!!! */
	      xt+=(xx*cos(q*(am-j-1)*i));
#else
	      xt += (env[i]*cos(q*(am-j-1)*i));
#endif
	    }
	  f->a[j]=2.0*xt/(float)n;
	  f->a[jj]=f->a[j];
	}
    }
  else
    {
      for (j=0,jj=n-1;j<m;j++,jj--)
	{
	  xt = 0.0;
	  for (i=0;i<n2;i++)
	    {
#ifdef NEXT
	      xx = env[i];
	      xt += (xx*cos(q*(am-j-1)*(i+.05)));
#else
	      xt += (env[i]*cos(q*(am-j-1)*(i+.05)));
#endif
	    } 
#ifdef NEXT
	  if (am != m)
	    {
	      xx = env[m-1];
	      xt += (xx*0.5*cos(one_pi*(am-j-1)));
	    }
#else
	  if (am != m) xt += (env[m-1]*0.5*cos(one_pi*(am-j-1)));
#endif
	}
      f->a[j] = 2.0*xt/(float)n;
      f->a[jj]=f->a[j];
    }
  return(f);
}

void make_filter(int order, float *env, float *a)
{
  /* for filters elsewhere that don't use local flt_info struct */
  flt_info *f;
  int i;
  f = design_filter(order,env);
  for (i=0;i<order;i++) a[i] = f->a[i];
  free(f->a);
  free(f);
}

static void filter(dac_info *dp)
{
  /* straight FIR filter, each sound has its own filter state/coeffs etc */
  /* input in dp->fvals, output also */
  int chan,order,k;
  float x;
  flt_info *f;
  float *a,*d;
  f=dp->flt;
  a = f->a;
  order = f->order;
  for (chan=0;chan<dp->chans;chan++)
    {
      d = (float *)(f->d[chan]);
      x=0.0; 
      d[0]=dp->fvals[chan];
      for (k=order-1;k>0;k--) 
	{
	  x+=d[k]*a[k]; 
	  d[k]=d[k-1];
	} 
      x+=d[0]*a[0]; 
      dp->fvals[chan]=x;
    }
}

#define comb(in,out,scaler,loc,size,line) out=line[loc]; line[loc]=in + out*scaler; loc++; if (loc == size) loc=0;
#define allpass(in,out,sf,sb,temp,loc,size,line) temp=in+sb*line[loc]; out=line[loc]+sf*temp; line[loc]=temp; loc++; if (loc == size) loc=0;
#define one_pole(a0,x,b1,y) y = a0*x-b1*y

static cmb *make_comb(float scaler, int len)
{
  cmb *c;
  c = (cmb *)calloc(1,sizeof(cmb));
  c->line = (float *)calloc(len,sizeof(float));
  c->scaler = scaler;
  c->loc = 0;
  c->size = len;
  return(c);
}

static alp *make_allpass(float sb, float sf, int len)
{
  alp *a;
  a = (alp *)calloc(1,sizeof(alp));
  a->size = len;
  a->sf = sf;
  a->sb = sb;
  a->loc = 0;
  a->line = (float *)calloc(len,sizeof(float));
  return(a);
}

static op *make_one_pole(float a0, float b1)
{
  op *o;
  o=(op *)calloc(1,sizeof(op));
  o->a0 = a0;
  o->b1 = b1;
  o->y1 = 0.0;
  return(o);
}

static int prime (int num)
{
  int lim,i;
  if (num == 2) return(1);
  if ((num%2) == 1)
    {
      lim = sqrt(num);
      for (i=3;i<lim;i+=2)
	{
	  if ((num%i) == 0) return(0);
	}
      return(1);
    }
  return(0);
}

static int get_prime(int num)
{
  int i;
  if ((num%2) == 1)
    i=num;
  else i=num+1;
  while (!(prime(i))) {i+=2;}
  return(i);
}

static int base_dly_len[15] = {1433, 1601, 1867, 2053, 2251, 2399, 347, 113, 37, 59, 53, 43, 37, 29, 19};
static int dly_len[15];

static rev_info *make_reverb(snd_info *sp, float sampling_rate, int chans)
{ /* Mike McNabb's nrev from Mus10 days (ca. 1978) */
  float srscale;
  int i;
  rev_info *r;
  srscale = reverb_decay*sampling_rate/25641.0;
  reverb_factor = sp->revfb;
  lp_coeff = sp->revlp;
  revchans = chans;
  revdecay = 0;
  global_reverbing = 1;
  for (i=0;i<15;i++)
    {
      dly_len[i] = get_prime(srscale*base_dly_len[i]);
    }
  r=(rev_info *)calloc(1,sizeof(rev_info));
  r->comb1=make_comb(.822*reverb_factor,dly_len[0]);
  r->comb2=make_comb(.802*reverb_factor,dly_len[1]);
  r->comb3=make_comb(.773*reverb_factor,dly_len[2]);
  r->comb4=make_comb(.753*reverb_factor,dly_len[3]);
  r->comb5=make_comb(.753*reverb_factor,dly_len[4]);
  r->comb6=make_comb(.733*reverb_factor,dly_len[5]);
  r->onep=make_one_pole(lp_coeff,lp_coeff-1.0);
  r->allpass1=make_allpass(-0.700,0.700,dly_len[6]);
  r->allpass2=make_allpass(-0.700,0.700,dly_len[7]);
  r->allpass3=make_allpass(-0.700,0.700,dly_len[8]);
  r->allpass4=make_allpass(-0.700,0.700,dly_len[9]);
  r->allpass5=make_allpass(-0.700,0.700,dly_len[11]);
  if (chans > 1)
    { 
      r->allpass6=make_allpass(-0.700,0.700,dly_len[12]);
      if (chans > 2)
	{
	  r->allpass7=make_allpass(-0.700,0.700,dly_len[13]);
	  r->allpass8=make_allpass(-0.700,0.700,dly_len[14]);
	}
    }
  return(r);
}

static void free_comb(cmb *c)
{
  if (c)
    {
      free(c->line);
      free(c);
    }
}

static void free_allpass(alp *c)
{
  if (c)
    {
      free(c->line);
      free(c);
    }
}

static void free_one_pole(op *o) 
{
  if (o) free(o);
}

static void free_reverb(rev_info *r)
{
  global_reverbing = 0;
  if (r)
    {
      free_comb(r->comb1);
      free_comb(r->comb2);
      free_comb(r->comb3);
      free_comb(r->comb4);
      free_comb(r->comb5);
      free_comb(r->comb6);
      free_one_pole(r->onep);
      free_allpass(r->allpass1);
      free_allpass(r->allpass2);
      free_allpass(r->allpass3);
      free_allpass(r->allpass4);
      free_allpass(r->allpass5);
      free_allpass(r->allpass6);
      free_allpass(r->allpass7);
      free_allpass(r->allpass8);
    }
}

static void reverb(rev_info *r, float inp, short *outs, int chans)
{
  float rin,rout,temp,c1,c2,c3,c4,c5,c6;
  cmb *cm1,*cm2,*cm3,*cm4,*cm5,*cm6;
  alp *al1,*al2,*al3,*al4,*al5,*al6,*al7,*al8;
  op *o1;
  rin = volume*inp;
  cm1=r->comb1;
  cm2=r->comb2;
  cm3=r->comb3;
  cm4=r->comb4;
  cm5=r->comb5;
  cm6=r->comb6;
  al1=r->allpass1;
  al2=r->allpass2;
  al3=r->allpass3;
  al4=r->allpass4;
  al5=r->allpass5;
  al6=r->allpass6;
  al7=r->allpass7;
  al8=r->allpass8;
  o1=r->onep;
  comb(rin,c1,cm1->scaler,cm1->loc,cm1->size,cm1->line);
  comb(rin,c2,cm2->scaler,cm2->loc,cm2->size,cm2->line);
  comb(rin,c3,cm3->scaler,cm3->loc,cm3->size,cm3->line);
  comb(rin,c4,cm4->scaler,cm4->loc,cm4->size,cm4->line);
  comb(rin,c5,cm5->scaler,cm5->loc,cm5->size,cm5->line);
  comb(rin,c6,cm6->scaler,cm6->loc,cm6->size,cm6->line);
  rin=c1+c2+c3+c4+c5+c6;
  allpass(rin,rout,al1->sf,al1->sb,temp,al1->loc,al1->size,al1->line);
  allpass(rout,rin,al2->sf,al2->sb,temp,al2->loc,al2->size,al2->line);
  allpass(rin,rout,al3->sf,al3->sb,temp,al3->loc,al3->size,al3->line);
  one_pole(o1->a0,rout,o1->b1,o1->y1);
  allpass(o1->y1,rin,al4->sf,al4->sb,temp,al4->loc,al4->size,al4->line);
  allpass(rin,rout,al5->sf,al5->sb,temp,al5->loc,al5->size,al5->line);
  outs[0] += (short)(clm_sndfix * rout);
  if (chans > 1)
    {
      allpass(rin,rout,al6->sf,al6->sb,temp,al6->loc,al6->size,al6->line);
      outs[1] += (short)(clm_sndfix * rout);
      if (chans > 2)
	{
	  allpass(rin,rout,al7->sf,al7->sb,temp,al7->loc,al7->size,al7->line);
	  outs[2] += (short)(clm_sndfix * rout);
	  allpass(rin,rout,al8->sf,al8->sb,temp,al8->loc,al8->size,al8->line);
	  outs[3] += (short)(clm_sndfix * rout);
	}
    }
}

static void contrast (dac_info *dp, float amp, float index)
{
  int i;
  float val;
  for (i=0;i<dp->chans;i++)
    {
      val = dp->vals[i]*half_pi_flt*dp->contrast_amp;
      dp->fvals[i] = amp * c_sin(val + index*c_sin(val*4.0));
    }
}

#if 0
static void load_vals(dac_info *dp)
{
  /* in lieu of any other input source, grab next samples */
  int i;
  for (i=0;i<dp->chans;i++)
    {
      NEXT_SAMPLE(dp->vals[i],dp->chn_fds[i]);
    }
}
#endif

static void load_fvals(dac_info *dp, float amp)
{
  int i;
  for (i=0;i<dp->chans;i++)
    {
      dp->fvals[i] = (clm_sndflt * amp * dp->vals[i]);
    }
}

static void unload_fvals(dac_info *dp)
{
  /* unfloat fvals into vals */
  int i;
  for (i=0;i<dp->chans;i++)
    {
      dp->vals[i] = (clm_sndfix * dp->fvals[i]);
    }
}

static void apply_changes(dac_info *dp, snd_info *sp, float amp, float sr, float index, float ex, float revscl)
{
  if (dp->expanding) expand(dp,sr,ex);
  else speed(dp,sr);
  if (sp->contrasting) contrast(dp,amp,index);
  else load_fvals(dp,amp);
  if (dp->filtering) filter(dp);
  if (dp->reverbing) dp->rev_in = dp->fvals[0]*revscl;
  unload_fvals(dp);
}



#define NO_CHANGE 0
#define JUST_AMP 1
#define JUST_SPEED 2
#define ALL_CHANGES 3

static int choose_dac_op (dac_info *dp, snd_info *sp)
{
  if (!sp) return(NO_CHANGE);
  if ((dp->expanding) || (dp->filtering) || (dp->reverbing) || (sp->contrasting)) 
    return(ALL_CHANGES);
  else
    {
      if ((sp->play_direction != 1) || (sp->srate != 1.0) || (dp->cur_srate != 1.0))
	return(JUST_SPEED);
      else
	{
	  if ((sp->amp == dp->cur_amp) && (sp->amp == 1.0))
	    return(NO_CHANGE);
	  else return(JUST_AMP);
	}
    }
}

#define CURSOR_UPDATE_INTERVAL 1024
static int cursor_time;
/* can't move cursor on each dac buffer -- causes clicks */

static int dac_pausing = 0;
void toggle_dac_pausing(snd_state *ss) {dac_pausing = (!dac_pausing); play_button_pause(ss,dac_pausing);}
int play_in_progress(void) {return(play_list_members > 0);}

static int fill_dac(int write_ok)
{
  int i,j,k,m,dac_increments,len,cursor_change;
  float amp,incr,sr,sincr,ind,indincr,ex,exincr,rev,revincr;
  dac_info *dp;
  snd_info *sp;
  memset(dac_buffer,0,DAC_BUFFER_SIZE * sizeof(short));
  if (global_reverb) memset((char *)revin,0,DAC_BUFFER_SIZE * sizeof(float));
  dac_increments = DAC_BUFFER_SIZE / dac_chans;
  len = dac_increments;
  if (dac_pausing) 
    cursor_change = 0;
  else
    {
      cursor_time += len;
      cursor_change = (cursor_time >= CURSOR_UPDATE_INTERVAL);
      for (i=0;i<=max_active_slot;i++)
	{
	  if ((dp = (play_list[i])))
	    {
	      sp=dp->sp;
	      if ((sp) && (cursor_change) && (sp->cursor_follows_play)) 
		{
		  if (sp->syncing) 
		    handle_cursor(sp->chans[0],cursor_moveto(sp->chans[0],current_location(dp->chn_fds[0])));
		  else
		    {
		      for (m=0;m<sp->nchans;m++) 
			handle_cursor(sp->chans[m],cursor_moveto(sp->chans[m],current_location(dp->chn_fds[m])));
		    }
		}
	      switch (choose_dac_op(dp,sp))
		{
		  
		case NO_CHANGE:
		  /* simplest case -- no changes at all */
		  if (dp->chans == 1) 
		    {
		      for (j=0;j<DAC_BUFFER_SIZE;j+=dac_chans) {ADD_NEXT_SAMPLE(dac_buffer[j+dp->chan],dp->chn_fds[0]);}
		    }
		  else 
		    {
		      for (j=0;j<DAC_BUFFER_SIZE;j+=dac_chans) 
			{
			  for (k=0;k<dp->chans;k++) {ADD_NEXT_SAMPLE(dac_buffer[j+k],dp->chn_fds[k]);}
			}
		    }
		  break;

		case JUST_AMP:
		  /* sp->amp is current UI value, dp->cur_amp is current local value */
		  amp = dp->cur_amp;
		  incr = (sp->amp - amp) / (float)dac_increments;
		  if (dp->chans == 1) 
		    {
		      for (j=0;j<DAC_BUFFER_SIZE;j+=dac_chans,amp+=incr) {SCALE_NEXT_SAMPLE(dac_buffer[j+dp->chan],dp->chn_fds[0],amp);}
		    }
		  else 
		    {
		      for (j=0;j<DAC_BUFFER_SIZE;j+=dac_chans,amp+=incr) 
			{
			  for (k=0;k<dp->chans;k++) {SCALE_NEXT_SAMPLE(dac_buffer[j+k],dp->chn_fds[k],amp);}
			}
		    }
		  dp->cur_amp = amp;
		  break;

		case JUST_SPEED:
		  /* includes amp changes */
		  /* sp->srate is current UI value, dp->cur_srate is current local value */
		  amp = dp->cur_amp;
		  incr = (sp->amp - amp) / (float)dac_increments;
		  sr = dp->cur_srate;
		  sincr = (sp->srate*sp->play_direction - sr) / (float)dac_increments;
		  if ((sr != 0.0) || (sincr != 0.0))
		    {
		      for (j=0;j<DAC_BUFFER_SIZE;j+=dac_chans,amp+=incr,sr+=sincr) 
			{
			  speed(dp,sr);
			  for (k=0;k<dp->chans;k++) dac_buffer[j+k+dp->chan] += (amp*dp->vals[k]);
			}
		    }
		  dp->cur_srate = sr;
		  dp->cur_amp = amp;
		  break;

		case ALL_CHANGES:
		  amp = dp->cur_amp;
		  incr = (sp->amp - amp) / (float)dac_increments;
		  sr = dp->cur_srate;
		  sincr = (sp->srate*sp->play_direction - sr) / (float)dac_increments;
		  ind = dp->cur_index;
		  indincr = (sp->contrast - ind) / (float)dac_increments;
		  ex = dp->cur_exp;
		  exincr = (sp->expand - ex) / (float)dac_increments;
		  rev = dp->cur_rev;
		  revincr = (sp->revscl - rev) / (float)dac_increments;
		  for (j=0;j<DAC_BUFFER_SIZE;j+=dac_chans,amp+=incr,sr+=sincr,ind+=indincr,ex+=exincr,rev+=revincr) 
		    {
		      apply_changes(dp,sp,amp,sr,ind,ex,rev);
		      for (k=0;k<dp->chans;k++) dac_buffer[j+k+dp->chan] += dp->vals[k];
		      if (dp->reverbing) revin[j] += dp->rev_in;
		    }
		  dp->cur_srate = sr;
		  dp->cur_amp = amp;
		  dp->cur_rev = rev;
		  dp->cur_exp = ex;
		  dp->cur_index = ind;
		  break;
		}

	      /* check for EOF */
	      if (read_sample_eof(dp->chn_fds[0]))
		{
		  if (write_ok) 
		    stop_playing(dp);
		  else 
		    {
		      if (!global_reverb) 
			return(-len); /* stop apply without any reverb ringing */
		      else 
			{ /* let reverb ring through revdecay */
			  play_list_members = 0; 
			  max_active_slot = -1;
			}
		    }
		}
	    }
	}
      if (global_reverb) 
	{
	  for (i=0;i<DAC_BUFFER_SIZE;i+=dac_chans)
	    {
	      reverb(global_reverb,revin[i],(short *)(dac_buffer+i),revchans);
	    }
	  if (play_list_members == 0)
	    {
	      revdecay += DAC_BUFFER_SIZE;
	      if (revdecay > dac_decay) {global_reverbing=0; revdecay=0; if (!write_ok) return(-len);}
	    }
	}
    }
  if (write_ok) write_dac(dac_buffer,DAC_BUFFER_SIZE,0);
  if (cursor_change) cursor_time = 0;
  return(len);
}

int feed_dac(dac_manager *tm)
{
  /* return true when done */
  int err;
  switch (tm->slice)
    {
    case 0: /* start_dac, get first buffer, goto next step, return false */
      cursor_time = 0;
      err = open_dac(tm->srate,tm->channels);
      if (err) 
	{
	  snd_printf(tm->ss,snd_string_cant_play); 
	  max_active_slot = -1;
	  free(tm); 
	  return(TRUE);
	}
      fill_dac(1);
      lock_apply(tm->ss,NULL);
      tm->slice++;
      return(FALSE);
      break;
    case 1: /* get next buffer, feed dac, when play_list_members == 0 goto next step, return false */
      fill_dac(1);
      if ((!global_reverbing) && (play_list_members == 0)) tm->slice++; 
      return(FALSE);
      break;
    case 2: /* close dac, clear play_in_progress, unset play buttons? return true */
      close_dac();
      dac_pausing = 0;
      if (global_reverb) {free_reverb(global_reverb); global_reverb=NULL;}
      finish_record_and_replay();
      max_active_slot = -1;
      unlock_apply(tm->ss,NULL);
      free(tm);
      return(TRUE);
      break;
    }
  return(TRUE);
}


/* ---------------- support for Apply button (snd-apply.c) ---------------- */

void initialize_apply(snd_info *sp)
{
  int i,direction,beg;
  dac_info *dp;
  snd_state *ss;
  snd_fd **fds;
  c_init_sine();
  ss = sp->state;
  fds = (snd_fd **)calloc(sp->nchans,sizeof(snd_fd *));
  dp = make_dac_info(sp,sp->nchans,fds);
  max_active_slot = 0;
  if (!play_list) 
    {
      play_list = (dac_info **)calloc(ss->max_sounds,sizeof(dac_info *));
      dac_max_sounds = ss->max_sounds;
    }
  play_list[0] = dp;
  play_list_members = 1;
  sp->playing = dp;
  dp->cur_srate = sp->srate*sp->play_direction;
  dp->cur_amp = sp->amp;
  dp->cur_index = sp->contrast;
  dp->cur_exp = sp->expand;
  dp->cur_rev = sp->revscl;
  if (sp->play_direction == 1) 
    {
      direction = READ_FORWARD; 
      beg = 0;
    }
  else 
    {
      direction = READ_BACKWARD;
      beg = current_ed_samples(sp->chans[0])-1;
    }
  for (i=0;i<sp->nchans;i++)
    {
      fds[i] = init_sample_read(beg,sp->chans[i],direction);
    }
  dac_chans = sp->nchans;
}

void finalize_apply(snd_info *sp)
{
  play_list[0] = NULL;
  max_active_slot = -1;
  play_list_members = 0;
  free_dac_info((dac_info *)(sp->playing));
  sp->playing = NULL;
  if (global_reverb) {free_reverb(global_reverb); global_reverb=NULL;}
}

int run_apply(snd_info *sp, int ofd)
{
  int len;
#ifdef CLM_LITTLE_ENDIAN
  int i;
  unsigned char j;
  unsigned char *k;
#endif
  len = fill_dac(0); /* only place where fill_dac return value is used (negative => all done) */
#ifdef CLM_LITTLE_ENDIAN
  /* since we're writing NeXT 16-bit big endian temp files (see make_temp_header in snd-file.c) 
   * we have to translate the dac buffer before writing (it's little endian in this case)
   */
  k = (unsigned char *)dac_buffer;
  for (i=0;i<DAC_BUFFER_SIZE;i++,k+=2)
    {
      j=(*k);
      (*k) = (*(k+1));
      (*(k+1)) = j;
    }
#endif
  write(ofd,(char *)dac_buffer,DAC_BUFFER_SIZE*2); /* dac_buffer is short array (len is short_size/chans) */
  return(len);
}
