/*  
 *  This file is part of abc2ps, 
 *  Copyright (C) 1996-2007  Michael Methfessel
 *  See file abc2ps.c for details.
 */

#include <stdio.h>
#include <math.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>

#include "abc2ps.h"
#include "util.h"
#include "music.h"
#include "wav.h"
#include "playback.h"

#include "sox.h"

int samples_per_second;
int bits_per_sample;
int num_channels;

int active;            /* enable/disable au output */
int current_bar;       /* for more informative error messages */

void au_write_note (float t0, float tn, float tx, int hs, 
                    struct AU_QUALITY q, float amp, struct SBUF *sb);

#define NGC 10     /* max number of .. parts in gchord */
#define TGC 33     /* mac length of each part */

/* acc chords scheduling within one bar */
struct {
  char  gch[TGC];
  float tgc;
} gctab[NGC];

int ngctab;


/* --- accompaniment stuff --- */

int do_invert;

#define AU_NPMX 16

#define AU_ACC_L  1
#define AU_ACC_r  2
#define AU_ACC_q  3
#define AU_ACC_t  4
#define AU_ACC_R  5
#define AU_ACC_Q  6
#define AU_ACC_T  7

struct accpat {
  int totdur;              /* length of whole pattern, as sanity check */
  int del[AU_NPMX];        /* delay relative to last barline */
  int dur[AU_NPMX];        /* duration */
  int n;                   /* how many notes in the pattern */
  struct AU_QUALITY q;     /* audio quality */
  int type;                /* is bass or fifth or treble, see above */
  float amp;               /* amplitude */
  char  id[33];            /* identifier, for debug */
};

char acc_desc[81];

/* --- au_interp_gchrest --- */
/* returns chord notes as halfsteps relative to root, fifth in *f */
#define   _1     0
#define   _3     4
#define   _b3    3
#define   _5     7
#define   _b5    6
#define   _a5    8
#define   _7     11
#define   _b7    10
#define   _bb7   9
#define   _9     14
#define   _a9    15
#define   _b9    13
#define   _11    17
#define   _13    21
#define   _b13   20
#define   _4     5
#define   _6     9

int au_interp_gchrest (char *r, int h[], int *f)
{
  /* default fifth. Will be modified for altered chords. */
  *f=_5;   

  /* --- dominant chords */
  if (!strcmp(r,"7")) {                                 /* dominant 7 */
    h[0]=_3; h[1]=_5; h[2]=_b7; return 3; }
  
  if (!strcmp(r,"7b5") || !strcmp(r,"7#11")) {          /* 7b5 7#11 */
    h[0]=_3; h[1]=_b5; h[2]=_b7; *f=_b5; return 3; }

  if (!strcmp(r,"+")) {                                 /* + */
    h[0]=_3; h[1]=_a5; *f=_a5; return 2; }
  
  if (!strcmp(r,"7#5") || !strcmp(r,"7+5")) {           /* 7#5  7+5 */
    h[0]=_3; h[1]=_a5; h[2]=_b7; *f=_a5; return 3; }

  if (!strcmp(r,"7#5#11") || !strcmp(r,"7b5#5")) {      /* 7#5#11 7b5#5 */
    h[0]=_3; h[1]=_b5; h[2]=_b7; *f=_a5; return 3; }

  if (!strcmp(r,"7b9")) {                               /* 7b9 */
    h[0]=_3; h[1]=_b7; h[2]=_b9; return 3; }

  if (!strcmp(r,"7b9#5")) {                             /* 7b9#5 */
    h[0]=_3; h[1]=_b7; h[2]=_b9; *f=_a5; return 3; }

  if (!strcmp(r,"7#5b9")) {                             /* 7#5b9 */
    h[0]=_3; h[1]=_b7; h[2]=_a5; h[3]=_b9; return 4; }
  
  if (!strcmp(r,"9")) {                                 /* 9 */
    h[0]=_3; h[1]=_b7; h[2]=_9;  return 3; }

  if (!strcmp(r,"9#5")) {                               /* 9#5 */
    h[0]=_3; h[1]=_b7; h[2]=_9; *f=_a5; return 3; }
  
  if (!strcmp(r,"11")) {                                /* 11 */
    h[0]=_3; h[1]=_b7; h[2]=_9; h[3]=_11; return 4; }

  if (!strcmp(r,"9b5") || !strcmp(r,"9#11")) {          /* 9b5 9#11 */
    h[0]=_3; h[1]=_b7; h[2]=_9; *f=_5; return 3; }
  
  if (!strcmp(r,"7#9")) {                               /* 7#9 */
    h[0]=_3; h[1]=_b7; h[2]=_a9; return 3; }

  if (!strcmp(r,"13")) {                                /* 13 */
    h[0]=_3; h[1]=_b7; h[2]=_9; h[3]=_13; return 4; }

  if (!strcmp(r,"13b9")) {                              /* 13b9 */
    h[0]=_3; h[1]=_b7; h[2]=_b9; h[3]=_13; return 4; }

  if (!strcmp(r,"13#9")) {                              /* 13#9 */
    h[0]=_3; h[1]=_b7; h[2]=_a9; h[3]=_13; return 4; }

  if (!strcmp(r,"7b9b13") || !strcmp(r,"7b13b9")) {     /* 7b9b13 7b13b9 */
    h[0]=_3; h[1]=_b7; h[2]=_b9; h[3]=_b13; return 4; }
  
  if (!strcmp(r,"13b5") || !strcmp(r,"13#11")) {        /* 13b5 13#11 */
    h[0]=_3; h[1]=_b5; h[2]=_b7; h[3]=_13; *f=_b5; return 4; }

  if (!strcmp(r,"13b5") || !strcmp(r,"13#11")) {        /* 13b5 13#11 */
    h[0]=_3; h[1]=_b5; h[2]=_b7; h[3]=_13; *f=_b5; return 4; }

  /* alt is done as 7#9#5. The scale also has b9 b5 */
  if (!strcmp(r,"alt") || !strcmp(r,"7alt") ||          /* alt */
      !strcmp(r,"7#9#5") || !strcmp(r,"7#5#9")) {
    h[0]=_3; h[1]=_a5; h[2]=_b7; h[3]=_a9; *f=_a5; return 4; }
  
  /* --- major chords */
  if (!strcmp(r, "")) {                                 /* major triad */
    h[0]=_3; h[1]=_5; return 2; }

  if (!strcmp(r,"6")) {                                 /* maj 6 */
    h[0]=_3; h[1]=_6;  return 2; }
  
  if (!strcmp(r, "6/9")) {                              /* 6/9 */
    h[0]=_3; h[1]=_6; h[2]=_9; return 3; }

  if (!strcmp(r,"M7") || !strcmp(r,"maj7")) {           /* M7 maj7 */
    h[0]=_3; h[1]=_5; h[2]=_7; return 3; }

  if (!strcmp(r,"M9") || !strcmp(r,"maj9")) {           /* M9 maj9 */
    h[0]=_3; h[1]=_7; h[2]=_9; return 3; }

  if (!strcmp(r,"add9") || !strcmp(r,"maj9")) {         /* add9 */
    h[0]=_3; h[1]=_9; return 2; }

  if (!strcmp(r,"M7b5") || !strcmp(r,"M7#11")) {        /* M7b5 M7#11 */
    h[0]=_3; h[1]=_7; h[2]=_7; *f=_b5; return 3; }
  
  /* --- minor chords */
  if (!strcmp(r, "m")) {                                /* minor triad */
    h[0]=_b3; h[1]=_5; return 2; }
  
  if (!strcmp(r, "m7")) {                               /* m7 */
    h[0]=_b3; h[1]=_b7; return 2; }

  if (!strcmp(r, "m7sus")) {                            /* m7 */
    h[0]=_4; h[1]=_b7; return 2; }

  if (!strcmp(r, "m9")) {                               /* m9 */
    h[0]=_b3; h[1]=_b7; h[2]=_9; return 3; }

  if (!strcmp(r, "m11")) {                              /* m11 */
    h[0]=_b3; h[1]=_b7; h[2]=_11; return 3; }

  if (!strcmp(r, "madd9") || !strcmp(r, "m+9")) {       /* m(add9) */
    h[0]=_b3; h[1]=_9; return 2; }

  if (!strcmp(r, "m6")) {                               /* m6 */
    h[0]=_b3; h[1]=_6; return 2; }
  
  if (!strcmp(r, "m6/9")) {                             /* m6/9 */
    h[0]=_b3; h[1]=_6; h[2]=_9;  return 3; }
  
  if (!strcmp(r, "mb6")) {                              /* mb6 */
    h[0]=_b3; h[1]=_a5; return 2; }
  
  if (!strcmp(r, "m#7")) {                              /* m#7 */
    h[0]=_b3; h[1]=_7; return 2; }
  
  if (!strcmp(r, "m7b9")) {                             /* m7b9 */
    h[0]=_b3; h[1]=_7; h[2]=_b9; return 3; }
  
  if (!strcmp(r, "m7b5")) {                             /* m7b5 */
    h[0]=_b3; h[1]=_b5; h[2]=_b7; *f=_b5; return 3; }


  /* --- sus, dim chords */
  if (!strcmp(r,"sus9") || !strcmp(r,"9sus")) {         /* sus9 9sus */
    h[0]=_4; h[1]=_b7; h[2]=_9; return 3; }

  if (!strcmp(r,"sus7b9")) {                            /* sus7b9 */
    h[0]=_4; h[1]=_b7; h[2]=_b9; return 3; }
  
  if (!strcmp(r,"sus13") || !strcmp(r,"13sus")) {       /* sus13 13sus */
    h[0]=_4; h[1]=_b7; h[2]=_9; h[3]=_13; return 4; }
  
  if (!strcmp(r,"sus7") || !strcmp(r,"7sus") ||         /* sus7 7sus sus */
      !strcmp(r,"sus")) { 
    h[0]=_4; h[1]=_5; h[2]=_b7; return 3; }
      
  if (!strcmp(r,"dim7") || !strcmp(r,"7dim")) {         /* dim7 7dim */
    h[0]=_b3; h[1]=_b5; h[2]=_bb7; *f=_b5; return 3; }

  if (!strcmp(r,"dim") || !strcmp(r,"mb5")) {           /* dim mb5 */
    h[0]=_b3; h[1]=_b5; *f=_b5; return 2; }

  /* chord was not identified */
  return 0;

}

/* --- voiceroot: minimal voicing, try to keep changes small --- */
int voiceroot (int r, int rsv, int rmin, int rmax)
{
  int rr;

  /* use next line to disable this stuff */
  /*|   return r;  |*/

  rr = r;
  if (rsv < 1000) {
    while (rr > rsv+6) rr -= 12;
    while (rr < rsv-6) rr += 12;
    /* make sure we don't wander off to infinity */
    while (rr > rmax) rr -= 12;
    while (rr < rmin) rr += 12;
  }
/*|   printf ("voiceroot: r=%d rsv=%d rr=%d\n", r, rsv, rr);  |*/
  return rr;
}


/* --- au_split_chstring --- */
int au_split_chstring (char *gc0, char *gc, char *add, char *bs)
{
  char *p, *q, *r, str[81];
  
  /* remove spaces and ( ); separate off bass at '/' except if 6/9 */
  p = gc0;
  q = gc;
  while (*p) {
    if ((*p == '/') && ((*(p-1) != '6') || (*(p+1) != '9'))) break;
    if ((*p != ' ') && (*p != '(') && (*p != ')')) *q++ = *p;
    p++;
  }
  *q = 0;

  /* get the explicit bass */
  *bs = 0;
  if (*p == '/') {
    p++;
    r = bs;
    while (*p) { if (*p != ' ') *r++ = *p; p++; }
    *r = 0;
  }

  /* now split gc into main chord and add spec, eg E7(addC#) */
  /* if "add" is not followed by a letter (eg. add9) leave it in main chord */
  *add = 0;
  r = strstr (gc, "add");
  if (r) {
    if (isalpha(*(r+3))) {
      *r = 0; r += 3; 
      q = add;
      while (*r) { if (*r != ' ') *q++ = *r; r++; }
      *q = 0;
    }
  }

  /* we could add something here to translate "a" to "Am" etc. */
  
  /* filter out some common misuses */
  p = gc; q = str;
  while (*p) *q++ = tolower(*p++);
  *q = 0;
  if (strstr(str,  "tacet"))   return 0;
  if (strstr(str,  "n.c."))    return 0;
  if (strstr(str,  "d.c."))    return 0;
  if (strstr(str,  "nc"))      return 0;
  if (strstr(str,  "dc"))      return 0;
  if (strstr(str,  "fine"))    return 0;
  if (strstr(str,  "capo"))    return 0;
  if (strstr(str,  "segno"))   return 0;

  return 1;
}

/* --- au_numpitch: return halfsteps relative to C --- */
int au_numpitch (char *gc, char **rest)
{
  char *q;
  int k, hs;
  int ktab[7] = {0,2,4,5,7,9,11};      /* whole and half steps */

  q = gc;
  if      (strchr("ABCDEFG",*q)) k = *q - 'C';
  else if (strchr("abcdefg",*q)) k = *q - 'c'; 
  else { *rest = q; return -1; }
  q++;

  if (k<0) k += 7;
  hs = ktab[k];
  if (*q == 'b') { hs--; q++; }
  if (*q == '#') { hs++; q++; }
  if (hs < 0) hs += 12;
  *rest = q;
  return hs;
}


/* --- au_interp_gch: return pitches, as halfsteps relative to C */
int au_interp_gch (char *gc0, int type, int hs[], float tp, float tgc)
{
  char *q, gc[81], xb[81], add[81], *q0;
  int mroot, hh[10], ff, j, nn, broot, b, hsx, rc, aroot;
  static int bsv=2000, qsv=2000, tsv=2000;
  static char gchsv[81] = "\0";
  float dt;

  /* split into main chord, add spec, explicit bass */
  rc = au_split_chstring (gc0, gc, add, xb);
/*|   printf ("<%s> got rc=%d  <%s> <%s> <%s>\n", gc0, rc, gc, add, xb);  |*/
  if (!rc) return 0;
  if (*gc == '\0') strcpy (gc, gchsv);
  strcpy (gchsv, gc);
  

  /* get root of main chord */
  mroot = au_numpitch (gc, &q0);
  if (mroot < 0) {
    printf ("+++ error in chord \"%s\"\n", gc0);
    return 0;
  }
  
  /* get pitch for /bass */
  broot = mroot;
  if (*xb) {
    broot = au_numpitch (xb, &q);
    if (broot < 0) {
      printf ("+++ error in /bass, chord \"%s\"\n", gc0);
      broot = mroot;
    }
  }
  b = broot - mroot;

  /* get pitch for addX, eg E7(addC#) */
  aroot = -1;
  if (*add) { 
    aroot = au_numpitch (add, &q); 
    if (aroot < 0) { 
      printf ("+++ error in add, chord \"%s\"\n", gc0); 
      aroot = -1;
    } 
  } 

  /* interpret rest of chord */
  nn = au_interp_gchrest (q0, hh, &ff);
  if (nn == 0) {
    printf (" +++ unknown chord \"%s\" in bar %d\n", gc0, current_bar);  
    hh[0] = 0;
    nn = 1;
  }

  /* add in added note */
  if (aroot >=0) {
    hh[nn] = aroot - mroot;
    while (hh[nn] < 0)   hh[nn] += 12;
    while (hh[nn] >= 12) hh[nn] -= 12;
    hh[nn] +=12;   /* put on top */
    nn++;
  }

  /* printout */
  if (vb>14) { 
    printf ("  gch %-11s (", gc0);
    if (type == AU_ACC_R) printf ("B");
    if (type == AU_ACC_Q) printf ("Q");
    if (type == AU_ACC_T) printf ("T");
    if (type == AU_ACC_L) printf ("L");
    if (type == AU_ACC_r) printf ("r");
    if (type == AU_ACC_q) printf ("q");
    if (type == AU_ACC_t) printf ("t");
    printf (")  root %-2d  b %d  q %d  t", mroot, b, ff);
    for (j=0; j<nn; j++) printf (" %d", hh[j]); printf ("\n"); 
  }    
  
  if ((type == AU_ACC_R) || (type == AU_ACC_r)) {
    hsx = voiceroot (mroot+b, bsv, 0, 18);
    bsv = hsx;
    if (type == AU_ACC_r) hsx -= 12;
    if (type == AU_ACC_R) hsx -= 24;
    hs[0] = hsx;
    return 1;
  }

  /* for fifth in bass, use root if chord was specified here */
  if (type == AU_ACC_Q) {
    dt = tgc - tp;
    hsx = voiceroot (mroot+ff, bsv, 0, 18);
    if ((dt > -.001) && (dt < 0.001)) 
      hsx = voiceroot (mroot+b, bsv, 0, 18);
    bsv = hsx;
    hsx -= 24;
    hs[0] = hsx;
    return 1;
  }

  if (type == AU_ACC_q) {
    hsx = voiceroot (mroot+ff, qsv, 0, 18);
    qsv = hsx;
    hsx -= 12;
    hs[0] = hsx;
    return 1;
  }
  
  if ((type == AU_ACC_T) || (type == AU_ACC_t)) {
    hsx = voiceroot (mroot+hh[0], tsv, 3, 18); 
    tsv = hsx;
    if (type == AU_ACC_t) hsx -= 12;
    if (type == AU_ACC_T) hsx -= 24;

    for (j=0; j<nn; j++) {
      hs[j] = hh[j] + hsx - hh[0];
      /* this just shifts each note separately into a fixed range */
      /* leads to some random inversions */
      if (invert_chords) {
        while (hs[j] >= AU_INV_MAX)  hs[j] -= 12;  
        while (hs[j] <  AU_INV_MIN)  hs[j] += 12;  
      }
    }
    return nn;
  }

  return 0;
}


/* --- au_play_pat --- */
void au_play_pat (float tnow, float tbar, struct accpat *p, float tempo,
                   float *tdacc, float tdone, struct SBUF *sb)
{
  int i, j, npit, hstab[10], k;
  float tp, t, tadd, tfac, t0, tn, ttot, tgc;
  char *gc;

  if (p->n == 0) return;

  tfac = 60.0 / (tempo * QUARTER);
  tadd = 0.3;
  t = tnow - tbar;
  
/*|    printf ("au_play_pat  t=%.1f  n=%d  id=%s\n", t, p->n, p->id);   |*/
/*|    printf ("chord table (%d)\n", ngctab);    |*/
/*|    for (k=0; k<ngctab; k++)     |*/
/*|      printf ("  time %.1f  <%s>\n", gctab[k].tgc, gctab[k].gch);    |*/
  
  ttot = tfac * p->totdur;
  if (t > ttot+0.01) 
    printf ("++++ bar %d is longer than acc pattern, %.2f > %.2f\n", 
            current_bar, t, ttot);
  if (t > 0.5 && t < ttot-0.01) 
    printf ("++++ bar %d is shorter than acc pattern, %.2f < %.2f\n", 
            current_bar, t, ttot);

  for (i=0; i<p->n; i++) {
    tp = p->del[i] * tfac;
    if (tp > t+0.001) continue;
    
    /* select chord */
    gc = NULL;
    for (k=0; k<ngctab; k++) 
      if (tp > gctab[k].tgc-0.001) { gc = gctab[k].gch; tgc = gctab[k].tgc; }
    if (!gc) continue;
    
    if (strlen(gc)) {  
      t0 = tbar + tfac * (p->del[i]);  
      tn = t0   + tfac * (p->dur[i]);  
      if (vb>13) 
        printf ("  add %.2f to %.2f (%d of %d) \"%s\"  tp=%.2f tgc=%.2f\n",   
                t0, tn, i, p->n, gc, tp, tgc);  
      
      npit = au_interp_gch (gc, p->type, hstab, tp, tgc);  
      if (npit) {  
        if (tn+tadd >= sb->t2) au_write_sbuf (tdone, sb);   
        for (j=0; j<npit; j++) 
          au_write_note (t0, tn, tn+tadd, hstab[j], p->q, p->amp, sb);   
        if (tn > *tdacc) *tdacc = tn;
      }  
    }  
  } 

}


/* --- parse_accpat --- */
void parse_accpat (char *desc, char *s, struct accpat *bp, struct accpat *qp, 
                   struct accpat *tp, struct accpat *Bp, 
                   struct accpat *Qp, struct accpat *Tp) 
{
  char *p, *q, str[101];
  int done,type,a,b;

  strcpy (acc_desc, desc);

  strcpy(str, s);
  if (vb > 4) printf ("Parse pattern <%s>\n", str);

  bp->type = AU_ACC_r;
  qp->type = AU_ACC_q;
  tp->type = AU_ACC_t;
  Bp->type = AU_ACC_R;
  Qp->type = AU_ACC_Q;
  Tp->type = AU_ACC_T;
  bp->n = qp->n = tp->n = 0;
  Bp->n = Qp->n = Tp->n = 0;

  bp->amp=0.3;
  qp->amp=0.3;
  tp->amp=0.3;
  Bp->amp=0.5;
  Qp->amp=0.5;
  Tp->amp=0.5;

  p=str;
  type=done=0;
  for (;;) {
    while (*p==' ') p++;
    q=p;
    while ((*q!=' ') && (*q!='\0')) q++;
    if (*q=='\0') done=1;
    *q='\0';
    
    if (strlen(p)) {
      if      (!strcmp(p,"L")) type=AU_ACC_L;
      else if (!strcmp(p,"R")) type=AU_ACC_R;
      else if (!strcmp(p,"Q")) type=AU_ACC_Q;
      else if (!strcmp(p,"T")) type=AU_ACC_T;
      else if (!strcmp(p,"r")) type=AU_ACC_r;
      else if (!strcmp(p,"q")) type=AU_ACC_q;
      else if (!strcmp(p,"t")) type=AU_ACC_t;
      else {
        a = b = 0;
        sscanf (p, "%d/%d", &a, &b);
        if (type == AU_ACC_L) {
          bp->totdur = qp->totdur = tp->totdur = a*EIGHTH;
          Bp->totdur = Qp->totdur = Tp->totdur = a*EIGHTH;
        }

        else if (type == AU_ACC_R) {
          Bp->del[Bp->n] = a*EIGHTH;
          Bp->dur[Bp->n] = b*EIGHTH;
          Bp->n++;
        }
        
        else if (type == AU_ACC_Q) {
          Qp->del[Qp->n] = a*EIGHTH;
          Qp->dur[Qp->n] = b*EIGHTH;
          Qp->n++;
        }

        else if (type == AU_ACC_T) {
          Tp->del[Tp->n] = a*EIGHTH;
          Tp->dur[Tp->n] = b*EIGHTH;
          Tp->n++;
        }

        else if (type == AU_ACC_r) {
          bp->del[bp->n] = a*EIGHTH;
          bp->dur[bp->n] = b*EIGHTH;
          bp->n++;
        }

        else if (type == AU_ACC_q) {
          qp->del[qp->n] = a*EIGHTH;
          qp->dur[qp->n] = b*EIGHTH;
          qp->n++;
        }

        else if (type == AU_ACC_t) {
          tp->del[tp->n] = a*EIGHTH;
          tp->dur[tp->n] = b*EIGHTH;
          tp->n++;
        }
      }
    }
    p=q+1;
    if (done) break;
  }

}

/* --- pr_accpat --- */
#define PATLEN 12
void pr_accpat (struct accpat *p, char *txt)
{
  int i, j, del, dur, l;
  char c1, c2, str[PATLEN+2];

  c1 = 'O';
  c2 = 'o';
  if (p->type == AU_ACC_R || p->type == AU_ACC_Q ||
      p->type == AU_ACC_T) c1 = 'B';
  
  l = p->totdur/EIGHTH;
  for (i=0; i<l; i++) str[i] = '-';
  for (i=l; i<PATLEN; i++) str[i] = ' ';
  str[PATLEN+1] = '\0';

  for (i=0; i<p->n; i++) {
    del = p->del[i]/EIGHTH, 
    dur = p->dur[i]/EIGHTH;
    str[del] = c1;
    for (j=del+1; j<del+dur; j++) str[j] = c2;
  }

  str[PATLEN] = '\0';
  printf ("   %-8s  %s  ", txt, str);
  
  for (i=0; i<p->n; i++)  
    printf ("%d/%d ", p->del[i]/EIGHTH, p->dur[i]/EIGHTH); 
  printf ("\n");

}


/* --- print_accpat --- */
void print_accpat (struct accpat *bp, struct accpat *qp, struct accpat *tp,
                   struct accpat *Bp, struct accpat *Qp, struct accpat *Tp,
                   char *text)
{
  int l;

  l = Bp->totdur/EIGHTH;

/*|   if (*text) printf ("\n"); |*/
/*|   printf ("%-2s length     ", text); |*/
/*|   for (i=0; i<l; i++) printf ("."); |*/
/*|   for (i=l; i<PATLEN; i++) printf (" "); |*/
/*|   printf ("%d\n", l);       |*/


  printf ("%-2s %s (%d)\n", text, acc_desc, l);

/*|   printf ("%-2s length     ", text); |*/
/*|   for (i=0; i<l; i++) printf ("."); |*/
/*|   for (i=l; i<PATLEN; i++) printf (" "); |*/
/*|   printf ("%d\n", l);       |*/



  if (tp->n) pr_accpat (tp, "treble");
  if (qp->n) pr_accpat (qp, "fifth");
  if (bp->n) pr_accpat (bp, "root");

  if (Tp->n) pr_accpat (Tp, "Treble");
  if (Qp->n) pr_accpat (Qp, "Fifth");
  if (Bp->n) pr_accpat (Bp, "Root");


}



/* --- set_accpat --- */
/* R is root, Q is fifth, T is treble, L is pattern length */
void set_accpat (char *a0, struct METERSTR m, struct accpat *bp, 
                 struct accpat *qp, struct accpat *tp,
                 struct accpat *Bp, struct accpat *Qp, struct accpat *Tp) 
{
  int m1, m2;
  char a[101];
  strcpy (a, a0);

  /* select default pattern */
  if (*a == '0') {
    m1 = m.meter1;
    m2 = m.meter2; 
    if (vb>3) printf ("\nChoose accompaniment using meter %d/%d:", m1, m2);
    if      ((m1==4) && (m2==4)) strcpy(a, "4");
    else if ((m1==3) && (m2==4)) strcpy(a, "3");
    else if ((m1==6) && (m2==8)) strcpy(a, "6");
    else if ((m1==2) && (m2==4)) strcpy(a, "2");
    else rx (" No suitable acc pattern found for this meter.","");
    if (vb>3) printf (" %s\n", a);
  }
  else {
    if (vb>3) printf ("\nAccompaniment: %s\n", a);
  }

  /* --- start accpats --- */
  if  (*a == 'b') 
    parse_accpat ("Generic latin pattern",
                  "L 8 R 0/3 Q 3/1 4/4 t 0/3 3/1 6/2", bp,qp,tp,Bp,Qp,Tp);  
  
  else if (*a == '4')  
    parse_accpat ("Standard 4/4 stride",
                  "L 8 R 0/4 Q 4/4 t 2/4 6/2", bp,qp,tp,Bp,Qp,Tp); 
  
  else if (*a == 'B')  
    parse_accpat ("4/4 stride, always use root for bass",
                  "L 8 R 0/3 4/3 t 2/4 6/2 q 2/4 6/2", bp,qp,tp,Bp,Qp,Tp);  
  
  /* stride, treble has q h q */
  else if (*a == 'x')  
    parse_accpat ("Stride, treble short-long-short",
                  "L 8 R 0/3 Q 4/3 t 0/2 2/4 6/2", 
                  bp,qp,tp,Bp,Qp,Tp); 

  else if (*a == 'X') 
    parse_accpat ("Stride, treble short-long-short, root in bass",
                  "L 8 R 0/3 4/3 t 0/2 2/4 6/2 q 0/2 2/4 6/2", 
                  bp,qp,tp,Bp,Qp,Tp); 

  else if (*a == 'F') 
    parse_accpat ("Full chord on all four beats",
                  "L 8 R 0/2 2/2 4/2 6/2 t 0/2 2/2 4/2 6/2 "
                  "q 0/2 2/2 4/2 6/2", bp,qp,tp,Bp,Qp,Tp); 

  else if (*a == 'y') 
    parse_accpat ("Treble on all four beats, bass root on 1 and 3",
                  "L 8 R 0/3 4/3 t 0/2 2/2 4/2 6/2 "
                  "q 0/2 2/2 4/2 6/2", bp,qp,tp,Bp,Qp,Tp); 

  else if (*a == 'Y') 
    parse_accpat ("Treble on all four beats, bass root and fifth",
                  "L 8 R 0/3 Q 4/3 t 0/2 2/2 4/2 6/2",
                  bp,qp,tp,Bp,Qp,Tp); 

  else if (*a == 'z') 
    parse_accpat ("Bass only, 1 and 3", 
                  "L 8 R 0/4 4/4", bp,qp,tp,Bp,Qp,Tp); 

  else if (*a == 'Z') 
    parse_accpat ("Bass only, all four beats",
                  "L 8 R 0/2 2/2 4/2 6/2", bp,qp,tp,Bp,Qp,Tp); 

  /* bass and fifth, 2 beats */
  else if (*a == 'q') 
    parse_accpat ("Bass only, alternate root and fifth",
                  "L 8 R 0/4 Q 4/4", bp,qp,tp,Bp,Qp,Tp); 

  else if (*a == 'Q') 
    parse_accpat ("Bass only, all four beats, root and fifth",
                  "L 8 R 0/2 4/2 Q 2/2 6/2", bp,qp,tp,Bp,Qp,Tp); 

  else if (*a == '2') 
    parse_accpat ("Standard 2/4 time accompaniment",
                  "L 4 R 0/2 t 2/2 q 2/2", bp,qp,tp,Bp,Qp,Tp);  
  
  else if (*a == '3') 
    parse_accpat ("Standard 3/3 waltz",
                  "L 6 R 0/6 t 2/2 4/2 q 2/2 4/2", bp,qp,tp,Bp,Qp,Tp);    

  else if (*a == 'w') 
    parse_accpat ("Another 3/3 waltz",
                  "L 6 R 0/2 Q 2/2 t 4/2", bp,qp,tp,Bp,Qp,Tp);   
  
  else if (*a == 'W') 
    parse_accpat ("Another 3/3 waltz",
                  "L 6 R 0/1 Q 1/1 3/1 5/1 t 2/1 4/1", bp,qp,tp,Bp,Qp,Tp); 

  else if (*a == 'j') 
    parse_accpat ("Jig",
                  "L 6 R 0/1 Q 3/1 t 2/1 5/1", bp,qp,tp,Bp,Qp,Tp);  
  
  else if (*a == '6') 
    parse_accpat ("Standard 6/8 waltz",
                  "L 6 R 0/1 Q 3/1 t 1/1 2/1 4/1 5/1", bp,qp,tp,Bp,Qp,Tp); 

  else if (*a == 'J') 
    parse_accpat ("6/8 waltz, treble on all beats",
                  "L 6 R 0/1 Q 3/1 t 0/1 1/1 2/1 3/1 4/1 5/1", bp,qp,tp,Bp,Qp,Tp);  

  else if (*a == 't') 
    parse_accpat ("12/8 waltz",
"L 12 R 0/3 6/3 Q 3/3 9/3 t 0/1 1/1 2/1 3/1 4/1 5/1 6/1 7/1 8/1 9/1 10/1 11/1",  
                  bp,qp,tp,Bp,Qp,Tp);  
  
  else if (*a == 'T') 
    parse_accpat ("12/8 waltz, bass uses root only",
                  "L 12 R 0/3 6/3 3/3 9/3 t 0/1 1/1 2/1 3/1 4/1 5/1 6/1 7/1 8/1 9/1 10/1 11/1",  
                  bp,qp,tp,Bp,Qp,Tp);  
  
  
  else rx("unknown acc specifier: ", a);

/*|   if (vb > 3) print_accpat (bp, qp, tp, Bp, Qp, Tp, a); |*/
  
  strcpy (bp->id, "r");
  strcpy (qp->id, "q");
  strcpy (tp->id, "t");
  strcpy (Bp->id, "R");
  strcpy (Qp->id, "Q");
  strcpy (Tp->id, "T");

}

  
FILE *aufp;
struct SBUF sb;
float tsave, twrote;
char aufname[201];

#define DETUNE 0.1


/* --- print_au_info --- */
void print_au_info (int job)
{
  char *c, apat[21], text[12];
  char pats[81];
  struct accpat bpat, qpat, tpat, Bpat, Qpat, Tpat;
  
  samples_per_second = SAMPLES_PER_SEC;
  bits_per_sample = 8;
  num_channels = 1;

  if (cd_format) {
    samples_per_second = CD_SAMPLES_PER_SEC;
    bits_per_sample = 16;
    num_channels = 2;
  }

  if (job == 0) {
    printf ("Audio: %s format, %d samples/sec, %d bits/samp, %d channel%s.\n",
            wav_format? "wav":"au",
            samples_per_second, bits_per_sample,
            num_channels, num_channels==1 ? "":"s" );
#if LIL_ENDIAN==0
    printf ("Compiled for big endian CPU.\n");
#else
    printf ("Compiled for little endian CPU.\n");
#endif
  }

  if (job == 1) {
    printf ("\nAccompaniment patterns:\n");

    strcpy (pats, PATS);
    c = pats;
    while (*c) {
      printf ("\n");
      sprintf (text, "%c", *c);
      sprintf (apat, "%c", *c);
      set_accpat (apat, voice[0].meter,
                  &bpat, &qpat, &tpat, &Bpat, &Qpat, &Tpat);
      print_accpat (&bpat, &qpat, &tpat, &Bpat, &Qpat, &Tpat, text);
      c++;
    }
  }

}


/* --- open_audio_file ---- */
void open_audio_file (char aunam[])
{
  int rc;

  strcpy (aufname, aunam);

  /* initialize */
  samples_per_second = SAMPLES_PER_SEC;
  bits_per_sample = 8;
  num_channels = 1;

  if (cd_format) {
    samples_per_second = CD_SAMPLES_PER_SEC;
    bits_per_sample = 16;
    num_channels = 2;
  }

  if (vb > 2)
    printf ("Audio: %s format, %d samples/sec, %d bits/samp, %d channel%s\n",
            wav_format? "wav":"au",
            samples_per_second, bits_per_sample,
            num_channels, num_channels==1 ? "":"s" );

  if (wav_format) {
    strcat (aufname, ".wav");
    if (vb>5) printf ("Open audio output file %s\n", aufname);
    aufp = fopen (aufname, "wb");
    if (!aufp) rx ("Cannot open audio output file: ", aufname);
    wavstartwrite (aufp, samples_per_second, bits_per_sample, num_channels);
  }

  else {
    strcat (aufname, ".au");
    if (vb>5) printf ("Open audio output file %s\n", aufname);
    aufp = fopen (aufname, "wb");
    if (!aufp) rx ("Cannot open audio output file: ", aufname);
    rc = write_header (aufp, samples_per_second, bits_per_sample, num_channels)\
;
    if (rc != 0) rx("Error writing audio file header","");
  }

  au_init_sbuf (&sb);

}


/* --- close_audio_file --- */
void close_audio_file ()
{
  int m,min;
  float sec;

  tsave += 0.5;
  au_write_sbuf (tsave, &sb);

  if (wav_format) wavstopwrite ();
  fclose (aufp);

  min = tsave/60;
  sec = tsave - 60*min;

  m = get_file_size (aufname);
  printf ("Audio written on %s.%s (%d:%02.0f minutes, %d byte%s%s)\n",
          AUDIOFILE,
          wav_format ? "wav" : "au",
          min,sec,
          m, m==1 ? "" : "s",
          cd_format ? ", CD" : "");

}


/* --- au_init_sbuf --- */
void au_init_sbuf (struct SBUF *sb)
{
  int i;

  sb->n      =  MAX_SAMPLES;
  sb->sps    =  samples_per_second;
  sb->tau    =  1.0/sb->sps;
  sb->t1     =  -0.5;     /* leave some time for initial graces */
  sb->t2     =  sb->t1 + sb->n * sb->tau;
  sb->g0     =  1.0;
  sb->top    =  1.0;
  sb->new    =  1;
  for (i=0; i<MAX_SAMPLES; i++) sb->data[i]=0.0;

  if (vb>9) {
    printf ("\nInit audio buffer: %d samples, %d per second, tau=%.8f\n",
            sb->n, sb->sps, sb->tau);
    printf ("Time interval is (%.3f,%.3f)\n", sb->t1, sb->t2);
  }
  
}


/* --- au_agc_sbuf: scale to get consistent amplitudes --- */
/* FIXME this is not really a good way to do it */
void au_agc_sbuf (int nb, struct SBUF *sb)
{
  int i,m,i0,num;
  float g0,g1,g,s,d2,tmax,amp0,top;

  num=1600;

  num=4000;

  num = (num * samples_per_second) / 8000;


  amp0=0.70;
  g0 = sb->g0;
  top = sb->top;
  i0 = 0;
  for (;;) {
    
    tmax=0;
    for (i=i0; i<i0+num; i++) {
      if (i==nb) break;
      d2 = sb->data[i] * sb->data[i];
      if (d2>tmax) tmax=d2;
    }
    m = i;
    tmax=sqrt(tmax);
    top = 0.2*tmax+0.8*top;
    if (sb->new) top=tmax;
    if (top<0.25) top=0.25;
    g1 = amp0/top;
    if (sb->new) g0=g1;
    if (sb->new) sb->new=0;
    if (g1*tmax>0.90) g1=0.90/tmax;
    if (g0*tmax>0.90) g0=0.90/tmax;
/*|  printf ("agc %5d to %5d   max=%.2f   top=%.2f   gain %.2f to %.2f\n",|*/
/*|          i0, m, tmax, top, g0, g1);      |*/
    s = (g1-g0)/(m-i0); 
    for (i=i0; i<m; i++) { 
      g=g0+s*(i-i0); 
      sb->data[i] *= g;
    }  
    
    g0=g1;
    i0 = m;
    if (i0>=nb) break;
  }

  sb->g0 = g0;
  sb->top = top;
}


/* --- au_write_sbuf: unload part of buffer below time t --- */
void au_write_sbuf (float t, struct SBUF *sb)
{
  int i0, i, nb;

  if (!active) return;

  nb = sb->n;
  i0 = (t - sb->t1) / sb->tau;

  if (vb>14)  
    printf ("  Write sbuf up to t=%.3f (point %d) in interval=(%.3f,%.3f)\n", 
            t, i0, sb->t1, sb->t2);
  
  if (i0 <= 0) return;
  if (i0 > nb) i0=nb;
  twrote += i0 * sb->tau;

/*|   if (!cd_format)   |*/
  au_agc_sbuf (i0,sb);  
  
  if (wav_format)
    wavwrite (aufp, sb->data, i0);
  else
    auwrite (aufp, sb->data, i0, bits_per_sample, num_channels);

  for (i=0; i<nb-i0; i++)  sb->data[i] = sb->data[i+i0];
  for (i=nb-i0; i<nb; i++) sb->data[i] = 0;

  sb->t1 = sb->t1 + i0*sb->tau;
  sb->t2 = sb->t2 + i0*sb->tau;

  if (vb>14) printf ("  New interval is (%.3f,%.3f)\n", sb->t1, sb->t2);

}


struct TRIPLE au_sp (float a, float b, float c)
{
  struct TRIPLE t;
  t.low=0;      if (a<1000) t.low=a; 
  t.mid=t.low;  if (b<1000) t.mid=b;
  t.hi= t.mid;  if (c<1000) t.hi=c;
  return t;

}


/* --- au_set_quality: set parameters for tone quality ---*/
struct AU_QUALITY au_set_quality (char c)
{
  struct AU_QUALITY q;
  float x = 1001;
  int select;

  if      (c == 'p') select = 1;
  else if (c == 'a') select = 2;
  else if (c == 'f') select = 3;
  else if (c == 'c') select = 4;
  else if (c == 'o') select = 5;
  else if (c == 'g') select = 6;
  else select = c - '0';
  if ((select > 6) || (select < 1)) {
    printf ("+++ unknown au quality \'%c\', using p\n", c);
    select = 1;
  }

  q.type = 0;
  q.num  = select;

  /* C,=130.8    C=261.6   c=523.2  c'=1046.4  */
  q.freq.low = 130.8;
  q.freq.mid = 261.6; 
  q.freq.hi  = 523.2;
  q.oamp = au_sp ( 0.0, 0.0, 0.0 );
  q.oa2  = au_sp ( 0.0, 0.0, 0.0 );
  q.oa3  = au_sp ( 0.0, 0.0, 0.0 );
  q.oa4  = au_sp ( 0.0, 0.0, 0.0 );
  q.oa5  = au_sp ( 0.0, 0.0, 0.0 );
  
  /* piano */
  if (select==1) { 
    strcpy (q.name, "P1");
    q.type = AU_SINE; 
    q.att  = au_sp ( 0.005, 0.003, 0.001);
    q.dec1 = au_sp ( 0.90, 0.60, 0.40 ); 
    q.dec2 = au_sp ( 0.30, 0.02, 0.01 ); 
    q.amp1 = au_sp ( 0.40, 0.40, 0.40 );
    q.amp2 = au_sp ( 0.50, 0.30,0.30 );
    q.dec3 = au_sp ( 0.10, 0.08, 0.04 );

    /* specify overtones */
    q.oa2 = au_sp (0.04, 0.05, 0.00);
    q.oa3 = au_sp (0.05, 0.15, 0.15);
    q.oa4 = au_sp (0.001, 0.0005, 0.0005);
    q.oa5 = au_sp (0.001, -0.0005, 0.0005);

/*|     q.oamp = au_sp ( 1.00, 1.00, 1.00 ); |*/
/*|     q.odec = au_sp ( 0.80, 0.60, 0.40 ); |*/

    q.oamp = au_sp ( 1.00, 0.80, 0.20 );
    q.odec = au_sp ( 0.50, 0.30, 0.10 );

    q.amp1 = au_sp ( 0.40, 0.40, 0.40 );
    q.amp2 = au_sp ( 0.00, 0.00, 0.00 );

    q.att  = au_sp ( 0.003, 0.003, 0.003);

  }     

  /* triangle. Accordion? */
  if (select==2) {
    strcpy (q.name, "Acc");
    q.type = AU_TRIANGLE;
    q.sm   = au_sp ( 0.10,  0.15, 0.20 );
    q.att  = au_sp ( 0.01,  x, x );
    q.dec1 = au_sp ( 1.00,  x, x );
    q.dec2 = au_sp ( 0.30,  x, x );
    q.amp1 = au_sp ( 0.50,  x, x );
    q.amp2 = au_sp ( 0.10,  x, x );
    q.dec3 = au_sp ( 0.02,  x, x );
  }

  /* flute */
  if (select==3) {
    strcpy (q.name, "Fl");
    q.type = AU_TRIANGLE;
    q.sm   = au_sp ( 0.10,  0.20,   0.25 );
    q.att  = au_sp ( 0.02,  0.02,  0.02 );
    q.dec1 = au_sp ( 2.20,  2.20,   x );
    q.dec2 = au_sp ( 0.30,  0.30,   0.30 );
    q.amp1 = au_sp ( 0.50,  0.50,   x );
    q.amp2 = au_sp ( 0.005,  0.005, x );
    q.dec3 = au_sp ( 0.02,  0.02,   x );
  }

  /* sawtooth, faster decay. cembalo */
  if (select==4) {
    strcpy (q.name, "Cem");
    q.type = AU_SAWTOOTH;
    q.sm   = au_sp ( 0.03,   0.035, 0.08 );
    q.att  = au_sp ( 0.002, 0.002, x );
    q.dec1 = au_sp ( 0.60,   0.50, x );
    q.dec2 = au_sp ( 0.02,   0.01, x );
    q.amp1 = au_sp ( 0.30,   0.30, x );
    q.amp2 = au_sp ( 0.40,   0.40, x );
    q.dec3 = au_sp ( 0.10,   x, x );
  }


  /* organ */
  if (select==5) {
    strcpy (q.name, "Org");
/*| q.type = AU_TRIANGLE; |*/
    q.type = AU_SINE;
    q.sm   = au_sp ( 0.25,  x, x );
    q.sm   = au_sp ( 0.15,  0.25, 0.25 );
    q.att  = au_sp ( 0.005, x, x );
    q.dec1 = au_sp ( 3.00,  x, x );
    q.dec2 = au_sp ( 0.02,  x, x );
    q.amp1 = au_sp ( 0.30,  x, x );
    q.amp2 = au_sp ( 0.10,  x, x );
    q.dec3 = au_sp ( 0.10,  x, x );
    q.oa2 = au_sp (0.50, 0.50, 0.50);
    q.oa3 = au_sp (0.05, 0.05, 0.05);
    q.oa4 = au_sp (0.30, 0.30, 0.30);
    q.oa5 = au_sp (0.001, 0.001, 0.001);
  }


  /* something plucked. git, bass.. */
  if (select==6) {
    strcpy (q.name, "Git");
/*| q.type = AU_TRIANGLE; |*/
    q.type = AU_SINE;
    q.sm   = au_sp ( 0.20,  0.20, 0.20 );
    q.att  = au_sp ( 0.002,  x, x);
    q.dec1 = au_sp ( 0.20,   0.15, 0.10 );
    q.dec2 = au_sp ( 0.80,   0.40, 0.30 );
    q.amp1 = au_sp ( 0.30,   x, x );
    q.amp2 = au_sp ( 0.15,   x, x );
    q.dec3 = au_sp ( 0.10,   x, x );
    q.oa2 = au_sp (0.10, 0.10, 0.10);
    q.oa3 = au_sp (0.15, 0.15, 0.15);
    q.oa4 = au_sp (-0.005, -0.005, -0.005);
    q.oa5 = au_sp (-0.0005, -0.0005, -0.0005);
  }


  if (!q.type) rxi ("Undefined audio quality: ", select);
  
  return q;

}


/* --- au_getpar: interpolate through par values --- */
float au_getpar (struct TRIPLE p, struct TRIPLE f, float freq)
{

  if (freq < f.low) return p.low;
  if (freq > f.hi)  return p.hi;
  
  if (freq < f.mid) 
    return p.low + (freq-f.low)*(p.mid-p.low)/(f.mid-f.low);
  else
    return p.mid + (freq-f.mid)*(p.hi-p.mid)/(f.hi-f.mid);

}


/* --- au_add_sample: add pure sine-wave samples for one note --- */
void au_add_sample (float t0, float tn, float tx, float freq, 
                   struct AU_QUALITY q, float amp, struct SBUF *sb)
{
  int i, n0, nn, nx, nb;
  float att, dec1, amp1, dec2, amp2, dec3;
  float tau, t1, t2; 
  float t, fc, fs, fa, fd, md, mc, ms, f0;
  float md2, fd2, env, mdx; 
  float tpi = 6.283185; 

  att  = au_getpar(q.att,  q.freq, freq);
  dec1 = au_getpar(q.dec1, q.freq, freq);
  amp1 = au_getpar(q.amp1, q.freq, freq);
  dec2 = au_getpar(q.dec2, q.freq, freq);
  amp2 = au_getpar(q.amp2, q.freq, freq);
  dec3 = au_getpar(q.dec3, q.freq, freq);

  tau = sb->tau;
  t1  = sb->t1;
  t2  = sb->t2;
  nb  = sb->n;

  n0 = (t0-t1)/tau;
  nn = (tn-t1)/tau;
  nx = (tx-t1)/tau;

  if ((n0<0) || (nn>nb) || (nx>nb+1))
    printf ("+++ sine sample outside buffer (%.3f,%.3f) t=%.3f %.3f %.3f\n", 
            t1,t2,t0,tn,tx);

  if (n0<0) n0=0; if (n0 > nb) n0=nb;
  if (nn<0) nn=0; if (nn > nb) nn=nb;
  if (nx<0) nx=0; if (nx > nb) nx=nb;

  /* detune a bit.. helps for multiple voices */
  freq = freq+ranf(-DETUNE,DETUNE);  
  md = exp(-tau/dec1);
  mc = cos(tpi*freq*tau);
  ms = sin(tpi*freq*tau);
  md2 = exp(-tau/dec2);
  t  = 0;

  fs = f0 = 0.0;
  fc = 1.0;
  fd  = amp1*amp;
  fd2 = amp2*amp;

  for (i=n0; i<nn; i++) {
    fa = t/(t+att);
    env = fa*(fd+fd2);
    sb->data[i] += f0*env;
    t  = t+tau;
    fs = f0*mc + fc*ms; 
    fc = fc*mc - f0*ms;
    f0 = fs;
    fd = fd*md;
    fd2= fd2*md2;
  }
  
  mdx = exp(-tau/dec3);
  for (i=nn; i<nx; i++) {
    sb->data[i] += f0*env;
    env = env*mdx;
    fs = f0*mc + fc*ms; 
    fc = fc*mc - f0*ms;
    f0 = fs;
  }


}

/* --- au_add_osample: add in sine-wave samples with overtones --- */
void au_add_osample (float t0, float tn, float tx, float freq, 
                   struct AU_QUALITY q, float amp, struct SBUF *sb)
{
  int i, n0, nn, nx, nb;
  float att, dec1, amp1, dec2, amp2, dec3;
  float tau, t1, t2; 
  float t, fc, fs, fd, md, mc, ms, f0, oamp, odec, foa, mdo;
  float md2, fd2, env, mdx, fa, ma;
  float tpi = 6.283185; 
  static float s2x, c2x, s3x, c3x, s4x, s5x, a2=-100, a3, a4, a5;

  att  = au_getpar(q.att,  q.freq, freq);
  dec1 = au_getpar(q.dec1, q.freq, freq);
  amp1 = au_getpar(q.amp1, q.freq, freq);
  dec2 = au_getpar(q.dec2, q.freq, freq);
  amp2 = au_getpar(q.amp2, q.freq, freq);
  dec3 = au_getpar(q.dec3, q.freq, freq);
  oamp = au_getpar(q.oamp, q.freq, freq);
  odec = au_getpar(q.odec, q.freq, freq);
  a2   = au_getpar(q.oa2,  q.freq, freq);
  a3   = au_getpar(q.oa3,  q.freq, freq);
  a4   = au_getpar(q.oa4,  q.freq, freq);
  a5   = au_getpar(q.oa5,  q.freq, freq);

/*|   if (a2 < -99) { |*/
/*|     printf ("Enter a2 a3 a4 a5: "); |*/
/*|     scanf ("%f %f %f %f", &a2, &a3, &a4, &a5); |*/
/*|   } |*/

  tau = sb->tau;
  t1  = sb->t1;
  t2  = sb->t2;
  nb  = sb->n;
  
  n0 = (t0-t1)/tau;
  nn = (tn-t1)/tau;
  nx = (tx-t1)/tau;
  if (vb>19) {  
    printf ("Add sample:  %.1f Hz   tm %.3f %.3f %.3f   pt %d %d %d\n",
            freq, t0,tn,tx, n0, nn, nx); 
    printf ("    env1 (%.3f,%.4f)  env2 (%.3f %.4f) att/end %.4f %.4f\n",
            amp1, dec1, amp2, dec2, att, dec3);
    printf ("    o-tone env (%.3f,%.4f)  mix= %.4f %.4f %.4f %.4f\n",
            oamp, odec, a2, a3, a4, a5);
  } 
  if ((n0<0) || (nn>nb) || (nx>nb+1))
    printf ("+++ osine sample outside buffer (%.3f,%.3f) t=%.3f %.3f %.3f\n", 
            t1,t2,t0,tn,tx);

  if (n0<0) n0=0; if (n0 > nb) n0=nb;
  if (nn<0) nn=0; if (nn > nb) nn=nb;
  if (nx<0) nx=0; if (nx > nb) nx=nb;
  
  /* detune */
  freq = freq+ranf(-DETUNE,DETUNE);  
  ma = exp (-tau/att);
  md = exp(-tau/dec1);
  mc = cos(tpi*freq*tau);
  ms = sin(tpi*freq*tau);
  md2 = exp(-tau/dec2);
  mdo = exp(-tau/odec);
  t = 0;

  fs  = f0 = 0.0;
  fc  = 1.0;
  fa  = 1.0;
  fd  = amp1*amp;
  fd2 = amp2*amp;
  foa = oamp*amp;

  for (i=n0; i<nn; i++) {
    env = (1-fa) * (fd+fd2);
    sb->data[i] += env *f0 + (1-fa)*foa*(a2*s2x + a3*s3x + a4*s4x + a5*s5x); 
    t  = t+tau;
    fs = f0*mc + fc*ms; 
    fc = fc*mc - f0*ms;

    s2x = 2*fs*fc;
    c2x = fc*fc - fs*fs;
    s3x = fs*c2x + fc*s2x;
    c3x = fc*c2x - fs*s2x;
    s4x = 2*c2x*s2x;
    s5x = s2x*c3x + c2x*s3x;
    
    f0  = fs;
    fa  = fa * ma;
    fd  = fd*md;
    fd2 = fd2*md2;
    foa = foa*mdo;
  }
  
  mdx = exp(-tau/dec3);
  for (i=nn; i<nx; i++) {
    sb->data[i] += env *f0 + fa* foa*(a2*s2x + a3*s3x + a4*s4x + a5*s5x); 
    env = env*mdx;
    foa = foa*mdx;
    fs = f0*mc + fc*ms; 
    fc = fc*mc - f0*ms;
    f0 = fs;
    s2x = 2*fs*fc;
    c2x = fc*fc - fs*fs;
    s3x = fs*c2x + fc*s2x;
    c3x = fc*c2x - fs*s2x;
    s4x = 2*c2x*s2x;
    s5x = s2x*c3x + c2x*s3x;
  }


}

/* --- au_add_ssample: sawtooth-wave samples --- */
void au_add_ssample (float t0, float tn, float tx, float freq, 
                   struct AU_QUALITY q, float amp, struct SBUF *sb)
{
  int i,n0,nn,nx,nb;
  float att,dec1,amp1,dec2,amp2,dec3,sm;
  float tau,t1,t2,tt,bet,a; 
  float t,fa,fd,md,f0,per; 
  float md2,fd2,env,mdx; 

  att  = au_getpar(q.att,  q.freq, freq);
  dec1 = au_getpar(q.dec1, q.freq, freq);
  amp1 = au_getpar(q.amp1, q.freq, freq);
  dec2 = au_getpar(q.dec2, q.freq, freq);
  amp2 = au_getpar(q.amp2, q.freq, freq);
  dec3 = au_getpar(q.dec3, q.freq, freq);
  sm   = au_getpar(q.sm,   q.freq, freq);

  tau = sb->tau;
  t1  = sb->t1;
  t2  = sb->t2;
  nb  = sb->n;

  n0 = (t0-t1)/tau;
  nn = (tn-t1)/tau;
  nx = (tx-t1)/tau;
  if (vb>19)  
    printf ("Add ssample:  %.1f Hz   tm %.3f %.3f %.3f   pt %d %d %d\n",
            freq, t0,tn,tx, n0,nn,nx); 
 
  if ((n0<0) || (nn>nb) || (nx>nb+1))
    printf ("+++ saw sample outside buffer (%.3f,%.3f) t=%.3f %.3f %.3f\n", 
            t1,t2,t0,tn,tx);

  if (n0<0) n0=0; if (n0 > nb) n0=nb;
  if (nn<0) nn=0; if (nn > nb) nn=nb;
  if (nx<0) nx=0; if (nx > nb) nx=nb;

  freq = freq+ranf(-DETUNE,DETUNE);  
  md = exp(-tau/dec1);
  md2 = exp(-tau/dec2);

  per = 1/freq;
  a = 1.0/sm;
  bet = 2*erf(0.5*a)/per;

  tt = 0;
  t  = 0;
  fd  = amp1*amp;
  fd2 = amp2*amp;
  for (i=n0; i<nn; i++) {
    fa = t/(t+att);
    env = fa*(fd+fd2);
    f0 = erf(a*tt/per)-bet*tt;
    sb->data[i] += f0*env;
    tt += tau;
    if (tt>0.5*per) tt -= per;
    t  = t+tau;
    fd = fd*md;
    fd2= fd2*md2;
  }
  
  mdx = exp(-tau/dec3);  
  for (i=nn; i<nx; i++) {  
    f0 = erf(a*tt/per)-bet*tt;
    sb->data[i] += f0*env;  
    tt += tau; 
    if (tt>0.5*per) tt -= per; 
    env = env*mdx;  
  }  
  

}


/* --- au_add_tsample: triangle-wave samples --- */
void au_add_tsample (float t0, float tn, float tx, float freq, 
                   struct AU_QUALITY q, float amp, struct SBUF *sb)
{
  int i,n0,nn,nx,nb;
  float att,dec1,amp1,dec2,amp2,dec3,sm;
  float tau,t1,t2,tt,bet,a,x; 
  float t,fa,fd,md,f0,per; 
  float md2,fd2,env,mdx; 

  att  = au_getpar(q.att,  q.freq, freq);
  dec1 = au_getpar(q.dec1, q.freq, freq);
  amp1 = au_getpar(q.amp1, q.freq, freq);
  dec2 = au_getpar(q.dec2, q.freq, freq);
  amp2 = au_getpar(q.amp2, q.freq, freq);
  dec3 = au_getpar(q.dec3, q.freq, freq);
  sm   = au_getpar(q.sm,   q.freq, freq);

  tau = sb->tau;
  t1  = sb->t1;
  t2  = sb->t2;
  nb  = sb->n;

  n0 = (t0-t1)/tau;
  nn = (tn-t1)/tau;
  nx = (tx-t1)/tau;
  if (vb>19)  
    printf ("Add tsample:  %.1f Hz   tm %.3f %.3f %.3f   pt %d %d %d\n",
            freq, t0,tn,tx, n0,nn,nx); 
 
  if ((n0<0) || (nn>nb) || (nx>nb+1))
    printf ("+++ tri sample outside buffer (%.3f,%.3f) t=%.3f %.3f %.3f\n", 
            t1,t2,t0,tn,tx);

  if (n0<0) n0=0; if (n0 > nb) n0=nb;
  if (nn<0) nn=0; if (nn > nb) nn=nb;
  if (nx<0) nx=0; if (nx > nb) nx=nb;

  freq = freq+ranf(-DETUNE,DETUNE);  
  md = exp(-tau/dec1);
  md2 = exp(-tau/dec2);

  per = 1/freq;
  a = 1.0/sm;
  bet = erf(0.5*a);

  tt = 0;
  t  = 0;
  fd  = amp1*amp;
  fd2 = amp2*amp;
  for (i=n0; i<nn; i++) {
    fa = t/(t+att);
    env = fa*(fd+fd2);
    x = tt/per;
    if (tt<0) f0 = 4*(x+0.25)*erf(2*a*(x+0.25))-bet;
    else      f0 = bet-4*(x-0.25)*erf(2*a*(x-0.25));
    sb->data[i] += f0*env;
    tt += tau;
    if (tt>0.5*per) tt -= per;
    t  = t+tau;
    fd = fd*md;
    fd2= fd2*md2;
  }
  
  mdx = exp(-tau/dec3);   
  for (i=nn; i<nx; i++) {   
    x = tt/per;
    if (tt<0) f0 = 4*(x+0.25)*erf(2*a*(x+0.25))-bet;
    else      f0 = bet-4*(x-0.25)*erf(2*a*(x-0.25));
    sb->data[i] += f0*env;   
    tt += tau;  
    if (tt>0.5*per) tt -= per;  
    env = env*mdx;   
  }   
  

}


/* --- au_write_note --- */
void au_write_note (float t0, float tn, float tx, int hs, 
                    struct AU_QUALITY q, float amp, struct SBUF *sb)
{
  float freq, middlec=261.6,t1,t2;
  
  if (!active) return;

  if (hs==AU_IS_REST) return;

  freq = middlec * pow(2.0,hs/12.0);
  
  t1 = sb->t1;
  t2 = sb->t2;
  if (vb>=20)  
    printf ("Note %5.1f Hz  dur=%.2f  t  %.2f %.2f %.2f   buf %.2f %.2f\n",  
            freq, tn-t0,  t0,tn,tx, t1,t2);
  if (tx > t2) {
    au_write_sbuf (t1+tx-t2, sb);
    if (vb>=12)  
    printf ("au_write_note: shift buffer to %.2f %.2f\n", sb->t1, sb->t2);
  }
  
  if (t0 < sb->t1) t0 = sb->t1;

  if (q.type == AU_SINE) 
    au_add_osample (t0, tn, tx, freq, q, amp, sb);  
  if (q.type == AU_SAWTOOTH)
    au_add_ssample (t0,tn,tx,freq,q,amp,sb);
  if (q.type == AU_TRIANGLE)
    au_add_tsample (t0,tn,tx,freq,q,amp,sb);

}


/* --- au_clear_atab --- */
void au_clear_atab (int atab[])
{
  int i;
  for (i=0;i<7;i++) atab[i]=0;
}

/* --- au_copy_atab --- */
void au_copy_atab (int atab[], int atab1[])
{
  int i;
  for (i=0;i<7;i++) atab1[i]=atab[i];
}



/* --- au_set_atab: update atab for one pit,acc pair --- */
void au_set_atab (int pit, int acc, int atab[])
{
  int p;

  p=pit;
  while (p>22) p-=7;
  while (p<16) p+=7; 
  if (acc!=0) atab[p-16]=acc;

}


/* --- au_set_atabs: update atab for one note --- */
void au_set_atabs (int v, int k, int atab[])
{
  int pit,acc,j;

  for (j=0; j<symv[v][k].gr.n; j++) {
    pit=symv[v][k].gr.p[j];
    acc=symv[v][k].gr.a[j];
    au_set_atab (pit,acc,atab);
  }

  for (j=0; j<symv[v][k].npitch; j++) {
    pit=symv[v][k].pits[j];
    acc=symv[v][k].accs[j];
    au_set_atab (pit,acc,atab);
  }

}


/* --- hs_shift: return number of halfsteps between pitch and C --- */
int hs_shift (int pit, int acc, int atab[], struct KEYSTR key)
{
  int p,k,oct,sf;
  int ktab[7] = {0,2,4,5,7,9,11};    /* whole and half steps */
  int stab[7] = {2,4,6,1,3,5,7};     /* for sharps in keysig */
  int ftab[7] = {6,4,2,7,5,3,1};     /* for flats in keysig */

  p=pit;
  while (p>22) p-=7;
  while (p<16) p+=7; 

  k=ktab[p-16];
  sf = key.sf;

  if (acc==0) acc=atab[p-16];
  if (acc) {
    if (acc==A_SH) k=k+1;
    if (acc==A_DS) k=k+2;
    if (acc==A_FT) k=k-1;
    if (acc==A_DF) k=k-2;
  }
  else {
    if ((sf>0) && (sf>=stab[p-16])) k++;
    if ((sf<0) && (-sf>=ftab[p-16])) k--;
  }
  
  oct=(pit-p)/7;
  if (key.ktype==BASS) oct=oct-2;
  if (key.ktype==ALTO) oct=oct-1;

  k=k+12*oct;
  atab[p-16]=acc;

  return k;

}

/* --- au_play_note: write output for one note ---*/
void au_play_note (int v, int k, float t0, float tn, float tadd, float tgr, 
                   int atab[], struct KEYSTR key, struct AU_QUALITY q,
                   float amp, struct SBUF *sb)
{
  int ng,j,pit,acc,hs;
  float tg,dg;

  ng = symv[v][k].gr.n;
  dg = tgr/ng;
  tg = t0-tgr;

  for (j=0; j<ng; j++) {
    pit = symv[v][k].gr.p[j];
    acc = symv[v][k].gr.a[j];
    hs = hs_shift (pit,acc,atab,key); 
    au_write_note (tg,tg+dg,tg+dg+tadd,hs,q,amp,sb); 
    tg += dg;
  }
  
  for (j=0; j<symv[v][k].npitch; j++) {
    pit = symv[v][k].pits[j];
    acc = symv[v][k].accs[j];
    hs = hs_shift (pit,acc,atab,key); 
    au_write_note (t0,tn,tn+tadd,hs,q,amp,sb); 
  }    
    
}


/* --- au_flow_control: call at bar and end, returns xpos --- */
int au_flow_control (int i, int v, int k, struct AU_FLOWCTRL *fc)
{
  int ii;
  
  ii=i;

  /* Take care of missing :| at tune end */
  if (i==XP_END) {
    if ((!fc->repeat) && (fc->lrep!=XP_NIL) && (fc->lrep!=XP_START)) {
      fc->repeat=1; 
      ii=xp[fc->lrep].next; 
      fc->lrep=XP_NIL; 
      fc->nrep++;
    }
    return ii;
  }
  
  if (symv[v][k].type!=BAR) return i;

  if (!fc->repeat) {
    if (symv[v][k].u==B_LREP) fc->lrep=i;
    if ((symv[v][k].u==B_RREP) || (symv[v][k].u==B_DREP)) {
      if ((fc->lrep==XP_NIL) && (fc->rrep!=XP_NIL)) fc->lrep=fc->rrep;
      fc->rrep=i;
      if (fc->lrep!=XP_NIL) { 
        ii=fc->lrep; 
        fc->lrep=XP_NIL; 
        fc->nrep++;
        fc->repeat=1; 
      }
    }
  }
  
  else {
    if ((symv[v][k].u==B_RREP) || (symv[v][k].u==B_DREP)) {
      fc->repeat=0; 
      if (symv[v][k].u==B_DREP) fc->lrep=i;
    }
    if (symv[v][k].v==1) {
      if (fc->rrep!=XP_NIL) { 
        ii=fc->rrep; 
        fc->rrep=XP_NIL, 
        fc->repeat=0;
      }
    }
  }

/*|   if (ii!=i) printf ("FC: jump %d to %d  v=%d k=%d\n", i,ii,v,k); |*/
  return ii;
  
}



/* --- find_ties: return index of last note tied on --- */
/* Algorithm: a note is tied to the one before if
   (1) the tones are exactly the same, or
   (2) the posit on the staff is the same and there is no accidental.
   This should take care of ties over barlines, hopefully.  */

int find_ties (int i, int v, int k, int n, int atab[], struct KEYSTR key,
               struct AU_FLOWCTRL fc)
{ 
  int j,last,t,pit1,acc1,hs1,pit2,acc2,hs2,pitl,atab2[7],i2; 
  struct KEYSTR key2;
  struct AU_FLOWCTRL fc2;

  if (symv[v][k].slur_st==0) return k; 
  au_copy_atab (atab, atab2);
  key2=key;
  fc2=fc;
  
  pit1 = symv[v][k].pits[0];
  acc1 = symv[v][k].accs[0];
  hs1 = hs_shift(pit1,acc1,atab,key);
  last=k;
  pitl=pit1;
  
  i2=xp[i].next;
  for (;;) {
    j=xp[i2].p[v];
    t=symv[v][j].type; 
    
    if (t==REST)   return last;

    if (t==BAR) {   
      au_clear_atab (atab2);
      i2=au_flow_control (i2,v,j,&fc2);  
    }

    if (t==CLEF)   key2.ktype=symv[v][j].u; 
    if (t==KEYSIG) key2.sf=symv[v][j].q; 

    if (t==NOTE) { 
      pit2 = symv[v][j].pits[0];
      acc2 = symv[v][j].accs[0];
      hs2 = hs_shift(pit2,acc2,atab2,key2);
      if (symv[v][j].slur_end==0) return last;

      if (hs2 != hs1) {
        if (pit2 != pitl) return last;
        if (acc2 != 0)    return last;
      }

      if (symv[v][j].slur_st==0) return j;
      pitl=pit1;
      last=j;
    } 
    
    i2=xp[i2].next;
    if (i2==XP_END) break;
  } 
  return last;

} 


/* --- au_handle_gch --- */
void au_handle_gch (float t, float tempo, char *gch, char *gclast)
{
  char gc[NGC][TGC];
  int  dgc[NGC], ngc, j;
  float tgc;

  ngc = split_chord (gch, gc, dgc);
/*|   printf ("split_chord <%s> gave ngc=%d\n", gch, ngc);  |*/

  for (j=0; j<ngc; j++) {
    tgc = dgc[j] * 60.0 / (tempo*QUARTER);
    
    /* just add entries, last will be used if time duplicated */
    if (ngctab <= NGC-1) {
      gctab[ngctab].tgc = tgc + t;
      strcpy (gctab[ngctab].gch, gc[j]);
      ngctab ++;
/*|       printf ("  j=%d <%s> t=%.1f\n", j, gc[j], tgc); |*/
    }
    else
      printf ("+++ gctab overflow at %s\n", gch);

    strcpy (gclast, gc[ngc-1]);
  }

/*|    printf ("gctab has size %d:\n", ngctab);   |*/
/*|    for (j=0; j<ngctab; j++)    |*/
/*|      printf ("  %d  %.1f  %s\n", j, gctab[j].tgc, gctab[j].gch);   |*/
  
}    


/* --- au_play_notes ---*/
void au_play_notes (float tempo, struct AU_QUALITY q[], struct AU_QUALITY qac[])
{
  int v, i, n, k, t, v0;
  float tadd, tnow, tdone, tprt, dgr0, tgr1[AU_MAXV], tgr2, tend;
  float tbar, tbarnew, levx, tdec, amp, tstop;
  int in_tie[AU_MAXV], kk[AU_MAXV], kpt[AU_MAXV], k1, t1;
  int atab[AU_MAXV][7],  atab1[AU_MAXV][7];
  struct KEYSTR key[AU_MAXV], key1[AU_MAXV];
  float tn[AU_MAXV], td[AU_MAXV], tdacc;
  struct AU_FLOWCTRL fc;
  struct accpat bpat,  qpat,  tpat,  Bpat,  Qpat,  Tpat;
  char gclast[TGC];

  active = 1;
  if (pb_start > 1) {
    if (pb_start > barinit) active = 0; 
    if (pb_end   < barinit) active = 0; 
    if (vb > 3) printf ("Playback bars %d to %d\n", pb_start, pb_end);
  }
  

  if (do_acc) {
    set_accpat (acc_pat, voice[0].meter, &bpat, &qpat, &tpat, 
                &Bpat, &Qpat, &Tpat);
    if (vb > 3) print_accpat (&bpat, &qpat, &tpat, &Bpat, &Qpat, &Tpat, "");
    bpat.q = qpat.q = tpat.q = Tpat.q = qac[0];
    Bpat.q = Qpat.q = qac[1];
  }

  /* amplitude, decrease somewhat within one bar */
  levx = 0.8;
  tdec = 1.0;

  /* default length for grace notes */
  dgr0 = (QUARTER/5) *60.0/(tempo*QUARTER);

  /* leave time for decay after note stops */
  tadd=0.3;

  if (nvoice > AU_MAXV) rx("play_notes: increase AU_MAXV","");

  fc.rrep = XP_NIL;
  fc.lrep = XP_START;
  fc.repeat = fc.nrep = 0;
  tnow = tsave;
  tprt = tnow+20;
  tbar = tnow;
  tstop = 2e10;
  ngctab = 0;
  *gclast = '\0';
  current_bar = barinit;

  v0 = -1;
  for (v=0; v<nvoice; v++) {
    in_tie[v] = 0;
    tn[v] = td[v] = tnow;
    au_clear_atab (atab[v]);
    key[v]=voice[v].key0;
    kpt[v] = -1;
    tgr1[v] = -1;
    if (voice[v].draw && (v0 < 0)) v0 = v;
  }

  /* --- start loop over posits */
  i = xp[XP_START].next;

  for (;;) {
    tbarnew = -1;
    for (v=0; v<nvoice; v++) {
      k=xp[i].p[v];
      /* print dots for entertainment */
      if ((vb >= 1) && (tnow>tprt)) {printf("."); fflush(stdout); tprt+=20;}
      
      /* switch output on/off */
      if ((v == ivc0) && (k >= 0) && (symv[v][k].type == BAR)) { 
        if (vb > 9) 
          printf ("bar i=%d k=%d num=%d act=%d\n", i, k, symv[v][k].t, active);
        if (!active && (pb_start > 0) && (symv[v][k].t == pb_start)) {
          if (tsave > 0) au_write_sbuf (tsave, &sb);
          au_init_sbuf (&sb);
          active = 1;
          sb.t1  =  tnow - 0.005;  
          sb.t2  =  sb.t1 + sb.n * sb.tau;
          if (vb>3) printf("Playback ON at %d bar=%d\n", k, symv[v][k].t);  
        }
        
        if (active && (pb_end > 0) && ( symv[v][k].t == pb_end+1)) {
          tstop = tnow;
          if (vb>3) printf("Playback OFF at %d bar=%d\n", k, symv[v][k].t);  
        }
      }

      if (voice[v].draw && (k>=0)) {
        n  = voice[v].nsym;
        t  = symv[v][k].type;
        if (vb>18)  
          printf ("-- Sym: i=%d k=%d  tnow=%.3f   voice %d, type=%s\n",
                  i, k, tnow, v, symname(t));       
        
        /* if this a note or a rest, output the last one at k1 */
        if (( (t==NOTE) || (t==REST)) && (kpt[v]>=0)) {
          k1 = kpt[v];
          t1 = symv[v][k1].type;
          if (tgr1[v]<0) tgr1[v] = dgr0 * symv[v][k1].gr.n;
          tgr2 = dgr0*symv[v][k].gr.n;
          if (tgr2 > 0.5*(tnow-tn[v])) tgr2 = 0.5 * (tnow-tn[v]);
          if ((t1 == NOTE) && (!in_tie[v])) {
            tend = tnow - tgr2;
/*|         if (tend+tadd >= sb.t2)  |*/
/*|           printf ("Dump sbuf in posit loop tdone=%.2f tbar-2=%.2f\n", |*/
/*|                   tdone, tbar-2); |*/
            if (tend+tadd >= sb.t2) au_write_sbuf (tdone, &sb); 
/*|         if (tend+tadd >= sb.t2) au_write_sbuf (tbar-2, &sb);  |*/
            if (!au_nomusic && (tn[v] < tstop-.0001)) 
              au_play_note (v,k1,tn[v],tend,tadd,tgr1[v],atab1[v],key1[v],
                            q[v],amp,&sb);
            tn[v] = tnow;
            td[v] = tend;
          }
          if (t1==REST) { 
            tn[v] = tnow; 
            td[v] = tnow;
          } 
          tgr1[v]=tgr2;
        }
        
        switch (t) {
          
        case NOTE:
          /* gchords */
          if (do_acc && (v==0) && (strlen(symv[v][k].text) > 0)) 
            au_handle_gch (tnow-tbar, tempo, symv[v][k].text, gclast);
          au_set_atabs (v,k,atab[v]);
          if (in_tie[v]) {
            if (k==kk[v]) in_tie[v]=0;
            break;
          }
          kpt[v] = k;
          au_copy_atab (atab[v],atab1[v]);
          key1[v] = key[v];
          kk[v] = find_ties (i,v,k,n,atab[v],key[v],fc);
          amp=levx+(1-levx)*exp(-(tnow-tbar)/tdec);
          if (kk[v]>k) in_tie[v]=1;
          break;
          
        case REST:
          if (do_acc && (v==0) && (strlen(symv[v][k].text) > 0)) 
            au_handle_gch (tnow-tbar, tempo, symv[v][k].text, gclast);
          kpt[v] = k;
          break;
          
        case BAR:
          au_clear_atab (atab[v]);
          /* skip repeats and endings if bars were selected */
          if (pb_start < 0) i = au_flow_control (i, v, k, &fc);
          tbarnew = tnow;
          
          tdacc = -1;
          if (do_acc && (v == v0)) {
            au_play_pat (tnow, tbar, &bpat, tempo, &tdacc, tdone, &sb);
            au_play_pat (tnow, tbar, &qpat, tempo, &tdacc, tdone, &sb); 
            au_play_pat (tnow, tbar, &tpat, tempo, &tdacc, tdone, &sb); 
            au_play_pat (tnow, tbar, &Bpat, tempo, &tdacc, tdone, &sb);
            au_play_pat (tnow, tbar, &Qpat, tempo, &tdacc, tdone, &sb); 
            au_play_pat (tnow, tbar, &Tpat, tempo, &tdacc, tdone, &sb); 
          }

          if (*gclast) {
            strcpy (gctab[0].gch, gclast);
            gctab[0].tgc = 0;
            ngctab = 1;
          }
          current_bar = symv[v][k].t;
          break;
          
        case CLEF:
          key[v].ktype=symv[v][k].u; 
          break;
          
        case KEYSIG:
          key[v].sf=symv[v][k].q; 
          break;
          
        }
      }
    }
    
    /* output notes for acc patterns */
    if (do_acc) {
      if (tbarnew > -0.001) {
        tbar=tbarnew;
      }
    }
    
    /* determine which part of the buffer can be written out */
    if (vb>19) printf ("Update tdone for tnow=%.3f\n", tnow);
    tdone = tnow;
    if (tdacc < tdone) tdone = tdacc;
    for (v=0; v<nvoice; v++)  {
      if ((voice[v].draw) && (td[v]<tdone)) tdone = td[v]; 
      if (vb>18) 
        printf ("  vc %d  td=%.3f tdone=%.3f\n", v, td[v], tdone);
    }

    tnow = tnow + xp[i].dur * 60.0 / (tempo*QUARTER);
    if (active) tsave = tnow;
    i = xp[i].next;
    if (i == XP_END) i = au_flow_control (i, v, k, &fc);
    
    /* safety check */
    if (fc.nrep>20) {
      printf ("\n+++ Implausibly many repeats... terminate audio output\n");
      break;  
    }
    if (i == XP_END) break;
    if (tstop < 1e10) break;
  } 
  /* end of for(;;) loop */
  
  /* output the last note in each voice */
  for (v=0; v<nvoice; v++) {  
    if (voice[v].draw && (kpt[v]>=0)) { 
      k1 = kpt[v]; 
      if (symv[v][k1].type == NOTE) { 
        if (tgr1[v]<0) tgr1[v] = dgr0 * symv[v][k1].gr.n;
        if (tnow+tadd >= sb.t2) au_write_sbuf (tdone, &sb);  
        if (!au_nomusic)
          au_play_note (v,k1, tn[v], tnow, tadd, tgr1[v], atab1[v],
                        key1[v], q[v], amp,&sb); 
      }    
    } 
  }  
  
  if (vb>=1) printf(" "); 
  
}

/* -- prtr: utility for au_print_quality --- */
char* prtr (struct TRIPLE t, char str[])
{
  int num;
  float x;

  num=1;
  x=t.low-t.mid; if (x*x > 0.0000001) num=3;
  x=t.hi-t.mid;  if (x*x > 0.0000001) num=3;

  if (num==1) {
    x=sqrt(t.low*t.low);
    if (x < 0.01)
      sprintf (str,"%.5f", t.low);
    else if (x < 0.1) 
      sprintf (str,"%.3f", t.low);
    else
      sprintf (str,"%.2f", t.low);
  }
  else {
    x=t.hi*t.hi;
    if (t.mid*t.mid > x) x=t.mid*t.mid;
    if (t.low*t.low > x) x=t.low*t.low;
    x=sqrt(x);
    if (x < 0.01)
      sprintf (str,"%.5f %.5f %.5f", t.low, t.mid, t.hi); 
    else if (x < 0.1) 
      sprintf (str,"%.3f %.3f %.3f", t.low, t.mid, t.hi); 
    else
      sprintf (str,"%.2f %.2f %.2f", t.low, t.mid, t.hi); 
  }
  return str;
}


/* --- au_print_quality --- */
void au_print_quality (struct AU_QUALITY *q, int prt[])
{
  char t[81],tt[81];
  int n, num;

  /* whacked in, dont't print same data twice */
  n = q->num;
  if (prt[n]) return;
  prt[n] = 1;

  if (q->type==AU_SINE) { 
    num=0;
    if (q->oamp.low*q->oamp.low > 0.0000001) num++;
    if (q->oamp.mid*q->oamp.mid > 0.0000001) num++;
    if (q->oamp.hi *q->oamp.hi  > 0.0000001) num++;
    if (num)
      printf ("  sine wave plus overtones\n");
    else
      printf ("  sine wave (pure)\n");
  }
  if (q->type==AU_TRIANGLE) printf ("  triangle, smooth %s\n",prtr(q->sm,t));
  if (q->type==AU_SAWTOOTH) printf ("  sawtooth, smooth %s\n",prtr(q->sm,t));
  printf ("  attack:       %s\n", prtr(q->att,t));       
  printf ("  1st decay:    %s  with amplitude  %s\n", 
          prtr(q->dec1,t), prtr(q->amp1,tt));
  printf ("  2nd decay:    %s  with amplitude  %s\n", 
          prtr(q->dec2,t), prtr(q->amp2,tt));
  printf ("  final decay:  %s\n", prtr(q->dec3,t));       

  if ((q->type==AU_SINE) && num) {
/*| printf ("  1st decay:    %s  with amplitude  %s\n",  |*/
    printf ("  o-tone decay: %s  with amplitude  %s\n", 
            prtr(q->odec,t), prtr(q->oamp,tt));
    printf ("  1st overtone: %s\n", prtr(q->oa2,t));
    printf ("  2nd overtone: %s\n", prtr(q->oa3,t));
    printf ("  3d  overtone: %s\n", prtr(q->oa4,t));
    printf ("  4th overtone: %s\n", prtr(q->oa5,t));
  }
  
}

/* --- output_audio ---*/
void output_audio ()
{
  float tempo;
  struct AU_QUALITY qvc[AU_MAXV], qac[2];
  int l, i, prt[20];
  char auq[21], *r, q0, qa0, qa1;

  tempo = cfmt.playtempo;
  do_invert = 0;

  /* set audio quality for the voices/acc */
  strcpy (auq, cfmt.auquality);
  acc_pat[0] = cfmt.accomp; 
  acc_pat[1] = '\0';
  qa0 = 'p';
  qa1 = 'p';
  if (r = strchr(auq, '/')) {
    *r++ = 0;
    if (*r) { qa0 = qa1 = *r++; if (*r) qa1 = *r; }
  }
  qac[0] = au_set_quality (qa0);
  qac[1] = au_set_quality (qa1);

  l=strlen(auq);
  for (i=0; i<l; i++) qvc[i] = au_set_quality (auq[i]);
  if (nvoice>l) {
    q0 = 'p';
    if (l>0) q0 = auq[l-1];
    for (i=l;i<nvoice;i++) qvc[i] = au_set_quality (q0);
  }

  if (vb >= 4) {
    printf ("\nPlayback: tempo=%.0f\n", tempo);
    if (do_acc) {
      if (invert_chords) 
        printf ("Chords inverted to range %d, %d\n", AU_INV_MIN, AU_INV_MAX);
      else
        printf ("Chords in root position\n");
    }
    for (i=0; i<20;i++) prt[i]=0;
    for (i=0; i<nvoice; i++) {
      printf ("voice %d: use quality %d (%s)\n", i+1, qvc[i].num, qvc[i].name);
      if (vb >= 5) au_print_quality (&qvc[i], prt);
    }
    if (do_acc) {
      if (strcmp(acc_pat, "NONE")) {
        printf ("acc-trb: use quality %d (%s)\n", qac[0].num, qac[0].name);
        if (vb >= 5) au_print_quality (&qac[0], prt);
        printf ("acc-bs:  use quality %d (%s)\n", qac[1].num, qac[1].name);
        if (vb >= 5) au_print_quality (&qac[1], prt);
      }
    }
  }
  au_play_notes (tempo, qvc, qac);

}




