/*
 *  This file is part of jcabc2ps,
 *  Copyright (C) 1996,1997,1998  Michael Methfessel
 *  See file jcabc2ps.c for details.
 */

#include <string.h>
#include <ctype.h>

#include "jcabc2ps.h"
#include "buffer.h"
#include "parse.h"
#include "subs.h"
#include "util.h"
#include "pssubs.h"
#include "style.h"
#include "text.h"
#include "format.h"
#include "playback.h"

#include "music.h"

/*  subroutines connected with output of music  */

/* ----- nwid ----- */
/* Sets the prefered width for a note depending on the duration.
	Return value is default space on right and left side.
	Function is determined by values at 0, 1/2, 1.
	Return value is 1.0 for quarter note. */
float nwid (float dur)
{
	float a,b,x,p,x0,p0;

	a=2*(f1p-2*f5p+f0p);
	b=f1p-f0p-a;
	x = dur/(float)BASE;
	p = a*x*x + b*x + f0p;

	x0 = 0.25;
	p0 = a*x0*x0 + b*x0 + f0p;
	p  = p/p0;
	return p;
}

/* ----- xwid -- --- */
/* same as nwid but for stretched system */
float xwid (float dur)
{
	float a,b,x,p,x0,p0;

	a=2*(f1x-2*f5x+f0x);
	b=f1x-f0x-a;
	x = dur/(float)BASE;
	p = a*x*x + b*x + f0x;

	x0 = 0.25;
	p0 = a*x0*x0 + b*x0 + f0x;
	p  = p/p0;

	return p;
}

/* ----- next_note, prec_note ------ */
int next_note (k,n,symb)
int k,n;
Sym symb[];
{
	int i;
	for (i=k+1;i<n;i++) {
		if ((symb[i].type == NOTE)||(symb[i].type == REST)) return i;
	}
	return -1;
}

int prec_note (k,n,symb)
int k,n;
Sym symb[];
{
	int i;
	for (i=k-1;i>=0;i--) {
		if ((symb[i].type == NOTE)||(symb[i].type == REST)) return i;
	}
	return -1;
}


/* ----- followed_by_note ------ */
int followed_by_note (k,n,symb)
	int k,n;
	Sym symb[];
{
	int i;

	for (i=k+1;i<n;i++) {
		switch (symb[i].type) {
		case INVISIBLE:
			break;
		case NOTE:
		case REST:
			return i;
			break;
		default:
			return -1;
		}
	}
	return -1;
}

/* ----- preceded_by_note ------ */
int preceded_by_note (k,n,symb)
	int k,n;
	Sym symb[];
{
	int i;

	for (i=k-1;i>=0;i--) {
		switch (symb[i].type) {
		case INVISIBLE:
			break;
		case NOTE:
		case REST:
			return i;
			break;
		default:
			return -1;
		}
	}
	return -1;
}


void xch (i,j)  /* sub to exchange two integers */
int *i,*j;
{
	int k;
	k=*i;
	*i=*j;
	*j=k;
}



/* ----- print_linetype ----------- */
void print_linetype (t)
int t;
{

	if    (t == COMMENT)           fprintf(stderr,"COMMENT\n");
	elsif (t == MUSIC)             fprintf(stderr,"MUSIC\n");
	elsif (t == TO_BE_CONTINUED)   fprintf(stderr,"TO_BE_CONTINUED\n");
	elsif (t == E_O_F)             fprintf(stderr,"E_O_F\n");
	elsif (t == INFO)              fprintf(stderr,"INFO\n");
	elsif (t == TITLE)             fprintf(stderr,"TITLE\n");
	elsif (t == METER)             fprintf(stderr,"METER\n");
	elsif (t == PARTS)             fprintf(stderr,"PARTS\n");
	elsif (t == KEY)               fprintf(stderr,"KEY\n");
	elsif (t == XREF)              fprintf(stderr,"XREF\n");
	elsif (t == DLEN)              fprintf(stderr,"DLEN\n");
	elsif (t == HISTORY)           fprintf(stderr,"HISTORY\n");
	elsif (t == TEMPO)             fprintf(stderr,"TEMPO\n");
	elsif (t == BLANK)             fprintf(stderr,"BLANK\n");
	elsif (t == VOICE)             fprintf(stderr,"VOICE\n");
	else                           fprintf(stderr,"UNKNOWN LINE TYPE\n");
}

/* ----- print_syms: show sym properties set by parser ------
* This produces a dump of all the Syms.  
* Bug: Pitches need to be un-adjusted for clef and octave.
* How can we find the right offset for each Sym?
*/
void print_syms(num1,num2,symb)
	int   num1,num2;
	Sym   symb[];
{	char *F = "print_syms";
	int   a,acc,i,t,j,y;
	Ksig* ks;
	char  dsym[21] = {' ','~','.','J','M','H','u','v','R','T','K'};
	char  bsym[10]  = {'-', '1' ,'2', '3', '4', '5', '6', '7', '8', '9'};
	char  str[21];

	fprintf(stderr,"\n---------- Symbol list ----------\n");
	fprintf(stderr,"word  slur  eol  num  description\n");

	for (i=num1;i<num2;i++) {
		fprintf(stderr," %c %c   %c %c   %c  ",
						bsym[symb[i].word_st],
						bsym[symb[i].word_end],
						bsym[symb[i].slur_st],
						bsym[symb[i].slur_end],
						bsym[symb[i].eoln] );
		fprintf(stderr,"%4d  ", i);
		t=symb[i].type;
		switch (t) {

		case NOTE:
		case REST:
			if (t == NOTE) fprintf(stderr,"NOTE ");
			if (t == REST) fprintf(stderr,"REST ");
			if (symb[i].npitch>1) fprintf(stderr," [");
			for (j=0;j<symb[i].npitch;j++) {
				fprintf(stderr," ");
				if (symb[i].accs[j] == A_SH) fprintf(stderr,"^");
				if (symb[i].accs[j] == A_NT) fprintf(stderr,"=");
				if (symb[i].accs[j] == A_FT) fprintf(stderr,"_");
				if (symb[i].accs[j] == A_DS) fprintf(stderr,"^^");
				if (symb[i].accs[j] == A_DF) fprintf(stderr,"__");
				y = 3 * (symb[i].vpos[j]-18);	// + symb[i].yadd;
				V8 "%s: Sym %d y=%d from pits[%d]=%d vpos[%d]=%d pitadj=%d yadd=%d.\n",
					F,i,y,j,symb[i].pits[j],j,symb[i].vpos[j],symb[i].pitadj,symb[i].yadd V;
				strcpy (str,"z");
		//		if (t == NOTE) symbolic_pitch(symb[i].pits[j]-symb[i].pitadj,str);
				if (t == NOTE) symbolic_pitch(symb[i].pits[j],str);
				fprintf(stderr,"%s(%d)", str, symb[i].lens[j]);
			}
			if (symb[i].npitch>1) fprintf(stderr," ]");
			if (symb[i].p_plet)
				fprintf(stderr," (%d:%d:%d", symb[i].p_plet,symb[i].q_plet,symb[i].r_plet);
			if (symb[i].ach[0]) fprintf(stderr," \"%s\"",   symb[i].ach);
			if (symb[i].achp)   fprintf(stderr," pos %d",   symb[i].achp);
			if (symb[i].achy)   fprintf(stderr," at %6.3f", symb[i].achy);
			if (symb[i].ann) {
				V7 "%s: symb %d has %d-byte annotation !%s!\n",F,i,symb[i].ann->l,TextStr(symb[i].ann) V;
				fprintf(stderr," !%s!",     symb[i].ann->t);
				fprintf(stderr," pos %d",   symb[i].ann->p);
				fprintf(stderr," at %6.3f", symb[i].ann->y);
			}
			if (symb[i].dc.n>0) {
				fprintf(stderr,"  deco ");
				for (j=0;j<symb[i].dc.n;j++)
					fprintf(stderr,"%c",dsym[symb[i].dc.t[j]]);
			}
			if (symb[i].gr.n>0) {
				fprintf(stderr,"  grace ");
				for (j=0;j<symb[i].gr.n;j++) {
					if (j>0) fprintf(stderr,"-");
					if (symb[i].gr.a[j] == A_SH) fprintf(stderr,"^");
					if (symb[i].gr.a[j] == A_NT) fprintf(stderr,"=");
					if (symb[i].gr.a[j] == A_FT) fprintf(stderr,"_");
					symbolic_pitch (symb[i].gr.p[j],str);
					fprintf(stderr,"%s",str);
				}
			}
			break;

		case BAR:
			fprintf(stderr,"BAR ==  ==  == = ");
			if (symb[i].u == B_SNGL)   fprintf(stderr,"single");
			if (symb[i].u == B_DBL)    fprintf(stderr,"double");
			if (symb[i].u == B_LREP)   fprintf(stderr,"left repeat %d",symb[i].lrep);
			if (symb[i].u == B_RREP)   fprintf(stderr,"right repeat %d",symb[i].rrep);
			if (symb[i].u == B_DREP)   fprintf(stderr,"double repeat %d/%d",symb[i].lrep,symb[i].rrep);
			if (symb[i].u == B_FAT1)   fprintf(stderr,"thick-thin");
			if (symb[i].u == B_FAT2)   fprintf(stderr,"thin-thick");
			if (symb[i].u == B_FAT3)   fprintf(stderr,"thin-thick-thin");
			if (symb[i].u == B_INVIS)  fprintf(stderr,"invisible");
			if (symb[i].v)             fprintf(stderr,", ending %d", symb[i].v);
			if (symb[i].ach[0])        fprintf(stderr,", achord \"%s\"",symb[i].ach);
			if (symb[i].achy)          fprintf(stderr," at %6.3f", symb[i].achy);
			if (symb[i].ann) {
				fprintf(stderr,", annot !%s!", symb[i].ann->t);
				fprintf(stderr," pos %d",      symb[i].ann->p);
				fprintf(stderr," at %6.3f",    symb[i].ann->y);
			}
			if (symb[i].gr.n>0) {
				fprintf(stderr,"  grace ");
				for (j=0;j<symb[i].gr.n;j++) {
					if (j>0) fprintf(stderr,"-");
					if (symb[i].gr.a[j] == A_SH) fprintf(stderr,"^");
					if (symb[i].gr.a[j] == A_NT) fprintf(stderr,"=");
					if (symb[i].gr.a[j] == A_FT) fprintf(stderr,"_");
					symbolic_pitch (symb[i].gr.p[j],str);
					fprintf(stderr,"%s",str);
				}
			}
			break;

		case CLEF:
			switch (symb[i].u) {
			case NOCLEF:   fprintf(stderr,"CLEF  NOCLEF");   break;
			case NOCLEFd8: fprintf(stderr,"CLEF  NOCLEF-8"); break;
			case NOCLEFu8: fprintf(stderr,"CLEF  NOCLEF+8"); break;
			case TREBLE:   fprintf(stderr,"CLEF  TREBLE");   break;
			case TREBLEd8: fprintf(stderr,"CLEF  TREBLE-8"); break;
			case TREBLEu8: fprintf(stderr,"CLEF  TREBLE+8"); break;
			case TENOR:    fprintf(stderr,"CLEF  TENOR");    break;
			case ALTO:     fprintf(stderr,"CLEF  ALTO");     break;
			case ALTOd8:   fprintf(stderr,"CLEF  ALTO-8");   break;
			case ALTOu8:   fprintf(stderr,"CLEF  ALTO+8");   break;
			case BASS:     fprintf(stderr,"CLEF  BASS");     break;
			case BASSd8:   fprintf(stderr,"CLEF  BASS-8");   break;
			case BASSu8:   fprintf(stderr,"CLEF  BASS+8");   break;
			default:       fprintf(stderr,"CLEF=%d unknown",t); break;
			}
			break;

		case TIMESIG:
			fprintf(stderr,"TIMESIG  ");
			if    (symb[i].w == 1) fprintf(stderr,"C");
			elsif (symb[i].w == 2) fprintf(stderr,"C|");
			else                   fprintf(stderr,"%d/%d", symb[i].u,symb[i].v);
			break;

		case KEYSIG:
			fprintf(stderr,"KEYSIG   ");
			if (symb[i].t == A_SH) fprintf(stderr,"sharps ");
			if (symb[i].t == A_FT) fprintf(stderr,"flats ");
			if (symb[i].t == A_MX) fprintf(stderr,"mixed ");
			if (ks = symb[i].ksig) {
				fprintf(stderr," K:");
				for (a=0; a<ks->data.accs; a++) {
					acc = (ks->data.atyp[a] == A_SH) ? '^'
						: (ks->data.atyp[a] == A_FT) ? '_'
						: (ks->data.atyp[a] == A_NT) ? '='
						: '?';
					fprintf(stderr,"%c%c",acc,ks->data.aval[a]);
				}
			}
			fprintf(stderr," q=%d t=%d u=%d v=%d w=%d acc1=%d acc2=%d"
				,symb[i].q,symb[i].t,symb[i].u,symb[i].v,symb[i].w,symb[i].acc1,symb[i].acc2);
			if (symb[i].acc2 < symb[i].acc1)
				fprintf(stderr,", neutrals from %d", symb[i].acc2);
			break;

		case INVISIBLE:
			fprintf(stderr,"INVIS   ");
			break;

		default:
			fprintf(stderr,"UNKNOWN ");
			break;
		}
		fprintf(stderr,"\n"); 
	}
	fprintf(stderr,"\n");
}

/* ----- print_vsyms: print symbols for all voices ----- */
void print_vsyms ()
{	char *F = "print_vsyms";
	int v, s;

	fprintf(stderr,"\n");
	for (v = 0; v < nvoice; v++) {
//		if (nvoice>1)
			fprintf(stderr,"Voice <%s> (%d of %d)", voice[v].id,v,nvoice);
		print_syms(0,voice[v].nsym,symv[v]);
	}
}


/* ----- set_head_directions ----------- */

/* decide whether to shift heads to other side of stem on chords */
/* also position accidentals to avoid too much overlap */
void set_head_directions (s)
Sym *s;
{
	int i,n,nx,sig,d,da,shift,nac;
	int i1,i2,m;
	float dx,xx,xmn;

	n=s->npitch;
	sig=-1;
	if (s->stem>0) sig=1;
	for (i=0;i<n;i++) {
		s->shhd[i]=0;
		s->shac[i]=8;
		if (s->head == H_OVAL) s->shac[i]+=3;
		s->xmn=0;
		s->xmx=0;
	}
	if (n<2) return;

	/* sort heads by pitch */
	for (;;) {
		nx=0;
		for (i=1;i<n;i++) {
			if ((s->vpos[i] - s->vpos[i-1]) * sig > 0) {
				xch(&s->pits[i],&s->pits[i-1]);
				xch(&s->vpos[i],&s->vpos[i-1]);
				xch(&s->lens[i],&s->lens[i-1]);
				xch(&s->accs[i],&s->accs[i-1]);
				xch(&s->sl1[i],&s->sl1[i-1]);
				xch(&s->sl2[i],&s->sl2[i-1]);
				xch(&s->ti1[i],&s->ti1[i-1]);
				xch(&s->ti2[i],&s->ti2[i-1]);
				nx++;
			}
		}
		if (!nx) break;
	}

	shift=0;                                 /* shift heads */
	for (i=n-2;i>=0;i--) {
		d = s->vpos[i+1] - s->vpos[i];
		if (d<0) d=-d;
		if ((d>=2) || (d == 0))
			shift=0;
		else {
			shift=1-shift;
			if (shift) {
				dx=7.8;
				if (s->head == H_EMPTY) dx=7.8;
				if (s->head == H_OVAL)  dx=10.0;
				if (s->stem == -1) s->shhd[i]=-dx;
				else             s->shhd[i]=dx;
			}
		}
		if (s->shhd[i] < s->xmn) s->xmn = s->shhd[i];
		if (s->shhd[i] > s->xmx) s->xmx = s->shhd[i];
	}

	shift=0;                                 /* shift accidentals */
	i1=0; i2=n-1;
	if (sig<0) { i1=n-1; i2=0; }
	for (i=i1; ; i=i+sig) {          /* count down in terms of pitch */
		xmn=0;                         /* left-most pos of a close head */
		nac=99;                        /* relative pos of next acc above */
		for (m=0; m<n; m++) {
			xx = s->shhd[m];
			d  = s->vpos[m] - s->vpos[i];
			da=ABS(d);
			if ((da<=5) && (s->shhd[m]<xmn)) xmn=s->shhd[m];
			if ((d>0) && (da<nac) && s->accs[m]) nac=da;
		}
		s->shac[i]=8.5-xmn+s->shhd[i];   /* aligns accidentals in column */
		if (s->head == H_EMPTY) s->shac[i] += 1.0;
		if (s->head == H_OVAL)  s->shac[i] += 3.0;
		if (s->accs[i]) {
			if (nac>=6)                        /* no overlap */
				shift=0;
			elsif (nac>=4) {                 /* weak overlap */
				if (shift == 0) shift=1;
				else          shift=shift-1;
			}
			else {                             /* strong overlap */
				if      (shift == 0) shift=2;
				elsif (shift == 1) shift=3;
				elsif (shift == 2) shift=1;
				elsif (shift == 3) shift=0;
			}

			while (shift>=4) shift -=4;
			s->shac[i] += 3*shift;
		}
		if (i == i2) break;
	}

}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* set_minsyms: want at least one symbol in each voice 
*/
void set_minsyms(ivc)
	int  ivc;
{	char*F="set_minsyms";
	int  n2;

	n2 = voice[ivc].nsym;	// Last sym in voice
	V3 "%s: n2=%d\n",F,n2 V;
	if (n2>0) return;		// There's alredy one sym?
	zeroSym(&symv[ivc][n2],"symv/set_minsyms");
	symv[ivc][n2].type = INVISIBLE;
	symv[ivc][n2].u    = 3;
	symv[ivc][n2].v    = 3;
	symv[ivc][n2].w    = 3;
	voice[ivc].nsym++;		// Count the invisible sym
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* setSymChrs: set symbol characteristics
*/
void setSymChrs (n1,n2,sp)
	int n1,n2;
	Sym *sp;
{	char *F="setSymChrs";
	int i,np,m,ymn,ymx;
	float yav,yy;

	for (i=n1;i<n2;i++) {
		if ((sp[i].type == NOTE)||(sp[i].type == REST)) {
			sp[i].y = 3 * (sp[i].vpos[0]-18);	// + sp[i].yadd;
			V8 "%s: Sym %d y=%.2f from pits[%d]=%d vpos[%d]=%d pidadj=%d yadd=%d.\n",
				F,i,sp[i].y,0,sp[i].pits[0],0,sp[i].vpos[0],sp[i].pitadj,sp[i].yadd V;
			if (sp[i].type == REST) sp[i].y=12;
			V6 "%s: Sym %d x=%.2f y=%.2f w=[%.2f,%.2f] p=[%.2f,%.2f] x=[%.2f,%.2f]\n",
				F,i,sp[i].x,sp[i].y,sp[i].wl,sp[i].wr,sp[i].pl,sp[i].pr,sp[i].xl,sp[i].xr V;
			yav=0;
			ymn=1000;
			ymx=-1000;
			np=sp[i].npitch;
			for (m=0; m<np; m++) {
				yy = 3 * (sp[i].vpos[m]-18);	// + sp[i].yadd;
				V8 "%s: y=%d from pits[m=%d]=%d vpos[m=%d]=%d pidadj=%d yadd=%d.\n",
					F,m,sp[i].pits[m],m,sp[i].vpos[m],sp[i].pitadj,sp[i].yadd V;
				yav = yav + yy / np;
				if (yy < ymn) ymn = yy;
				if (yy > ymx) ymx = yy;
			}
			sp[i].ymn=ymn;
			sp[i].ymx=ymx;
			sp[i].yav=yav;
			sp[i].ylo=ymn;
			sp[i].yhi=ymx; 
		}
	}
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* set_beams: decide on beams 
*/
void set_beams (n1,n2,symb)
int n1,n2;
Sym symb[];
{
	int j,lastnote,start_flag;

	/* separate words at notes without flags */
	start_flag=0;
	lastnote=-1;
	for (j=n1;j<n2;j++) {
		if (symb[j].type == NOTE) {
			if (start_flag) {
				symb[j].word_st=1;
				start_flag=0;
			}
			if (symb[j].flags == 0) {
				if (lastnote>=0) symb[lastnote].word_end=1;
				symb[j].word_st=1;
				symb[j].word_end=1;
				start_flag=1;
			}
			lastnote=j;
		}
	}

}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* setStem: set stem directions and lengths.
* This is really just a debug wrapper around the assignment,
* though other stem-related code may be moved here eventually.
*/
void setStem(n,sp,dir,C)
	int  n;		/* Symbol number */
	Sym* sp;	/* Add stem to this symbol */
	int  dir;	/* 1=up -1=down */
	char*C;		/* Caller's name */
{	char*F="setStem";
	unless (sp) {V1 "%s: Called by %s with sp null!!!\n",F,C V; return;}
	sp->stem = dir;
	V5 "%s: Stem of symbol %d is %d.\n",C,n,dir V;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* setStems: decide on stem directions and lengths
*/
void setStems (n1,n2,symb)
	int n1,n2;
	Sym symb[];
{	char *F="setStems";
	int beam,j,k,n,stem,laststem;
	float avg,slen,lasty,dy;

	/* set stem directions; near middle, use previous direction */
	beam=0;
	laststem=0;
	for (j=n1; j<n2; j++) {
		if (symb[j].type == NOTE) { 
			setStem(j, &symb[j], 0, F);
			if (symb[j].len < WHOLE) setStem(j, &symb[j], 1, F);
			if (symb[j].yav >= 12) setStem(j, &symb[j], -symb[j].stem, F);
			if ((symb[j].yav > 11) && (symb[j].yav < 13) && (laststem != 0)) {
				dy=symb[j].yav-lasty;
				if ((dy>-7) && (dy<7)) setStem(j, &symb[j], laststem, F);
			} 
			if (symb[j].word_st && (!symb[j].word_end)) {   /* start of beam */
				avg=0;
				n=0;
				for (k=j;k<n2;k++) {
					if (symb[k].type == NOTE) {
						avg = avg + symb[k].yav;
						n++;
					}
					if (symb[k].word_end) break;
				}
				avg = avg / n;
				stem = 1;
				if (avg >= 12) stem = -1;
				if ((avg > 11) && (avg < 13) && (laststem != 0)) stem = laststem;
				beam = 1;
			}

			if (beam) setStem(j, &symb[j], stem, F);
			if (symb[j].word_end) beam = 0;
			if (bagpipe) setStem(j, &symb[j], -1, F);
			if (symb[j].len>=WHOLE) setStem(j, &symb[j], 0, F);
			laststem = symb[j].stem;
			if (symb[j].len >= HALF) laststem = 0;
			lasty = symb[j].yav;
		} else {
			laststem = 0; 
		}
	}

	/* shift notes in chords (need stem direction to do this) */
	for (j=n1;j<n2;j++)
		if (symb[j].type == NOTE) set_head_directions (&symb[j]);

	/* set height of stem end, without considering beaming for now */
	for (j=n1; j<n2; j++) if (symb[j].type == NOTE) {
		slen=STEM;
		if (symb[j].npitch>1) slen = STEM_CH;
		if (symb[j].flags == 3) slen += 4;
		if (symb[j].flags == 4) slen += 9;
		if ((symb[j].flags>2) && (symb[j].stem == 1)) slen -= 1;
		if (symb[j].stem == 1) {
			symb[j].y  = symb[j].ymn;
			symb[j].ys = symb[j].ymx+slen;
		} elsif (symb[j].stem == -1) {
			symb[j].y  = symb[j].ymx;
			symb[j].ys = symb[j].ymn-slen;
		} else {
			symb[j].y  = symb[j].ymx;
			symb[j].ys = symb[j].ymx;
		}
	}
}

/* ----- set_sym_times: set time axis; also count through bars ----- */
int set_sym_times(n1,n2,symb,meter0)
	int n1,n2;
	Sym symb[];
	Msig meter0;
{	char *F = "set_sym_times";
	int i,pp,qq,rr,meter1,meter2,count,bnum,lastb,bsave;
	int qtab[] = {0,0,3,2,3,0,2,0,3,0};
	float time, factor, fullmes;

	meter1 = meter0.meter1;
	meter2 = meter0.meter2;
	V3 "%s: meter1=%d meter2=%d.\n",F,meter1,meter2 V;
	lastb=bnum=0;
	bsave=1;
	time=0.0;
	factor=1.0;
	for (i=n1;i<n2;i++) {
		symb[i].time=time;
		/* count through bar numbers, put into symb.t */
		if (symb[i].type == BAR) {
			fullmes = (WHOLE*meter1)/(meter2?meter2:1);
			if (bnum == 0) {
				bnum = barinit;
				if (fullmes < time+0.001) bnum++;
				symb[i].t = bnum;
				lastb = i;
			} elsif (time-symb[lastb].time>=fullmes-0.001) {
				bnum++;
				while (symb[i+1].type == BAR) {i++; symb[i].time=time; }
				symb[i].t = bnum;
				lastb = i;
			}
			if (symb[i].v > 1) {
				bnum = bsave;
				symb[i].t = 0;
			}
			if (symb[i].v == 1) bsave=bnum;
		}
		if ((symb[i].type == NOTE)||(symb[i].type == REST)) {
			if (symb[i].p_plet) {
				pp=symb[i].p_plet;
				qq=symb[i].q_plet;
				rr=symb[i].r_plet;
				if (qq == 0) {
					qq=qtab[pp];
					if (qq == 0) {
						qq=2;
						if (meter1 % 3 == 0) qq = 3;
					}
				}
				factor=((float)qq)/((float)pp);
				count=rr;
			}
			time=time+factor*symb[i].len;
			if (count>0) count--;
			if (count == 0) factor=1;
		}
		if (symb[i].type == TIMESIG) {     /* maintain meter as we go along */
			meter1 = symb[i].u;
			meter2 = symb[i].v;
			V3 "%s: TIMESIG symb[%d] meter1=u=%d meter2=v=%d.\n",F,meter1,meter2 V;
		}

	}
	return bnum;
}

/* ----- setSymWidths: set widths and prefered space --- */
/* This routine sets the minimal left and right widths wl,wr
	so that successive symbols are still separated when
	no extra glue is put between them. It also sets the prefered
	spacings pl,pr for good output and xl,xr for expanded layout.
	All distances in pt relative to the symbol center.
*/

void setSymWidths (ns1,ns2,symb,ivc)
	int ns1,ns2,ivc;
	Sym symb[];
{	char *F = "setSymWidths";
	int i,n,j0,j,m,n1,n2,bt,k,st,sl,k1,k2,got_note,ok,wr;
	float xx,w,swfac,spc,dur,yy;
	char t[81],tt[81];
	V6 "%s: Called with ns1=%d ns2=%d symb[%d]\n",F,ns1,ns2,ivc V;
	swfac = 1.0;
	if (strstr(cfmt.vocalfont.name,"Times-Roman"))    swfac=1.00;
	if (strstr(cfmt.vocalfont.name,"Times-Courier"))  swfac=1.05;
	if (strstr(cfmt.vocalfont.name,"Helvetica"))      swfac=1.00;
	if (strstr(cfmt.vocalfont.name,"Helvetica-Bold")) swfac=1.05;

	got_note = 0;
	for (i=ns1; i<ns2; i++) {
		st = symb[i].type;
		V6 "%s: Symb[%d].type = %d = \"%s\"\n",F,i,st,symname[st] V;
		switch (st) {

		case INVISIBLE:   /* empty space; shrink,space,stretch from u,v,w */
			symb[i].wl = symb[i].wr = 0.5*symb[i].u;
			symb[i].pl = symb[i].pr = 0.5*symb[i].v;
			symb[i].xl = symb[i].xr = 0.5*symb[i].w;
			break;

		case NOTE:
		case REST:
			got_note=1;
			dur=symb[i].len;
			symb[i].wl=symb[i].wr=4.5;
			if (symb[i].head == H_EMPTY) {symb[i].wl=6.0; symb[i].wr=14.0;}
			if (symb[i].head == H_OVAL)  {symb[i].wl=8.0; symb[i].wr=18.0;}
			symb[i].pl=symb[i].xl=symb[i].wl;
			symb[i].pr=symb[i].xr=symb[i].wr;

			/* room for shifted heads and accidental signs */
			for (m=0;m<symb[i].npitch;m++) {
				xx=symb[i].shhd[m];
				AT_LEAST (symb[i].wr, xx+6);
				AT_LEAST (symb[i].wl, -xx+6);
				if (symb[i].accs[m]) {
					xx=xx-symb[i].shac[m];
					AT_LEAST (symb[i].wl, -xx+3);
				}
				AT_LEAST (symb[i].xl, -xx+3);
				symb[i].pl=symb[i].wl;
				symb[i].xl=symb[i].wl;
			}

			/* room for slide */
			for (k=0;k<symb[i].dc.n;k++) {
				if (symb[i].dc.t[k] == D_SLIDE) symb[i].wl += 10;
			}

			/* room for grace notes */
			if (symb[i].gr.n>0) {
				xx=GSPACE0;
				if (symb[i].gr.a[0]) xx=xx+3.5;
				for (j=1;j<symb[i].gr.n;j++) {
					xx=xx+GSPACE;
					if (symb[i].gr.a[j]) xx=xx+4;
				}
				symb[i].wl = symb[i].wl + xx+1;
				symb[i].pl = symb[i].pl + xx+1;
				symb[i].xl = symb[i].xl + xx+1;
			}

			/* space for flag if stem goes up on standalone note */
			if (symb[i].word_st && symb[i].word_end)
				if ((symb[i].stem == 1) && symb[i].flags>0)
					AT_LEAST (symb[i].wr, 12);

			/* leave room for dots */
			if (symb[i].dots>0) {
				AT_LEAST (symb[i].wr,12+symb[i].xmx);
				if (symb[i].dots>=2) symb[i].wr=symb[i].wr+3.5;
				n = (int) symb[i].y;
				/* special case: standalone with up-stem and flags */
				if (symb[i].flags && (symb[i].stem == 1) && !(n%6)) {
					if ((symb[i].word_st == 1) && (symb[i].word_end == 1)) {
						symb[i].wr = symb[i].wr + DOTSHIFT;
						symb[i].pr = symb[i].pr + DOTSHIFT;
						symb[i].xr = symb[i].xr + DOTSHIFT;
					}
				}
			}

			/* extra space when down stem follows up stem */
			j0=preceded_by_note(i,ns2,symb);
			if ((j0>=0) && (symb[j0].stem == 1) && (symb[i].stem == -1))
				AT_LEAST (symb[i].wl, 7);

			/* make sure helper lines don't overlap */
			if ((j0>=0) && (symb[i].y>27) && (symb[j0].y>27))
				AT_LEAST (symb[i].wl, 7.5);

			/* leave room accompaniment chord */
			if (strlen(symb[i].ach)>0) {
				/*
				* Special case: accomp chord under ending. Leave some room
				* to the left of the note.  Adjust for length of ending achord [jc]
				*/
				V3 "%s: symb %d chord \"%s\" under ending %d (%s)\n"
					,F,i,symb[i].ach,symb[i-1].v,symb[i].vtxt V;
				if ((i>0) && (symb[i-1].type == BAR)) {
					V3 "%s: BAR sym %d.\n",F,i V;
					if (bt = symb[i-1].v) {
						wr = 8 * strlen(symb[i-1].vtxt);
						V3 "%s: BAR sym %d bt=%d wr=%d.",F,i-1,bt,wr V;
						if (bt) AT_LEAST(symb[i].wl,wr);
						V3 "%s: symb[%d].vtxt=\"%s\" u=%d wr=%d.\n"
							,F,i,symb[i].vtxt,symb[i].u,symb[i].wr V;
					}
				}
#if 0
				V6 "%s: symb[%d].vtxt=\"%s\" u=%d wr=%d.\n"
					,F,i,symb[i].vtxt,symb[i].u,symb[i].wr V;
				if (*symb[i].vtxt) {
					symb[i].wr += strlen(symb[i].vtxt);
					V3 "%s: symb[%d].vtxt=\"%s\" u=%d wr=%d.\n"
						,F,i,symb[i].vtxt,symb[i].u,symb[i].wr V;
				}
#endif
				/* rest is same for all accomp chord cases */
				tex_str(symb[i].ach,t,&w);
				xx=cfmt.achordfont.size*w;
				xx=xx*1.05;     /* extra space mainly for helvetica font */
				spc=xx*GCHPRE;
				k1=prec_note(i,ns2,symb);
				k2=next_note(i,ns2,symb);
				if (spc>8.0) spc=8.0;
				if ((k1) && (strlen(symb[k1].ach)>0))
					AT_LEAST (symb[i].wl, spc);
				if ((k2>0) && (strlen(symb[k2].ach)>0))
					AT_LEAST (symb[i].wr, xx-spc);
			}

			/* leave room annotation */
			if (symb[i].ann) {
				/*
				* Special case: annotations under ending. Leave some room
				* to the left of the note.  Adjust for length of ending text [jc]
				*/
				V3 "%s: symb %d annotation \"%s\" under ending %d (%s)\n"
					,F,i,symb[i].ann->t,symb[i-1].v,symb[i].vtxt V;
				if ((i>0) && (symb[i-1].type == BAR)) {
					V3 "%s: BAR sym %d.\n",F,i V;
					if (bt = symb[i-1].v) {
						wr = 8 * strlen(symb[i-1].vtxt);
						V3 "%s: BAR sym %d bt=%d wr=%d.",F,i-1,bt,wr V;
						if (bt) AT_LEAST(symb[i].wl,wr);
						V3 "%s: symb[%d].vtxt=\"%s\" u=%d wr=%d.\n"
							,F,i,symb[i].vtxt,symb[i].u,symb[i].wr V;
					}
				}
#if 0
				V6 "%s: symb[%d].vtxt=\"%s\" u=%d wr=%d.\n"
					,F,i,symb[i].vtxt,symb[i].u,symb[i].wr V;
				if (*symb[i].vtxt) {
					symb[i].wr += strlen(symb[i].vtxt);
					V3 "%s: symb[%d].vtxt=\"%s\" u=%d wr=%d.\n"
						,F,i,symb[i].vtxt,symb[i].u,symb[i].wr V;
				}
#endif
				/* rest is same for all annotation cases */
				tex_str(symb[i].ann->t,t,&w);
				xx=cfmt.annotfont.size*w;
				xx=xx*1.05;     /* extra space mainly for helvetica font */
				spc=xx*GCHPRE;
				k1=prec_note(i,ns2,symb);
				k2=next_note(i,ns2,symb);
				if (spc>8.0) spc=8.0;
				if (symb[k1].ann) {
					if ((k1>0) && symb[k1].ann  && symb[k1].ann->t) AT_LEAST (symb[i].wl, spc);
					if ((k2>0) && symb[k2].ann  && symb[k2].ann->t) AT_LEAST (symb[i].wr, xx-spc);
				}
			} 
			/* leave room for vocals under note */
			for (j=0;j<NWLINE;j++) {
				if (symb[i].wordp[j]) {
					sl=tex_str(symb[i].wordp[j],t,&w);
					xx = swfac * cfmt.vocalfont.size * (w+1*cwid(' '));
					AT_LEAST (symb[i].wl,xx*VOCPRE);
					AT_LEAST (symb[i].wr,xx*(1.0-VOCPRE));
				}
			}

			AT_LEAST (symb[i].pl, symb[i].wl);
			AT_LEAST (symb[i].xl, symb[i].wl);
			AT_LEAST (symb[i].pr, symb[i].wr);
			AT_LEAST (symb[i].xr, symb[i].wr);
			break;

		case BAR:
			symb[i].wl=symb[i].wr=0;
			if (symb[i].u == B_SNGL)  {
				symb[i].wl=symb[i].wr=3;
			} else
			if (symb[i].u == B_DBL )  {
				symb[i].wl=7;
				symb[i].wr=4;
			} else
			if (symb[i].u == B_LREP)  {
				V6 "%s: Left repeat at sym %d.\n",F,i V;
				if (symb[i].lrep < 1) {		/* [jc] */
					V3 "%s: ### symb[%d].lrep = %d.\n",F,i,symb[i].lrep V;
					symb[i].lrep = 1;
				}
				symb[i].wl = 1;
				symb[i].wr = 5 + 6 * symb[i].lrep;
				V6 "%s: Left repeat %d gives wl=%d wr=%d.\n"
					,F,symb[i].lrep,symb[i].w,symb[i].wr V;
			} else
			if (symb[i].u == B_RREP)  {
				V6 "%s: Right repeat at sym %d.\n",F,i V;
				if (symb[i].rrep < 1) {		/* [jc] */
					V3 "%s: ### symb[%d].rrep = %d.\n",F,i,symb[i].rrep V;
					symb[i].rrep = 1;
				}
				symb[i].wr = 1;
				symb[i].wl = 5 + 6 * symb[i].rrep;
				V6 "%s: Right repeat %d gives wl=%d wr=%d.\n"
					,F,symb[i].rrep,symb[i].w,symb[i].wr V;
			} else
			if (symb[i].u == B_DREP)  {
				V6 "%s: Double repeat at sym %d.\n",F,i V;
				symb[i].wl=symb[i].wr=12;
			} else
			if (symb[i].u == B_FAT1)  {	// Thick+thin bars
				symb[i].wl=3;
				symb[i].wr=9;
			} else
			if (symb[i].u == B_FAT2)  {	// Thin+thick bars
				symb[i].wl=9;
				symb[i].wr=3;
			} else
			if (symb[i].u == B_FAT3)  {	// THin+thick+thin bars
				symb[i].wl=9;
				symb[i].wr=9;
			}

			if (ivc == ivc0) {       /* bar numbers and labels next */
				ok=0;
				if ((cfmt.barnums>0) && (symb[i].t%cfmt.barnums == 0)) {
					ok=1;
				}
				if (strlen(symb[i].ach)>0) ok=1;
				if (symb[i].ann) ok=1;
				if (ok) {
					if (strlen(symb[i].ach)>0) {
						tex_str(symb[i].ach,t,&w);
						xx=cfmt.barlabelfont.size*w*0.5;
					} elsif (symb[i].ann) {
						tex_str(symb[i].ann->t,t,&w);
						xx=cfmt.barlabelfont.size*w*0.5;
					} else {
						sprintf (tt,"%d",symb[i].t);
						tex_str(tt,t,&w);
						xx=cfmt.barnumfont.size*w*0.5;
					}
					yy=60;
					if (!got_note) yy=0;
					if (symb[i-1].type == NOTE) {
						yy=symb[i-1].ymx;
						if (symb[i-1].stem == 1) yy=yy+26;
					}
					if (strlen(symb[i-1].ach)>0) yy=60;
					if (symb[i-1].ann) yy=60;
					AT_LEAST (symb[i].wl, 2);
					if (yy>BNUMHT-4.0) AT_LEAST (symb[i].wl, xx+1);
					if (!got_note) AT_LEAST (symb[i].wl, xx+1);
					yy=0;
					if (symb[i+1].type == NOTE) yy=symb[i+1].ymx;
					if (yy>BNUMHT-4.0) AT_LEAST (symb[i].wr, xx+2);
					if (strlen(symb[i+1].ach)>0) AT_LEAST (symb[i].wr, xx+4);
					if (symb[i+1].ann) AT_LEAST (symb[i].wr, xx+4);
				}
			}

			symb[i].pl = symb[i].wl;
			symb[i].pr = symb[i].wr;
			symb[i].xl = symb[i].wl;
			symb[i].xr = symb[i].wr;
			break;

		case CLEF:
/*
			V5 "%s: CLEF Sym %d has u=%d v=%d w=%d t=%d q=%d clef=%d acc1=%d acc2=%d.\n",F,
				i,symb[i].u,symb[i].v,symb[i].w,symb[i].t,symb[i].q,symb[i].clef,symb[i].acc1,symb[i].acc2 V;
			switch (symb[i].u) {		// Clef widths constant except for "none"
			case NOCLEF:
				symb[i].wl = symb[i].wr = symb[i].xl = 0;
				symb[i].pl = symb[i].pr = symb[i].xr = 0;
				break;
			default:
				symb[i].wl = symb[i].wr = symb[i].xl =  9.5;
				symb[i].pl = symb[i].pr = symb[i].xr =  9.5;
			}
*/
			V5 "%s: CLEF Sym %d has u=%d v=%d w=%d t=%d q=%d clef=%d acc1=%d acc2=%d.\n",F,
				i,symb[i].u,symb[i].v,symb[i].w,symb[i].t,symb[i].q,symb[i].clef,symb[i].acc1,symb[i].acc2 V;
			switch (symb[i].u) {		// Clef widths constant except for "none"
			case NOCLEF:
				symb[i].wl = symb[i].wr = symb[i].xl = 0;
				symb[i].pl = symb[i].pr = symb[i].xr = 0;
				break;
			default:
				symb[i].wl = symb[i].wr = symb[i].xl = 13.5;
				symb[i].pl = symb[i].pr = symb[i].xr = 11.5;
				symb[i].pl = symb[i].wl = symb[i].xl =  7.0;	// [jc] Shifted clefs left to edge of staff
			}
			V5 "%s: CLEF Sym %d has pl=%d pr=%d wl=%d wr=%d xl=%d xr=%d.\n",F,
				i,symb[i].pl,symb[i].pr,symb[i].wl,symb[i].wr,symb[i].xl,symb[i].xr V;
			break;

		case KEYSIG:
			V6 "%s: KEYSIG Sym %d has u=%d v=%d w=%d t=%d q=%d clef=%d acc1=%d acc2=%d.\n",F,
				i,symb[i].u,symb[i].v,symb[i].w,symb[i].t,symb[i].q,symb[i].clef,symb[i].acc1,symb[i].acc2 V;
			n1 = symb[i].u >= 0 ? symb[i].u : symb[i].clef;
			n2 = symb[i].v >= 0 ? symb[i].v : symb[i].acc1;
			if (symb[i].ksig) {
				V6 "%s: KEYSIG %s.\n",F,dmpKsig(symb[i].ksig) V;
				V6 "%s: KEYSIG accs=%d.\n",F,symb[i].ksig->data.accs V;
			} else {
				V6 "%s: KEYSIG no ksig field.\n",F V;
			}
			if (n2>=n1) {
				V6 "%s: KEYSIG Sym %d n2=%3.1f >= n1=%3.1f.\n",F,i,n2,n1 V;
				symb[i].wl = 3;				/* Was 2 [jc] */
				symb[i].wr = 6*(n2-n1+1)-2;	/* Was +2 [jc] */
				symb[i].pl = symb[i].wl;
				symb[i].pr = symb[i].wr;
				symb[i].xl = symb[i].wl;
				symb[i].xr = symb[i].wr;
			} else {
				V6 "%s: KEYSIG Sym %d n2=%3.1f < n1=%3.1f.\n",F,i,n2,n1 V;
				symb[i].wl = symb[i].pl = symb[i].xl = 1;	/* Was 3 [jc] */
				symb[i].wr = symb[i].pr = symb[i].xr = 0;	/* Was 3 [jc] */
			}
			V6 "%s: KEYSIG Sym %d set pl=%3.1f pr=%3.1f.\n",F,i,symb[i].pl,symb[i].pr V;
			V6 "%s: KEYSIG Sym %d set wl=%3.1f wr=%3.1f.\n",F,i,symb[i].wl,symb[i].wr V;
			V6 "%s: KEYSIG Sym %d set xl=%3.1f xr=%3.1f.\n",F,i,symb[i].xl,symb[i].xr V;
			break;

		case TIMESIG:
			V3 "%s: TIMESIG u=%d v=%d w=%d.\n",F,symb[i].u,symb[i].v,symb[i].w V;
			switch (symb[i].w) {
			case 4:			// Free meter.
				V3 "%s: TIMESIG (none)\n",F V;
				symb[i].wl = 0.0;
				symb[i].wr = 0.0;
				symb[i].pl = 0.0;
				symb[i].pr = 0.0;
				symb[i].xl = 0.0;
				symb[i].xr = 0.0;
				break;
			default:
				V3 "%s: TIMESIG %d/%d\n",F,symb[i].u,symb[i].v V;
				symb[i].wl = 6+4*(strlen(symb[i].ach)-1);	// Was 8+4*...
				symb[i].wr = symb[i].wl;	// Was +4;
				symb[i].pl = symb[i].wl;
				symb[i].pr = symb[i].wr;
				symb[i].xl = symb[i].wl;
				symb[i].xr = symb[i].wr;
				break;
			}
			V3 "%s: TIMESIG symb[%d] wl=%6.3f pl=%6.3f xl=%6.3f.\n",F,i,symb[i].wl,symb[i].pl,symb[i].xl V;
			V3 "%s: TIMESIG symb[%d] wr=%6.3f pr=%6.3f xr=%6.3f.\n",F,i,symb[i].wr,symb[i].pr,symb[i].xr V;
			break;

		default:
			fprintf(stderr,">>> cannot set width for sym type %d\n", symb[i].type);
			symb[i].wl = symb[i].wr = symb[i].xl = 0;
			symb[i].pl = symb[i].pr = symb[i].xr = 0;
			break;
		}
	}
}



/* ----- contract_ksigs: delete duplicate keysigs at staff start ----
* Depending on line breaks, a key and/or meter change can come
*	at the start of a staff. Solution: scan through symbols from start
*	of line as long as sym is a CLEF, KEYSIG, or TIMESIG. Remove all
*	these, but set flags so that set_initsyms will draw the
*	key and meter which result at the end of the scan.
*/
int contract_ksigs (ip1)
	int   ip1;
{	char *F = "contract_ksigs";
	int   i,k,v,t,n3,sf,jp1,m,mtop,type;
	Ksig *ks;
	V6 "%s: Called with ip1=%d.\n",F,ip1 V;
	mtop = 10000;
	for (v=0; v<nvoice; v++) {
		i = ip1;
		m = 0;
		V6 "%s: Voice v=%d i=%d m=%d.\n",F,v,i,m V;
		for (;;) {
			m++;
			k = xp[i].p[v];
			V6 "%s: Voice v=%d i=%d k=%d m=%d.\n",F,v,i,k,m V;
			if (k>=0) {
				type = symv[v][k].type;
				V6 "%s: Voice %d symv[%d][%d].type=%d=%s.\n",F,v,v,k,type,SymName(type) V;
				if (type == CLEF) {
					voice[v].key->data.clef = symv[v][k].u;
					symv[v][k].type=INVISIBLE;
				} elsif (type == KEYSIG) {
					V6 "%s: KEYSIG Sym[%d][%d] q=%d t=%d u=%d v=%d w=%d acc1=%d acc2=%d ksig=%X.\n",
						F,v,i,symv[v][k].q,symv[v][k].t,symv[v][k].u,symv[v][k].v,symv[v][k].w,
						symv[v][k].acc1,symv[v][k].acc2,symv[v][k].ksig V;
					sf = symv[v][k].v >= 0 ? symv[v][k].v  : symv[v][k].acc1;
					n3 = symv[v][k].w >= 0 ? symv[v][k].w  : symv[v][k].acc2;
					if (n3-1 < sf) {
						V6 "%s: Adjust sf from %d to %d.\n",F,sf,n3-1 V;
						sf = n3-1;
					}
					t  = symv[v][k].t;
					if (t == A_FT) sf = -sf;
					if (t == A_NT) sf = 0;
					voice[v].key->data.sf = sf;
					if (ks = symv[v][k].ksig) {
						V6 "%s: Sym[%d][%d].ksig is %s\n",F,v,k,dmpKsig(ks) V;
						voice[v].key = asgnKsig(voice[v].key, ks);
					}
					V6 "%s: Sym[%d][%d] final sf is %d.\n",F,v,i,sf V;
					symv[v][k].type = INVISIBLE;
					symv[v][k].acc1 = sf;
					symv[v][k].acc2 = n3;
				} elsif (type == TIMESIG) {
					voice[v].meter.insert = 1;
					voice[v].meter.meter1 = symv[v][k].u; V3 "%s: voice[%d].meter.meter1 = symv[%d][%d].u = %d.\n",F,v,v,k,voice[v].meter.meter1 V;
					voice[v].meter.meter2 = symv[v][k].v; V3 "%s: voice[%d].meter.meter2 = symv[%d][%d].v = %d.\n",F,v,v,k,voice[v].meter.meter1 V;
					voice[v].meter.mflag  = symv[v][k].w; V3 "%s: voice[%d].meter.mflag  = symv[%d][%d].w = %d.\n",F,v,v,k,voice[v].meter.mflag  V;
					strcpy(voice[v].meter.top,symv[v][k].ach);
					symv[v][k].type = INVISIBLE;
				} else {
					V6 "%s: Ignore type %d.\n",F,type V;
					break;
				}
			} else {
				V3 "%s: +++ k=%d = xp[%d].p[%d] +++\n",F,k,i,v V;
			}
			V6 "%s: Advance i from %d to %d.\n",F,i,xp[i].next V;
			i = xp[i].next;
			if (i == XP_END) {
				i = xp[i].prec;
				break;
			}
		}
		if (m < mtop && i >= 0) {mtop = m; jp1 = i; }
	}

	/* set glue for first symbol */
	xp[jp1].shrink = xp[jp1].wl+3;
	xp[jp1].space  = xp[jp1].wl+9;
	xp[jp1].stretch= xp[jp1].wl+16;

	return jp1;

}

/* ----- set_initsyms: set symbols at start of staff ----- */
int set_initsyms (v,wid0)
	int v;
	float *wid0;
{	char  *F = "set_initsyms";
	int    a,k,t,n;
	float  x;

	k = 0;

	/* add clef */
	zeroSym(&sym_st[v][k],"sym_st/set_initsyms 1");
	sym_st[v][k].type = CLEF;
	sym_st[v][k].u    = voice[ivc].key->data.clef;
	sym_st[v][k].v    = 0;
	k++;

	/* add keysig */
	zeroSym(&sym_st[v][k],"sym_st/set_initsyms 2");
	sym_st[v][k].type = KEYSIG;
	a = voice[ivc].key->data.accs;
	n = voice[ivc].key->data.sf;
	t = A_SH;
	if (n<0) { n = -n; t = A_FT; }
	sym_st[v][k].u = 1;
	sym_st[v][k].v = a ? a : n;
	sym_st[v][k].w = 100;
	sym_st[v][k].t = t;
	k++;

	/* add timesig */
	if (voice[ivc].meter.insert) {
		zeroSym(&sym_st[v][k],"sym_st/set_initsyms 3");
		sym_st[v][k].type = TIMESIG;
		sym_st[v][k].u    = voice[ivc].meter.meter1; V5 "%s: sym_st[%d].u = voice[%d].meter.meter1 = %d.\n",F,v,ivc,sym_st[v][k].u V;
		sym_st[v][k].v    = voice[ivc].meter.meter2; V5 "%s: sym_st[%d].v = voice[%d].meter.meter2 = %d.\n",F,v,ivc,sym_st[v][k].v V;
		sym_st[v][k].w    = voice[ivc].meter.mflag;  V5 "%s: sym_st[%d].2 = voice[%d].meter.mflag  = %d.\n",F,v,ivc,sym_st[v][k].w V;
		strcpy(sym_st[v][k].ach,voice[ivc].meter.top);
		k++;
		voice[ivc].meter.insert=0;
	}

	if (voice[ivc].insert_btype) {
		sym_st[v][k].type = BAR;
		sym_st[v][k].u=voice[ivc].insert_btype;
		sym_st[v][k].v=voice[ivc].insert_num;
		sym_st[v][k].t=voice[ivc].insert_bnum;
		strcpy(sym_st[v][k].ach,voice[ivc].insert_text);
		voice[ivc].insert_btype=0;
		voice[ivc].insert_bnum=0;
		k++;
	}

	n = k;
	setSymWidths(0,n,sym_st[v],ivc);

	x = 0;
	for (k=0; k<n; k++) {
		x = x+sym_st[v][k].wl;
		sym_st[v][k].x = x;
		x = x+sym_st[v][k].wr;
	}

	*wid0 = x+voice[v].insert_space;
	return n;

}


/* ----- print_poslist ----- */
void print_poslist ()
{
	int i,n,typ,vv;

	fprintf(stderr,"\n----------- xpos list -----------\n");
	fprintf(stderr," num ptr  type      time   dur     width    tfac"
					"   shrk  spac  stre  eol  vptr\n");

	n=0;
	i=xp[XP_START].next;
	for (;;) {
		typ=xp[i].type;
		fprintf(stderr,"%3d %3d  %d ", n,i,typ);
		if      (typ == NOTE)       fprintf(stderr,"NOTE");
		elsif (typ == REST)       fprintf(stderr,"REST");
		elsif (typ == KEYSIG)     fprintf(stderr,"KEY ");
		elsif (typ == TIMESIG)    fprintf(stderr,"TSIG");
		elsif (typ == BAR)        fprintf(stderr,"BAR ");
		elsif (typ == CLEF)       fprintf(stderr,"CLEF");
		elsif (typ == INVISIBLE)  fprintf(stderr,"INVS");
		else                      fprintf(stderr,"????");
		fprintf(stderr,"  %7.2f %6.2f  %4.1f %4.1f %5.2f  %5.1f %5.1f %5.1f",
						xp[i].time,xp[i].dur,xp[i].wl,xp[i].wr,xp[i].tfac,
						xp[i].shrink,xp[i].space,xp[i].stretch);
		if (xp[i].eoln)
			fprintf(stderr,"  %d  ",xp[i].eoln);
		else
			fprintf(stderr,"  -  ");
		for (vv=0;vv<nvoice;vv++) {
			if (xp[i].p[vv]>=0)
				fprintf(stderr," %2d",xp[i].p[vv]);
			else
				fprintf(stderr,"  -");
		}
		fprintf(stderr,"\n");
		i=xp[i].next;
		n++;
		if (i == XP_END) break;
	}

}




/* ----- insert_poslist: insert new element after element nins ----- */
int insert_poslist (nins)
int nins;
{
	int new,nxt,vv;

	new=ixpfree;
	ixpfree++;
	if (new>=XP_END)
		rxi("Too many symbols; use -maxs to increase limit, now ",maxSyms);

	nxt=xp[nins].next;
	xp[new].prec=nins;
	xp[new].next=nxt;
	xp[nins].next=new;
	xp[nxt].prec=new;
	for (vv=0;vv<nvoice;vv++) xp[new].p[vv]=-1;

	return new;

}

/* ----- set_poslist: make list of horizontal posits to align voices --- */
void set_poslist ()
{
	int i,n,v,vv,typ,nok,nins;
	float tol=0.01;
	float d,tim;

	/* initialize xp with data from top nonempty voice, ivc0 */
	v=ivc0;
	n=0;
	xp[0].next=1;
	for (i=0;i<voice[v].nsym;i++) {
		n++;
		xp[n].prec=n-1;
		xp[n].next=n+1;
		symv[v][i].p=n;
		for (vv=0;vv<nvoice;vv++) xp[n].p[vv]=-1;
		xp[n].p[v]=i;
		typ=symv[v][i].type;
		if (typ == REST) typ=NOTE;
		xp[n].type = typ;
		xp[n].time = symv[v][i].time;
		xp[n].dur = xp[n].tfac = 0;
		xp[n].shrink = xp[n].space = xp[n].stretch = 0;
		xp[n].wl = xp[n].wr = 0;
		xp[n].eoln=symv[v][i].eoln;
	}
	xp[n].next=XP_END;
	xp[XP_END].prec=n;
	ixpfree=n+1;

	/* find or insert syms for other voices */
	for (v=0;v<nvoice;v++) {
		if (voice[v].draw && v!=ivc0) {
			n=XP_START;
			for (i=0;i<voice[v].nsym;i++) {
				tim=symv[v][i].time;
				typ=symv[v][i].type;
				if (typ == REST) typ=NOTE;
				nok=-1;
				nins=n;
				for (;;) {
					n=xp[n].next;
					if (n == XP_END) break;
					d=xp[n].time-tim;
					if (xp[n].time<tim-tol) nins=n;
					if (d*d<tol*tol && xp[n].type == typ) {
						nok=n;
						break;
					}
					if (xp[n].time>tim+tol) break;
				}
				if (nok>0)
					n=nok;
				else {
					n=insert_poslist (nins);
					xp[n].type=typ;
					xp[n].time=tim;
					xp[n].dur = xp[n].tfac = 0;
					xp[n].shrink = xp[n].space = xp[n].stretch = 0;
					xp[n].wl = xp[n].wr = 0;
					xp[n].eoln=0;
				}
				symv[v][i].p=n;
				xp[n].p[v]=i;
			}
		}
	}

/*|   print_poslist (); |*/

}


/* ----- set_xpwid: set symbol widths and tfac in xp list ----- */
void set_xpwid()
{
	int i,j,k,i1,i2,k1,k2,v,nsm;
	float fac,dy,ff,wv,wx;

	/* set all tfacs to 1.0 */
	i=i1=xp[XP_START].next;
	for (;;) {
		xp[i].tfac=1.0;
		xp[i].wl=xp[i].wr=WIDTH_MIN;
		i2=i;
		i=xp[i].next;
		if (i == XP_END) break;
	}

	/* loop over voices. first voice last, assumed most important */
	for (v=nvoice-1;v>=0;v--) {
		nsm=voice[v].nsym;

		/* first symbol and last symbol */
		k1=symv[v][0].p;
		k2=symv[v][nsm-1].p;
		if (k1 == i1 && symv[v][0].wl>xp[k1].wl)     xp[k1].wl=symv[v][0].wl;
		if (k2 == i2 && symv[v][nsm-1].wr>xp[k2].wr) xp[k2].wr=symv[v][nsm-1].wr;

		/* loop over symbol nn pairs */
		for (i=1;i<nsm;i++) {
			j=i-1;
			k1=symv[v][j].p;
			k2=symv[v][i].p;
			if (xp[k1].next == k2) {
				if (symv[v][j].wr > xp[k1].wr) xp[k1].wr = symv[v][j].wr;
				if (symv[v][i].wl > xp[k2].wl) xp[k2].wl = symv[v][i].wl;

				if (symv[v][j].type == NOTE && symv[v][i].type == NOTE) {
					fac=1.0;
					/* reduce distance under a beam */
					if (symv[v][i].word_st == 0) fac=fac*fnnp;
					/* reduce distance for large jumps in pitch */
					dy = symv[v][i].y - symv[v][j].y;
					if (dy<0) dy=-dy;
					ff=1-0.010*dy;
					if (ff<0.9) ff=0.9;
					fac=fac*ff;
					xp[k2].tfac=fac;
				}
			}
		}
	}

	/* check for all nn pairs in voice, in case some syms very wide */
	for (v=nvoice-1;v>=0;v--) {
		nsm=voice[v].nsym;
		for (i=1;i<nsm;i++) {
			j=i-1;
			k1=symv[v][j].p;
			k2=symv[v][i].p;
			if (xp[k1].next!=k2) {
				wv=symv[v][j].wr+symv[v][i].wl;
				wx=xp[k1].wr+xp[k2].wl;
				k=k1;
				for (;;) {
					k=xp[k].next;
					if (k == k2) break;
					wx=wx+xp[k].wl+xp[k].wr;
				}
				if(wx<wv) {
					fac=wv/wx;
					xp[k1].wr=fac*xp[k1].wr;
					xp[k2].wl=fac*xp[k2].wl;
					k=k1;
					for (;;) {
						k=xp[k].next;
						if (k == k2) break;
						xp[k].wl=fac*xp[k].wl;
						xp[k].wr=fac*xp[k].wr;
					}

				}
			}
		}
	}

}


/* ----- set_spaces: set the shrink,space,stretch distances ----- */
void set_spaces ()
{
	int i,j,n,nxt,typ,typl,meter1,meter2;
	float w0,w1,w2;
	float vbnp,vbnx,vnbp,vnbx;

	/* note lengths for spaces at bars. Use dcefault meter for now */
	meter1=default_meter.meter1;
	meter2=default_meter.meter2;
	vbnp=(rbnp*meter1*BASE)/meter2;
	vbnx=(rbnx*meter1*BASE)/meter2;
	vnbp=(rnbp*meter1*BASE)/meter2;
	vnbx=(rnbx*meter1*BASE)/meter2;

	/* redefine durations as differences in start times */
	n=0;
	i=xp[XP_START].next;
	for (;;) {
		nxt=xp[i].next;
		if (nxt!=XP_END) xp[i].dur=xp[nxt].time-xp[i].time;
		i=nxt;
		n++;
		if (i == XP_END) break;
	}

	i=xp[XP_START].next;
	j=-1;
	typl=0;
	for (;;) {
		nxt=xp[i].next;
		typ=xp[i].type;

		/* shrink distance is sum of left and right widths */
		if (j>=0)
			xp[i].shrink=xp[j].wr+xp[i].wl;
		else
			xp[i].shrink=xp[i].wl;
		xp[i].space=xp[i].stretch=xp[i].shrink;

		if (xp[i].type == NOTE) {

			if (typl == NOTE) {             /* note after another note */
				w1 = lnnp*nwid(xp[j].dur);
				w2 = lnnp*nwid(xp[i].dur);
				xp[i].space = bnnp*w1 + (1-bnnp)*0.5*(w1+w2);
				w1 = lnnx*xwid(xp[j].dur);
				w2 = lnnx*xwid(xp[i].dur);
				xp[i].stretch = bnnx*w1 + (1-bnnx)*0.5*(w1+w2);
			} else {                        /* note at start of bar */
				w1 = lbnp*nwid(xp[i].dur);
				w0 = lbnp*nwid(vbnp);
				if (w0>w1) w0=w1;
				xp[i].space = bbnp*w1 + (1-bbnp)*w0 + xp[j].wr;
				if (xp[i].space<14.0) xp[i].space=14.0;
				w1 = lbnx*xwid(xp[i].dur);
				w0 = lbnx*xwid(vbnp);
				if (w0>w1) w0=w1;
				xp[i].stretch = bbnx*w1 + (1-bbnx)*w0 + xp[j].wr;
				if (xp[i].stretch<18.0) xp[i].stretch=18.0;
				if (xp[i].shrink<12.0) xp[i].shrink=12.0;
			}
		} else {                         /* some other symbol after note */
			if (typl == NOTE) {
				w1 = lnbp*nwid(xp[j].dur);
				w0 = lnbp*nwid(vnbp);
				xp[i].space = bnbp*w1 + (1-bnbp)*w0 + xp[i].wl;
				if (xp[i].space<13.0) xp[i].space = 13.0;
				w1 = lnbx*xwid(xp[j].dur);
				w0 = lnbx*xwid(vnbx);
				xp[i].stretch = bnbx*w1 + (1-bnbx)*w0 + xp[i].wl;
				if (xp[i].stretch<17.0) xp[i].stretch = 17.0;
			}
		} 
		/* multiply space and stretch by factors tfac */
		xp[i].space   = xp[i].space*xp[i].tfac;
		xp[i].stretch = xp[i].stretch*xp[i].tfac;

		/* make sure that shrink < space < stretch */
		if (xp[i].space<xp[i].shrink)  xp[i].space=xp[i].shrink;
		if (xp[i].stretch<xp[i].space) xp[i].stretch=xp[i].space;

		j=i;
		typl=typ;
		i=nxt;

		if (i == XP_END) break;
	}

	if (verbose>=11) print_poslist();

}

/* ----- check_overflow: returns upper limit which fits on staff ------ */
int check_overflow (int ip1, int ip2, float width)
{

	int i,jp2,lastcut,nbar,need_note;
	float space,shrink,stretch,alfa,alfa0;

	/* max shrink is alfa0 */
	alfa0=ALFA_X;
	if (cfmt.continueall) alfa0=cfmt.maxshrink;
	if (gmode == G_SHRINK)  alfa0=1.0;
	if (gmode == G_SPACE)   alfa0=0.0;
	if (gmode == G_STRETCH) alfa0=1.0;

	jp2=ip2;
	space=shrink=stretch=0;
	lastcut=-1;
	nbar=0;
	need_note=1;
	i=ip1;
	for (;;) {
		space=space+xp[i].space;
		shrink=shrink+xp[i].shrink;
		stretch=stretch+xp[i].stretch;
		alfa=0;
		if (space>shrink) {
			alfa=(space-width)/(space-shrink);
			if (xp[i].type!=BAR)
				alfa=((space+8)-width)/(space-shrink);
		}

		if (alfa>alfa0) {
			if (!cfmt.continueall) {
				if (verbose<=3) fprintf(stderr,"\n");
				fprintf(stderr,"+++ Overfull after %d bar%s in staff %d\n",
						nbar, nbar == 1 ? "" : "s", mline);
			}
			jp2=i;
			if (i == ip1) jp2=xp[i].next;
			if (lastcut>=0) jp2=xp[lastcut].next;
			break;
		}
		/* The need_note business is to cut at the first of consecutive bars */
		if (xp[i].type == NOTE)  need_note=0;
		if (xp[i].type == BAR && need_note == 0) {lastcut=i; need_note=1; nbar++; }
		if (xp[i].type == KEYSIG) lastcut=i;
		i=xp[i].next;
		if (i == ip2) break;
	}

	return jp2;

}


/* ----- set_glue --------- */
float set_glue (int ip1, int ip2, float width)
{ 	char *F = "set_glue";
	int i,j;
	float space,shrink,stretch,alfa,beta,glue,w,x,d1,d2,w1;
	float alfa0,beta0;

	alfa0=ALFA_X;                       /* max shrink and stretch */
	if (cfmt.continueall) alfa0=cfmt.maxshrink;
	if (gmode == G_SHRINK)  alfa0=1.0;
	if (gmode == G_SPACE)   alfa0=0.0;
	if (gmode == G_STRETCH) alfa0=1.0;
	beta0=BETA_X;
	if (cfmt.continueall) beta0=BETA_C;


	space=shrink=stretch=0;
	i=ip1;
	for (;;) {
		space=space+xp[i].space;
		shrink=shrink+xp[i].shrink;
		stretch=stretch+xp[i].stretch;
		j=i;
		i=xp[i].next;
		if (i == ip2) break;
	}

	/* add extra space if last symbol is not a bar */
	if (xp[j].type!=BAR) {
		d1=d2=xp[j].wr+3;
		if (xp[j].type == NOTE) d2 = lnbp*nwid(xp[j].dur)+xp[j].wr;
		if (d2<d1) d2=d1;
		shrink  = shrink  + d1;
		space   = space   + d2;
		stretch = stretch + d2;
	}

	/* select alfa and beta */
	alfa=beta=0;
	if (space>width) {
		alfa=99;
		if (space>shrink) alfa=(space-width)/(space-shrink);
	} else {
		beta=99;
		if (stretch>space) beta=(width-space)/(stretch-space);
		if (!cfmt.stretchstaff) beta=0;
	}

	if (gmode == G_SHRINK)  { alfa=1; beta=0;}     /* force minimal spacing */
	if (gmode == G_STRETCH) { alfa=0; beta=1;}     /* force stretched spacing */
	if (gmode == G_SPACE)   { alfa=beta=0;   }     /* force natural spacing */

/*|   if (alfa>alfa0) { alfa=alfa0; beta=0; } |*/

	if (beta>beta0) {
		if (!cfmt.continueall) {
			V3 "\n%s: +++ Underfull (%.0fpt of %.0fpt) in staff %d\n",F,
				(beta0*stretch+(1-beta0)*space)*cfmt.scale,cfmt.staffwidth,mline V;
		}
		alfa=0;
		if (!cfmt.stretchstaff) beta=0;
		if ((!cfmt.stretchlast) && (ip2 == XP_END)) {
			w1=alfa_last*shrink+beta_last*stretch+(1-alfa_last-beta_last)*space;
			if (w1<width) {
				alfa=alfa_last;    /* shrink undefull last line same as previous */
				beta=beta_last;
			}
		}
	}

	w=alfa*shrink+beta*stretch+(1-alfa-beta)*space;
	if (vb>=2) {
		if    (alfa > 0) fprintf(stderr,"Shrink staff %.0f%%",  100*alfa);
		elsif (beta > 0) fprintf(stderr,"Stretch staff %.0f%%", 100*beta);
		else             fprintf(stderr,"No shrink or stretch");
		fprintf(stderr," to width %.0f (%.0f,%.0f,%.0f)\n",w,shrink,space,stretch);
	}

	/* now calculate the x positions */
	x=0;
	i=ip1;
	for (;;) {
		glue=alfa*xp[i].shrink+beta*xp[i].stretch+(1-alfa-beta)*xp[i].space;
		x=x+glue;
		xp[i].x=x;
		if (verbose>22) fprintf(stderr,"pos[%d]: type=%d  pos=%.2f\n", i,xp[i].type,x);
		i=xp[i].next;
		if (i == ip2) break;
	}

	alfa_last=alfa;
	beta_last=beta;
	return w;

}


/* ----- adjGroup: even out spacings for one group of notes --- */
/* Here we repeat the whole glue thing, in rudimentary form */
void adjGroup(i1,i2)
int i1,i2;
{
	int j;
	float dx,x,spa,shr,str,hp,hx,alfa,beta,dur1;

	dx=sym[i2].x-sym[i1].x;
	shr=sym[i1].wr+sym[i2].wl;
	for (j=i1+1;j<i2;j++) shr=shr+sym[j].wl+sym[j].wr;
	dur1=sym[i1].len;
	hp=lnnp*nwid(dur1);
	hx=lnnx*xwid(dur1);
	spa = (i2-i1)*hp;
	str = (i2-i1)*hx;

	alfa=beta=0;
	if (dx>spa)
		beta=(dx-spa)/(str-spa);
	else
		alfa=(dx-spa)/(shr-spa);

	x=sym[i1].x;
	for (j=i1+1;j<=i2;j++) {
		x=x+alfa*(sym[j-1].wr+sym[j].wl)+beta*hx+(1-alfa-beta)*hp;
		sym[j].x=x;
	}

}


/* ----- adjSpace: even out triplet spacings etc --- */
void adjSpace (n)
int n;
{
	int i,i1,count,beam,num;

	/* adjust the n-plets */
	count=0;
	for (i=1;i<n;i++) {
		if ((sym[i].type == NOTE)||(sym[i].type == REST)) {
			if (sym[i].p_plet) {
				i1=i;
				count=sym[i].r_plet;
			}
			if (count>0 && sym[i].len!=sym[i1].len) count=0;
			if (count == 1) adjGroup (i1,i);
			if (count>0) count--;
		}
		else
			count=0;
	}

	/* adjust beamed notes of equal duration */
	beam=0;
	for (i=1;i<n;i++) {
		if ((sym[i].type == NOTE)||(sym[i].type == REST)) {
			if (sym[i].word_st && (!sym[i].word_end)) {
				i1=i;
				beam=1;
				if (sym[i].p_plet) beam=0;          /* don't do nplets here */
			}
			if (beam && sym[i].len!=sym[i1].len) beam=0;
			if (beam && sym[i].word_end) {
				num=i-i1+1;
				if (num>2 && num<4) adjGroup (i1,i);
			}
			if (sym[i].word_end) beam=0;
		}
		else
			beam=0;
	}

}


/* ----- adjRests: position single rests in bar center */
void adjRests (n,v)
int n,v;
{
	int i,ok;

	for (i=2;i<n-1;i++) {
		if ((sym[i].type == REST) && sym[i].fullmes) {

			ok=1;
			if ((sym[i-1].type == REST) || (sym[i-1].type == NOTE)) ok=0;
			if ((sym[i+1].type == REST) || (sym[i+1].type == NOTE)) ok=0;

			if (ok) {
				sym[i].head = H_OVAL;
				sym[i].dots = 0;
				sym[i].x = 0.5*(sym[i-1].x+sym[i+1].x);
			}

		}
	}

}



/* ----- copy_vsyms: copy selected syms for voice to v sym --- */
int copy_vsyms (int v, int ip1,int ip2, float wid0)
{
	int i,n,m,k;
	float d1,d2,r,x;

	/* copy staff initialization symbols */
	n=0;
	for (i=0;i<nsym_st[v];i++) {
		sym[n]=sym_st[v][i];
		n++;
	}
	/* copy real symbols, shifted by wid0 */
	i=ip1;
	m=0;
	for (;;) {
		k=xp[i].p[v];
		if (k >= 0) {
			sym[n]=symv[v][k];
			sym[n].x=xp[i].x+wid0;
			n++;
			m++;
		}
		i=xp[i].next;
		if (i == ip2) break;
	}

	/* adjust things for more pretty output.. */
	adjRests (n,v);
	if (mvoice>1) adjSpace (n);

	/* small random shifts make the output more human... */
	for (i=1;i<n-1;i++) {
		if ((sym[i].type == NOTE) || (sym[i].type == REST)) {
			d1=sym[i].x-sym[i-1].x;
			d2=sym[i+1].x-sym[i].x;
			r=RANFAC*d1;
			if (d2<d1) r=RANFAC*d2;
			if (r>RANCUT) r=RANCUT;
			x=ranf(-r,r);
			sym[i].x=sym[i].x+x;
		}
	}
	return n;
}


/* ----- draw_timesig ------- */
void draw_timesig (float x, Sym s)
{	char *F = "draw_timesig";
	switch (s.w) {
//	case 0:
//		V3 "%s: u=%d v=%d w=%d ignored.\n",F,s.u,s.v,s.w V;
//		break;
	case 4:		/* No timesig */
		V3 "%s: u=%d v=%d w=%d (none).\n",F,s.u,s.v,s.w V;
		V3 "%s: %.1f nsig\n", F, x V;
		PUT1("%.1f nsig\n", x)
		break;
	case 1:
		V3 "%s: %.1f csig\n", F, x V
		PUT1("%.1f csig\n", x)
		break;
	case 2:
		PUT1("%.1f ctsig\n", x)
		break;
	default:					/* Two-part timesig */
		V3 "%s: %.1f (%s) (%d) tsig\n", F, x, s.ach, s.v V;
		PUT3("%.1f (%s) (%d) tsig\n", x, s.ach, s.v)
//		PUT3("%.1f (%d) (%d) tsig\n", x, s.u, s.v)
	}
}

/*
* Vertical position of accidentals. This seems to be set up with a difference
* of 3 between notes, with E corresponding to a height of 0.
*/
int   sh_pos[8]={0,24,15,27,18, 9,21,15};
/*                  f  c  g  d  A  e  B */
int   ft_pos[8]={0,12,21, 9,18, 6,15, 3};
/*                  B  e  A  d  G  c  F */

/* ----- draw_ksig: return sf for this key ----- */
int draw_ksig(
	int   sn,
	float x,
	Sym   s)
{	char *F = "draw_ksig";
	float p;
	int   a, cl, i,n1,n2,n3,t,yad,sf, v;
	int   accs = -1;
	Ksig *ks = 0;
	V6 "%s: Sym %d has q=%d t=%d u=%d v=%d w=%d acc1=%d acc2=%d clef=%d yadd=%d.\n",
		F,sn,s.q,s.t,s.u,s.v,s.w,s.acc1,s.acc2,s.clef,s.yadd V;
	n1 = s.u >= 0 ? s.u : s.clef;		/* which symbol to start with */
	n2 = s.v >= 0 ? s.v : s.acc1;		/* up to which symbol to go */
	n3 = s.w >= 0 ? s.w : s.acc2;		/* draw neutrals instead starting from this one */
	t  = s.t;		/* type of symbol: sharp, flat or mixed */
	V6 "%s: n1=%d n2=%d n3=%d t=%d.\n",F,n1,n2,n3,t V;
	if (ks = s.ksig) {
		V6 "%s: Sym has ksig=%lX (%s).\n",F,ks,dmpKsig(ks) V;
		cl = ks->data.clef;
	} else {
		V3 "%s: Sym %d has no ksig.\n",F,sn V;
		cl = voice[ivc].key->data.clef;
	}
	V6 "%s: Clef %d=%s.\n",F,cl,SymClef(cl) V;
	yad = 0;		/* Y-position adjustment */
	switch (cl) {
	case BASS: case BASSd8: case BASSu8: yad = -6; break;
	case ALTO: case ALTOd8: case ALTOu8: yad = -3; break;
	}
	accs = voice[ivc].key->data.accs;
	V6 "%s: yad=%d accs=%d.\n",F,yad,accs V;
	if (!ks) {
		if (ks = voice[ivc].key) {
			V6 "%s: voice %d has %s\n",F,ivc,dmpKsig(ks) V;
		}
	}
	if (ks) {
		V6 "%s: Ksig has %d accidentals.\n",F,ks->data.accs V;
		for (a=0; a<ks->data.accs; a++) {
			V6 "%s: Accidental %d: atyp=%d aval='%c' apos=%d.\n",
				F,a,ks->data.atyp[a],ks->data.aval[a],ks->data.apos[a] V;
		}
	}
	if (n2>7) {
		fprintf(stderr,"+++ Keysig seems to have %d symbols ???\n", n2);
		return 0;
	}
	sf=0;
	if (!oldks || ks->data.accs > 0) {
		accs = ks->data.accs;
		V3 "%s: New keysig code with accs=%d.\n",F,accs V;
		p = x;
		for (a=0; a<accs; a++) {
			V5 "%s: Accidental %d atyp=%d apos=%d + %d.\n",F,a,ks->data.atyp[a],ks->data.apos[a],yad V;
			if (a>0) {
				v = ks->data.apos[a] - ks->data.apos[a-1];	// Vertical separation between accidentals
				V3 "%s: Acc %d p=%.1f apos=%d v=%d <--\n",F,a,p,ks->data.apos[a],v V;
				v = ABS(v);
				V3 "%s: Acc %d p=%.1f apos=%d v=%d <--\n",F,a,p,ks->data.apos[a],v V;
				if (v<12) {
					// Extra space if different accidentals are nearly the same pitch
					p += 2;
					V3 "%s: Acc %d p=%.1f apos=%d v=%d <<<\n",F,a,p,ks->data.apos[a],v V;
				}
			}
			switch (ks->data.atyp[a]) {
			case A_SH:
				V5 "%s: Accidental %d SH at %d.\n",F,a,ks->data.apos[a]+yad V;
				PUT2("%.1f %d sh0 ",p,ks->data.apos[a]+yad);
				break;
			case A_NT:
				V5 "%s: Accidental %d NT at %d.\n",F,a,ks->data.apos[a]+yad V;
				PUT2("%.1f %d nt0 ",p,ks->data.apos[a]+yad);
				break;
			case A_FT:
				V5 "%s: Accidental %d FT at %d.\n",F,a,ks->data.apos[a]+yad V;
				PUT2("%.1f %d ft0 ",p,ks->data.apos[a]+yad);
				break;
			}
			sf++;
			p += 5;
		}
	} else {
		V3 "%s: Original diatonic keysig code with t=%d.\n",F,t V;
		if (t == A_SH) {
			p = x;
			V3 "%s: A_SH x=%3.1f\n",F,x V;
			for (i=n1;i<=n2;i++) {
				if (i>=n3)
					PUT2("%.1f %d nt0 ",p,sh_pos[i]+yad)
				else {
					sf++;
					PUT2("%.1f %d sh0 ",p,sh_pos[i]+yad)
				}
				p=p+5;
			}
			PUT0("\n")
		} elsif (t == A_FT) {
			V3 "%s: A_FT x=%3.1f\n",F,x V;
			p = x;
			for (i=n1;i<=n2;i++) {
				if (i>=n3)
					PUT2("%.1f %d nt0 ", p, ft_pos[i]+yad)
				else {
					sf--;
					PUT2("%.1f %d ft0 ", p, ft_pos[i]+yad)
				}
				p=p+5;
			}
			PUT0("\n")
		} else {
			V3 "%s: t=%d x=%3.1f\n",F,t,x V;
			bug ("wrong type in draw_ksig", 0);
		}
	}
	V6 "%s: Return sf=%d.\n",F,sf V;
	return sf;
}


/* ----- drawBar ------- */
void drawBar(
	float x,	// X-pos of bar line
	float w,	// Space to left of bar
	float d,	// Distance to previous sym
	Sym  *sp)	// Symbol
{	char *F="drawBar";
	int   n;

	V5 "%s: Called at x=%.2f sp->u=%d.\n",F,x,sp->u V;
	if (sp->gr.n > 0) {
		V5 "%s: BAR has %d grace notes.\n",F,sp->gr.n V;
		drawGraceNotes(x,w,d,sp);
	}
	if (sp->u == B_SNGL)                        /* draw the bar */
		PUT1("%.1f bar\n", x)
	elsif (sp->u == B_DBL)
		PUT1("%.1f dbar\n", x)
	elsif (sp->u == B_LREP) {
		V6 "%s: Left repeat.\n",F V;
		if (sp->lrep < 1) {			/* [jc] */
			V3 "%s: ### sp->lrep = %d.\n",F,sp->lrep V;
			sp->lrep = 1;
		}
		V3 "%s: Left repeat %d times.\n",F,sp->lrep V;
		PUT2("%.1f fbar1 %.1f rdots\n", x, x+10)
		x += 10;
		for (n=1; n<sp->lrep; n++) {
			x += 6;
			PUT1("%.1f rdots\n", x)
			V3 "%s: Repeat %d.\n",F,n V;
		}
	} elsif (sp->u == B_RREP) {
		V6 "%s: Right repeat.\n",F V;
		if (sp->rrep < 1) {			/* [jc] */
			V3 "%s: ### sp->rrep = %d.\n",F,sp->rrep V;
			sp->rrep = 1;
		}
		V3 "%s: Left repeat %d times.\n",F,sp->rrep V;
		PUT2("%.1f fbar2 %.1f rdots\n", x, x-10)
		x -= 10;
		for (n=1; n<sp->lrep; n++) {
			x -= 6;
			PUT1("%.1f rdots\n", x)
			V3 "%s: Repeat %d.\n",F,n V;
		}
	} elsif (sp->u == B_DREP) {
		V6 "%s: Double repeat.\n",F V;
		PUT2("%.1f fbar1 %.1f rdots\n", x-1, x+9)
		PUT2("%.1f fbar2 %.1f rdots\n", x+1, x-9)
	} elsif (sp->u == B_FAT1) {
		PUT1("%.1f fbar1\n", x)
	} elsif (sp->u == B_FAT2) {
		PUT1("%.1f fbar2\n", x)
	} elsif (sp->u == B_FAT3) {
		PUT1("%.1f fbar3\n", x)
	} elsif (sp->u == B_INVIS) {
		;
	} else {
		fprintf(stderr,">>> dont know how to draw bar type %d\n", sp->u);
	}
	PUT0("\n")
}


/* ----- drawBarnums ------- */
void drawBarnums (fp)
	FILE *fp;
{	char *F="drawBarnums";
	int   i,last,ok,got_note;

	last=0;
	got_note=0;
	for (i=0;i<nsym;i++) {
		if ((sym[i].type == NOTE)||(sym[i].type == REST)) got_note=1;

		if ((sym[i].type == BAR) && (strlen(sym[i].ach)>0)) {	/* Bar nums now kept in "achord" field */
			if (last != 2) set_font (fp, cfmt.barlabelfont, 0);
			PUT3 (" %.1f %.1f M (%s) cshow ", sym[i].x, BNUMHT, sym[i].ach)
			last=2;
		} elsif ((sym[i].type == BAR) && sym[i].ann) { 		/* Barnums may be done as annotation */
			if (last != 2) set_font (fp, cfmt.barlabelfont, 0);
			PUT3 (" %.1f %.1f M (%s) cshow ", sym[i].x, BNUMHT, sym[i].ann->t)
			last=2;
		}

		if ((sym[i].type == BAR) && sym[i].t) {
			ok=0;
			if ((cfmt.barnums>0) && (sym[i].t%cfmt.barnums == 0)) ok=1;
			if ((cfmt.barnums == 0) && (!got_note)) ok=1;
			if ((cfmt.barnums!=0) && ((strlen(sym[i].ach)>0))) ok=0;
			V5 "%s: ok=%d.\n",F,ok V;
			if (ok) {
				if (last != 1) set_font (fp, cfmt.barnumfont, 0);
/*|     if ((mvoice>1) && (cfmt.barnums == 0))  |*/
				if (cfmt.barnums == 0)
					PUT1 (" 0 38 M (%d) rshow ", sym[i].t)
				else
					PUT3 (" %.1f %.1f M (%d) cshow ", sym[i].x, BNUMHT, sym[i].t)
				last=1;
			}
		}
	}
	PUT0("\n");

}


/* ----- update_endings: remember where to draw endings ------- */
void update_endings (float x,Sym s)
{
	int i;

	if (num_ending>0) {
		i=num_ending-1;
		if (ending[i].num == 1) {
			mes1++;
		} else {
			mes2++;
			if (mes2 == mes1) ending[i].b=x;
		}
	}

	if (s.v) {
		if (num_ending>0)
			if (ending[num_ending-1].num == 1) ending[num_ending-1].b=x-3;
		ending[num_ending].a=x;
		ending[num_ending].b=-1;
		ending[num_ending].num=s.v;
		if (s.v == 1) mes1=0;
		else        mes2=0;
		num_ending++;
	}

}



/* ----- set_ending: determine limits of ending box ------- */
void set_ending(i)
	int i;
{	char *F = "set_ending";
	int num,j,j0,j1,mes,mesmax;
	char *vtxt;
	char *etxt;
	float top;

	num = sym[i].v;
	vtxt = sym[i].vtxt;
	V5 "%s: Called with i=%d num=%d vtxt=\"%s\"\n",F,i,num,vtxt V;
	mesmax=0;
	if (num == 2) mesmax=mes1;

	mes=0;
	j0=j1=-1;
	for (j=i+1;j<nsym;j++) {
		if (sym[j].type == BAR) {
			if (sym[j].u!=B_INVIS)  mes++;
			if (mes == 1) j1=j;
			if (sym[j].u == B_RREP || sym[j].u == B_DREP || sym[j].u == B_FAT3 || sym[j].u == B_FAT2 ||
					sym[j].u == B_LREP || sym[j].u == B_FAT1 || sym[j].v>0 || *sym[j].vtxt) {
				j0 = j;
				break;
			}
			if (mes == mesmax) {
				j0 = j;
				break;
			}
		}
	}
	top=-1;
	if (j0 == -1) j0=j1;
	if (j0>=0) top=sym[j0].x;

	if (*sym[i].vtxt) {
		ending[num_ending].num = num;
		strcpy(ending[num_ending].etxt,sym[i].vtxt);
		V5 "%s: num=%d v=%d vtxt=\"%s\" etxt=\"$x\" (from sym[%d].vtxt)\n"
			,F,num,sym[i].v,sym[i].vtxt,ending[num_ending].etxt,i V;
	} else {
		ending[num_ending].num = num;
		sprintf(ending[num_ending].etxt,"%d\0",sym[i].v);
		V5 "%s: num=%d v=%d vtxt=\"%s\" etxt=\"$x\" (from sym[%d].v)\n"
			,F,num,sym[i].v,sym[i].vtxt,ending[num_ending].etxt,i V;
	}
	ending[num_ending].a=sym[i].x;
	ending[num_ending].b=top;
	if (num == 1) ending[num_ending].b=top-3;
	ending[num_ending].type=E_CLOSED;
	if (sym[j0].type == BAR && sym[j0].u == B_SNGL) ending[num_ending].type=E_OPEN;
	num_ending++;

	if (num == 1) mes1=mes;
	if (num == 2) mes1=0;

}


/* ----- drawEnds ------- */
void drawEnds ()
{	char *F="drawEnds";
	int   b, i;

	for (i=0;i<num_ending;i++) {
		if ((b = ending[i].b) < 0) {
			if (*ending[i].etxt) {
				V6 "%s: b=%d<0 ending[%d].etxt=\"%s\"\n",F,b,i,ending[i].etxt V;
				PUT3("%.1f %.1f (%s) end2\n",
					ending[i].a, ending[i].a+50, ending[i].etxt)
			} else {
				V6 "%s: b=%d<0 ending[%d].num=%d\n",F,b,i,ending[i].num V;
				PUT3("%.1f %.1f (%d) end2\n",
					ending[i].a, ending[i].a+50, ending[i].num)
			}
		} else {
			if (ending[i].type == E_CLOSED) {
				if (*ending[i].etxt) {
					V6 "%s: b=%d ending[%d].etxt=\"%s\" type==E_CLOSED\n",F,b,i,ending[i].etxt V;
					PUT3("%.1f %.1f (%s) end1\n",
						ending[i].a, ending[i].b, ending[i].etxt)
				} else {
					V6 "%s: b=%d ending[%d].num=%d type==E_CLOSED\n",F,b,i,ending[i].num V;
					PUT3("%.1f %.1f (%d) end1\n",
						ending[i].a, ending[i].b, ending[i].num)
				}
			} else {
				if (*ending[i].etxt) {
					V6 "%s: b=%d ending[%d].etxt=\"%s\" type!=E_CLOSED\n",F,b,i,ending[i].etxt V;
					PUT3("%.1f %.1f (%s) end2\n",
						ending[i].a, ending[i].b, ending[i].etxt)
				} else {
					V6 "%s: b=%d ending[%d].num=%d type!=E_CLOSED\n",F,b,i,ending[i].num V;
					PUT3("%.1f %.1f (%d) end2\n",
						ending[i].a, ending[i].b, ending[i].num)
				}
			}
		}
	}
	num_ending=0;

}

#define DflChYYA  31	// 38	// Default int   Y position above for chord syms
#define DflAnYYA  31	// 38	// Default int   Y position above for annotations
#define DflChYYAf 31.01	// 38	// Default float Y position above for chord syms
#define DflAnYYAf 31.01	// 38	// Default float Y position above for annotations
/* ----- drawRest ----- */
void drawRest(
	float x,
	float w,
	float d,
	float yy,
	Sym   s,
	float*achy,
	float*anny)
{ 	char *F = "drawRest";
	int y,i;
	float dotx,doty;

	drawGraceNotes(x, w, d, &s);					/* draw grace notes */

	*achy = DflChYYA;
	*anny = DflAnYYA;
	if (s.invis) return;

	y = (int)s.y;
	PUT2("%.2f %.0f", x, yy)

	if (s.head == H_OVAL)     {PUT0(" r1");}
	elsif (s.head == H_EMPTY) {PUT0(" r2");}
	else {
		if (s.flags == 0)     {PUT0(" r4");}
		elsif (s.flags == 1)  {PUT0(" r8");}
		elsif (s.flags == 2)  {PUT0(" r16");}
		elsif (s.flags == 3)  {PUT0(" r32");}
		else                  {PUT0(" r64");}
	}

	if (y%6) { dotx=6.5; doty=0; }                   /* dots */
	else     { dotx=6.5; doty=3; }
	if (s.head == H_OVAL)  { dotx=8; doty=-3; }
	if (s.head == H_EMPTY) { dotx=8; doty=3;  }
	for (i=0;i<s.dots;i++) {
		PUT2(" %.1f %.1f dt", dotx, doty)
		dotx=dotx+3.5;
	}


	PUT0("\n")
}

/* ----- drawGraceNotes ----- */
void drawGraceNotes (float x, float w, float d, Sym *s)
{
	int i,n,y,acc,ii,m;
	float xg[20],yg[20],lg,px[20],py[20],xx,yy;
	float s1,sx,sy,sxx,sxy,a,b,delta,lmin;
	float x0,y0,x1,y1,x2,y2,x3,y3,bet1,bet2,dy1,dy2,dx,dd,fac,facx;

	n=s->gr.n;
	if (n == 0) return;

	facx=0.3;
	fac=d/w-1;
	if (fac<0) fac=0;
	fac=1+(fac*facx)/(fac+facx);

	dx=0;
	for (m=0;m<s->npitch;m++) {              /* room for accidentals */
		dd=-s->shhd[m];
		if (s->accs[m]) dd=-s->shhd[m]+s->shac[m];
		if ((s->accs[m] == A_FT)||(s->accs[m] == A_NT)) dd=dd-2;
		if (dx<dd) dx=dd;
	}

	xx=x-fac*(dx+GSPACE0);
	for (i=n-1;i>=0;i--) {                   /* set note positions */
		yg[i] = 3*(s->gr.p[i]-18) + s->yadd;
		if (i == n-1) {                             /* some subtle shifts.. */
			if(yg[i]>=s->ymx)  xx=xx+1;              /* gnote above a bit closer */
			if((yg[i]<s->ymn-7)&&(n == 1)) xx=xx-2;   /* below with flag further */
		}

		if (i<n-1) {
			if (yg[i]>yg[i+1]+8) xx=xx+fac*1.8;
		}

		xg[i]=xx;
		xx=xx-fac*GSPACE;
		if (s->gr.a[i]) xx=xx-3.5;
	}

	if (n>1) {
		s1=sx=sy=sxx=sxy=0;                    /* linear fit through stems */
		for (i=0;i<n;i++) {
			px[i]=xg[i]+GSTEM_XOFF;
			py[i]=yg[i]+GSTEM;
			s1 += 1; sx += px[i]; sy += py[i];
			sxx += px[i]*px[i]; sxy += px[i]*py[i];
		}
		delta=s1*sxx-sx*sx;                   /* beam fct: y=ax+b */
		a=(s1*sxy-sx*sy)/delta;
		if (a>BEAM_SLOPE) a=BEAM_SLOPE;
		if (a<-BEAM_SLOPE) a=-BEAM_SLOPE;
		b=(sy-a*sx)/s1;

		if (bagpipe) { a=0; b=35; }

		lmin=100;                           /* shift to get min stems */
		for (i=0;i<n;i++) {
			px[i]=xg[i]+GSTEM_XOFF;
			py[i]=a*px[i]+b;
			lg=py[i]-yg[i];
			if (lg<lmin) lmin=lg;
		}
		if (lmin<10) b=b+10-lmin;
	}

	for (i=0;i<n;i++) {                     /* draw grace notes */
		if (n>1) {
			px[i]=xg[i]+GSTEM_XOFF;
			py[i]=a*px[i]+b;
			lg=py[i]-yg[i];
			PUT3("%.1f %.1f %.1f gnt ", xg[i],yg[i],lg)
		}
		else {
			lg=GSTEM;
			PUT3("%.1f %.1f %.1f gn1 ", xg[i],yg[i],lg)
		}

		acc=s->gr.a[i];
		if (acc == A_SH) PUT2("%.1f %.1f gsh0 ",xg[i]-4.5,yg[i])
		if (acc == A_FT) PUT2("%.1f %.1f gft0 ",xg[i]-4.5,yg[i])
		if (acc == A_NT) PUT2("%.1f %.1f gnt0 ",xg[i]-4.5,yg[i])
		if (acc == A_DS) PUT2("%.1f %.1f gds0 ",xg[i]-4.5,yg[i])
		if (acc == A_DF) PUT2("%.1f %.1f gdf0 ",xg[i]-4.5,yg[i])

		y = (int)yg[i];                         /* helper lines */
		if (y<=-6) {
			if (y%6) PUT2("%.1f %d ghl ",xg[i], y+3)
			else     PUT2("%.1f %d ghl ",xg[i], y)
		}
		if (y>=30) {
			if (y%6) PUT2("%.1f %d ghl ",xg[i], y-3)
			else     PUT2("%.1f %d ghl ",xg[i], y)
		}
	}

	if (n>1)                                /* beam */
		if (bagpipe)
			PUT4("%.1f %.1f %.1f %.1f gbm3 ", px[0],py[0],px[n-1],py[n-1])
		else
			PUT4("%.1f %.1f %.1f %.1f gbm2 ", px[0],py[0],px[n-1],py[n-1])


	if (slurgraces) {			/* slur */
		V3 "Slur grace notes.\n" V;
		bet1=0.2;
		bet2=0.8;
		yy=1000;
		for (i=n-1;i>=0;i--) if (yg[i]<=yy) {yy=yg[i]; ii=i;}
		x0=xg[ii];
		y0=yg[ii]-5;
		if (ii>0) { x0=x0-4; y0=y0+1; }
		x3=x-1;
		y3=s->ymn-5;
		dy1=(x3-x0)*0.4;
		if (dy1>3) dy1=3;
		dy2=dy1;
		if (yg[ii]>s->ymn+7){
			x0=xg[ii]-1;
			y0=yg[ii]-4.5;
			y3=s->ymn+1.5;
			x3=x-dx-5.5;
			dy2=(y0-y3)*0.2;
			dy1=(y0-y3)*0.8;
			bet1=0.0;
		}
		if (y3>y0+4) {
			y3=y0+4;
			x0=xg[ii]+2;
			y0=yg[ii]-4;
		}
		x1=bet1*x3+(1-bet1)*x0;
		y1=bet1*y3+(1-bet1)*y0-dy1;
		x2=bet2*x3+(1-bet2)*x0;
		y2=bet2*y3+(1-bet2)*y0-dy2;
		PUT4(" %.1f %.1f %.1f %.1f", x1,y1,x2,y2);
		PUT4(" %.1f %.1f %.1f %.1f gsl\n", x3,y3,x0,y0);
	} else {
		V3 "Detach grace notes.\n" V;
	}
}

/* ----- drawBasicNote: draw m-th head with accidentals and dots -- */
void drawBasicNote(
	float x,
	float w,
	float d,
	Sym  *s,
	int   m)
{	char *F="drawBasicNote";
	int y,i,yy;
	float dotx,doty,xx,dx,avail,add,fac;

	y = 3 * (s->vpos[m] - 18); // + s->yadd;	/* height on staff */
	V5 "%s: y=%.2f from pits[%d]=%d vpos[%d]=%d pitadj=%d yadd=%d.\n",
		F,y,m,s->pits[m],m,s->vpos[m],s->pitadj,s->yadd V;

	xx = x + s->shhd[m];					/* draw head */
	PUT2("%.1f %d", xx, y)
	if (s->head == H_OVAL)  PUT0(" HD")
	if (s->head == H_EMPTY) PUT0(" Hd")
	if (s->head == H_FULL)  PUT0(" hd")
	if (s->shhd[m]) {
		yy=0;
		if (y>=30) { yy=y; if (yy%6) yy=yy-3; }
		if (y<=-6) { yy=y; if (yy%6) yy=yy+3; }
		if (yy) PUT1(" %d hl", yy)
	}

	if (s->dots) {                                /* add dots */
		if (y%6) { dotx=8; doty=0; }
		else     { dotx=8; doty=3; }
		if (s->stem == -1)
			dotx=dotx+s->xmx-s->shhd[m];
		else
			dotx=dotx+s->xmx-s->shhd[m];
		if (s->dots && s->flags && (s->stem == 1) && !(y%6))
			if ((s->word_st == 1) && (s->word_end == 1) && (s->npitch == 1))
				dotx=dotx+DOTSHIFT;
		if (s->head == H_EMPTY) dotx=dotx+1;
		if (s->head == H_OVAL)  dotx=dotx+2;
		for (i=0;i<s->dots;i++) {
			PUT2(" %.1f %.1f dt", dotx, doty)
			dotx=dotx+3.5;
		}
	}

	if (s->accs[m]) {                          /* add accidentals */
		fac=1.0;
		avail=d-w-3;
		add=0.3*avail;
		fac=1+add/s->wl;
		if (fac<1) fac=1;
		if (fac>1.2) fac=1.2;
		dx=fac*s->shac[m];
		if (s->accs[m] == A_SH) PUT1(" %.1f sh", dx)
		if (s->accs[m] == A_NT) PUT1(" %.1f nt", dx)
		if (s->accs[m] == A_FT) PUT1(" %.1f ft", dx)
		if (s->accs[m] == A_DS) PUT1(" %.1f dsh", dx)
		if (s->accs[m] == A_DF) PUT1(" %.1f dft", dx)
	}
}


/* ----- drawDecos ----- */
float drawDecos (float x, Sym *s, float *tp)
{	char *F = "drawDecos";
	int y,sig,k,deco,m;
	float yc,xc,y1,top,top1,dx,dy;

	V5 "%s: Sym has %d decorations.\n",F,s->dc.n V;
	top = -1000;
	for (k=s->dc.n-1; k>=0; k--) {                 /*  decos close to head */
		deco=s->dc.t[k];
		if (deco == D_STACC) {                           /* dot */
			sig=1; if (s->stem == 1) sig=-1;
			y = s->y + 6*sig;
			if (y<top+3) y=top+3;
			if (!(y%6) && (y>=0) && (y<=24)) y+=3*sig;
			if (top<y) top=y;
			if (deco == D_STACC) PUT1(" %d stc",y)
			else                 PUT1(" %d emb",y)
		} 
		if (deco == D_SLIDE) {                           /* slide */
			yc=s->ymn;
			xc=5;
			for (m=0;m<s->npitch;m++) {
				dx=5-s->shhd[m];
				if (s->head == H_OVAL) dx=dx+2.5;
				if (s->accs[m]) dx=4-s->shhd[m]+s->shac[m];
				dy = 3 * (s->vpos[m]-18) + s->yadd - yc;
				if ((dy<10) && (dx>xc)) xc=dx;
			}
			yc=s->ymn;
			PUT2(" %.1f %.1f sld", yc, xc)
		}
	} 
	top1=top;
	for (k=s->dc.n-1;k>=0;k--) {                 /*  decos further away */
		deco = s->dc.t[k];
		V5 "%s: Deco %d is type %d.\n",F,k,deco V;
		if (deco == D_EMBAR) {                         /* bar */
			yc=s->ymx+6;
			if (s->stem == 1) yc=s->ys+4;
			if (yc<28) yc=28;
			if (yc<top+3) yc=top+3;
			if (top<yc+2) top=yc+2;
			PUT1(" %.2f emb", yc)
		}
		if ((deco == D_GRACE)||(deco == D_HAT)||(deco == D_ATT)) { /* gracing,hat,att */
			yc=s->ymx+9;
			if (s->stem == 1) yc=s->ys+5;
			if (yc<30) yc=30;
			if (yc<top+4) yc=top+4;
			if (top<yc+2) top=yc+2;
			if    (deco == D_GRACE) PUT1(" %.2f grm", yc)
			elsif (deco == D_HAT)   PUT1(" %.2f hat", yc)
			else                    PUT1(" %.2f att", yc)
		}
		if (deco == D_ROLL) {                            /* roll sign */
			y = s->y;
			if (s->stem == 1) {
				yc=s->y-5;
				if (yc>-2) yc=-2;
				PUT1(" %.2f cpd", yc)
			} else {
				yc=s->y+5;
				if (s->dots && (!(y%6))) yc=s->y+6;
				if (yc<26) yc=26;
				if (yc<top+1) yc=top+1;
				if (top<yc+8) top=yc+8;
				PUT1(" %.2f cpu", yc)
			}
		} 
		if (deco == D_HOLD) {                           /* hold sign */
			yc=27;
			if (s->stem == 1) {
				y1=s->ys+4;
			} else {
				y1=s->ymx+6;
			}
			if (yc<y1) yc=y1;
			if (yc<top+4) yc=top+4;
			if (top<yc+12) top=yc+12;
			PUT1(" %.1f hld", yc)
		} 
		if (deco == D_TRILL) {                          /* trill sign */
			yc=30;
			if (s->stem == 1) {
				y1=s->ys+5;
			} else {
				y1=s->ymx+7;
			}
			if (yc<y1) yc=y1;
			if (yc<top+1) yc=top+1;
			if (top<yc+8) top=yc+8;
			PUT1(" %.1f trl", yc)
		} 
		if ((deco == D_UPBOW)||(deco == D_DOWNBOW)) {     /* bowing signs */
			yc=21;
			if (s->stem == 1)
				y1=s->ys+4;
			else
				y1=s->ymx+8;
			if (yc<y1) yc=y1;
			if (yc<top+4) yc=top+4;
			if (top<yc+10) top=yc+10;
			if (deco == D_UPBOW)   PUT1(" %.1f upb", yc)
			if (deco == D_DOWNBOW) PUT1(" %.1f dnb", yc)
		}
	}
	*tp=top;
	return top1;
}


/* ----- drawNote ----- */
float drawNote(
	float x,	// X-pos of note head
	float w,	// Space to left of sym
	float d,	// Distance to previous sym
	Sym  *s,	// Symbol
	int   fl,	// 0: in beam 1: not in beam
	float*achy,	// Y-pos of chord
	float*anny)	// Y-pos of annotation
{	char *F = "drawNote";
	char c,cc;
	int y,i,m,k;
	float yc,slen,slen0,top,top2,xx;
	slen0=STEM;

	drawGraceNotes(x, w, d, s);					/* draw grace notes */

	c  = 'd';
	cc = 'u';
	if (s->stem == 1) {
		c = 'u';
		cc = 'd';
	}
	slen = s->stem * (s->ys - s->y);

	for (m = 0; m < s->npitch; m++) {
		if (m>0) PUT0(" ")
		drawBasicNote(x,w,d,s,m);					/* draw note heads */
		V6 "%s: Note m=%d has pits[%d]=%d vpos[%d]=%d pitadj=%d stem=%d yadd=%d y=%6.3f\n",
			F,m,m,s->pits[m],m,s->vpos[m],s->pitadj,s->stem,s->yadd,s->y V;
		xx = 3 * (s->vpos[m] - 18) - s->y;	// + s->yadd;
		V6 "xx=%.4f from vpos=%d y=%.3f\n",xx,s->vpos[m],s->y V;
		xx = xx * xx;
		V6 "xx=%.4f\n",xx V;
		if (xx<0.01) {								/* add stem */
			V5 "%s: stem=%d slen=%.1f c='%c'\n",F,s->stem,slen,c V;
			if (s->stem) PUT2(" %.1f s%c",slen,c)
			if (fl && (s->flags>0))					/* add flags */
				PUT3(" %.1f f%d%c",slen,s->flags,c)
		} else {
			V5 "%s: No stem because xx=%.6f\n",F,xx V;
		}
		if ((m>0) && (s->vpos[m] == s->vpos[m-1])) {	/* unions */
			if (s->stem) PUT2(" %.2f s%c",slen0,cc)
			if (s->flags>0)
				PUT3(" %.1f f%d%c",slen0,s->flags,cc)
		}
	}

	top = drawDecos (x,s,&top2);                /* add decorations */

	y = s->ymn;                                      /* lower helper lines */
	if (y<=-6) {
		for (i = -6; i >= y; i -= 6) {
			PUT1(" %d hl", i)
		}
		if (s->head == H_OVAL) PUT0("1")
	}
	y = s->ymx;                                      /* upper helper lines */
	if (y>=30) {
		for (i = 30; i <= y; i += 6) {
			PUT1(" %d hl", i)
		}
		if (s->head == H_OVAL) PUT0("1")
	}
	switch (s->achp) {
	default:
	case 0:
	case P_ABOVE:
		*achy = s->achy ? s->achy : DflChYYAf;
		V5 "%s: achy=%6.3f [ABOVE]\n",F,*achy V;
		break;
	case P_BELOW:
		*achy = s->achy ? s->achy : -12.01;
		V5 "%s: achy=%6.3f [BELOW]\n",F,*achy V;
		break;
	}
	V5 "%s: achy=%6.3f\n",F,*achy V;
	if (strlen(s->ach)>0) {                         /* position accomp chord */
		yc = *achy;
		if (yc<y+8) yc=y+8;
		if (yc<s->ys+4) yc=s->ys+4;
		for (k=0;k<s->dc.n;k++) {
			if ((s->dc.t[k] == D_GRACE) && (yc<y+12)) yc=y+12;
		}
		if (yc<top2) yc=top2;
		*achy = yc;
		V5 "%s: achy=%6.3f\n",F,*achy V;
	}
	if (s->ann) {				/* Is there an annotation? */
		switch (s->ann->p) {
		default:
		case 0:
		case P_ABOVE: *anny = s->ann->y ? s->ann->y :  DflChYYAf; break;
		case P_BELOW: *anny = s->ann->y ? s->ann->y : -12.01; break;
		}
		V5 "%s: anny=%6.3f\n",F,*anny V;
		V5 "%s: Annotation !%s! p=%d y=%6.3f\n",F,s->ann->t,s->ann->p,s->ann->y V;
		yc = *anny;
		V5 "%s: yc set to anny=%6.3f\n",F,yc V;
		if (s->ann->p == P_ABOVE) {
			if (yc < y+8) {
				yc = y+8;
				V5 "%s: yc changed to y+8=%6.3f\n",F,yc V;
			}
			if (yc < s->ys+4) {
				yc = s->ys+4;
				V5 "%s: yc changed to y+8=%6.3f\n",F,yc V;
			}
			for (k=0;k<s->dc.n;k++) {
				if ((s->dc.t[k] == D_GRACE) && (yc<y+12)) {
					yc = y+12;
					V5 "%s: yc changed to y+12=%6.3f\n",F,yc V;
				}
			}
			if (yc < top2) {
				yc = top2; V5 "%s: yc changed to top2=%6.3f\n",F,yc V;
			}
		}
		*anny = yc;
		V5 "%s: anny=%6.3f\n",F,*anny V;
	}
	PUT0("\n")
	return top;
}

/* ----- vsh: up/down shift needed to get k*6  ----- */
float vsh (float x, int dir)
{
	int ix,iy,ir;
	float x1,xx;
	x1=x*dir;
	ix=x1+600.999;
	ir=ix%6;
	iy=ix-600;
	if (ir>0) iy=iy+6-ir;
	xx=iy*dir;
	return xx-x;
}


/* ----- rnd3: up/down shift needed to get k*3  ----- */
float rnd3(float x)
{
	int ix,iy,ir;
	float xx;

	ix=x+600.999-1.5;
	ir=ix%3;
	iy=ix-600;
	if (ir>0) iy=iy+3-ir;
	xx=iy;
	return xx-x;
}


/* ----- rnd6: up/down shift needed to get k*6  ----- */
float rnd6(float x)
{
	int ix,iy,ir;
	float xx;

	ix=x+600.999-3.0;
	ir=ix%6;
	iy=ix-600;
	if (ir>0) iy=iy+6-ir;
	xx=iy;
	return xx-x;
}


/* ----- b_pos ----- */
float b_pos (int stem, int flags, float b)
{
	float bb,d1,d2,add;
	float top,bot;

	if (stem == 1) {
		top = b;
		bot = b - (flags-1) * BEAM_SHIFT - BEAM_DEPTH;
		if (bot > 26) return b;
	} else {
		bot = b;
		top = b + (flags-1) * BEAM_SHIFT + BEAM_DEPTH;
		if (top < -2) return b;
	}

	d1=rnd6(top-BEAM_OFFSET);
	d2=rnd6(bot+BEAM_OFFSET);
	add=d1;
	if (d1*d1>d2*d2) add=d2;
	bb=b+add;

/*  fprintf(stderr,"stem %d top %.1f, bot%.1f, choices %.1f %.1f => %.1f\n",
					stem, top,bot, d1,d2, add); */
/*  fprintf(stderr,"b_pos(%d) b=%.1f to %.1f\n", stem,b,bb); */

	return bb;
}


/* ----- calculate_beam ----- */
int calculate_beam (int i0, struct BEAM *bm)
{	char *F="calculate_beam";
	int j,j1,j2,i,stem,notes,flags;
	float x,y,ys,a,b,max_stem_err,stem_err,min_stem,slen,yyg,yg,try;
	float s,sx,sy,sxx,sxy,syy,delta,hh,dev,dev2,a0;

	j1 = i0;                      /* find first and last note in beam */
	j2 = -1;
	stem = sym[j1].stem;
	for (j=i0; j<nsym; j++)
		if (sym[j].word_end) {
			j2 = j;
			break;
		}
	if (j2 == -1) {
		return 0;
	}

	notes=flags=0;                /* set x positions, count notes and flags */
	for (j=j1; j<=j2; j++) {
		if(sym[j].type == NOTE) {
			sym[j].xs = sym[j].x + stem * STEM_XOFF;
			sym[j].stem = stem;
			if (sym[j].flags > flags) flags = sym[j].flags;
			notes++;
		}
	}

	s=sx=sy=sxx=sxy=syy=0;              /* linear fit through stem ends */
	for (j=j1;j<=j2;j++) if (sym[j].type == NOTE) {
		x=sym[j].xs;
		y=sym[j].ymx+STEM*stem;
		s += 1; sx += x; sy += y;
		sxx += x*x; sxy += x*y; syy += y*y;
	}

	delta=s*sxx-sx*sx;                  /* beam fct: y=ax+b */
	a=(s*sxy-sx*sy)/delta;
	b=(sy-a*sx)/s;

	/* the next few lines modify the slope of the beam */
	if (notes>=3) {
		hh=syy-a*sxy-b*sy;                /* flatten if notes not in line */
		dev=0;
		if (hh>0) {
			dev2=hh/(notes-2);
			if (dev2>0.5) a=BEAM_FLATFAC*a;
		}
	}


	if (a>=0) a=BEAM_SLOPE*a/(BEAM_SLOPE+a);   /* max steepness for beam */
	else      a=BEAM_SLOPE*a/(BEAM_SLOPE-a);


	/* to decide if to draw flat etc. use normalized slope a0 */
	a0=a*(sym[j2].xs-sym[j1].xs)/(20*(notes-1));

	if ((a0<BEAM_THRESH) && (a0>-BEAM_THRESH)) a=0;  /* flat below threshhold */

	b=(sy-a*sx)/s;                        /* recalculate b for new slope */

/*  if (flags>1) b=b+2*stem;*/      /* leave a bit more room if several beams */

	if (bagpipe) { b=-11; a=0; }

	max_stem_err=0;                        /* check stem lengths */
	for (j=j1; j<=j2; j++) {
		if (sym[j].type == NOTE) {
			if (sym[j].npitch == 1) {
				min_stem = STEM_MIN;
				if (sym[j].flags == 2) min_stem = STEM_MIN2;
				if (sym[j].flags == 3) min_stem = STEM_MIN3;
				if (sym[j].flags == 4) min_stem = STEM_MIN4;
			} else {
				min_stem=STEM_CH_MIN;
				if (sym[j].flags == 2) min_stem = STEM_CH_MIN2;
				if (sym[j].flags == 3) min_stem = STEM_CH_MIN3;
				if (sym[j].flags == 4) min_stem = STEM_CH_MIN4;
			}
			min_stem = min_stem + BEAM_DEPTH + BEAM_SHIFT * (sym[j].flags - 1);
			ys = a * sym[j].xs + b;
			if (stem == 1) slen = ys - sym[j].ymx;
			else           slen = sym[j].ymn - ys;
			stem_err = min_stem - slen;
			if (stem_err > max_stem_err) max_stem_err = stem_err;
		}
	}

	if (max_stem_err > 0)                   /* shift beam if stems too short */
		b = b + stem * max_stem_err;
	V5 "%s: b=%d stem=%d flags=%d.\n",F,b,stem,flags V;

	for (j=j1+1;j<=j2;j++) if (sym[j].type == NOTE) {  /* room for gracenotes */
		for (i=0;i<sym[j].gr.n;i++) {
			yyg=a*(sym[j].x-GSPACE0)+b;
			yg = 3 * (sym[j].gr.p[i]-18) + sym[j].yadd;
			if (stem == 1) {
				try=(yg+GSTEM)-(yyg-BEAM_DEPTH-2);
				if (try>0) b=b+try;
			} else {
				try=(yg)-(yyg+BEAM_DEPTH+7);
				if (try<0) b=b+try;
			}
		}
	}

	if ((a<0.01) && (a>-0.01))       /* shift flat beams onto staff lines */
		b = b_pos(stem,flags,b);
	V5 "%s: b=%d stem=%d flags=%d.\n",F,b,stem,flags V;

	for (j=j1;j<=j2;j++) if (sym[j].type == NOTE) {    /* final stems */
		sym[j].ys=a*sym[j].xs+b;
	}

	bm->i1=j1;                      /* save beam parameters in struct */
	bm->i2=j2;
	bm->a=a;
	bm->b=b;
	bm->stem=stem;
	bm->t=stem*BEAM_DEPTH;
	return 1;
}


/* ----- rest_under_beam ----- */
float rest_under_beam (float x, int head, struct BEAM *bm)
{
	float y,tspace,bspace;
	int j1,j2,j,nf,iy;

	tspace=9;
	bspace=11;
	if ((head == H_OVAL)||(head == H_EMPTY)) tspace=bspace=4;

	j1=bm->i1;
	j2=bm->i2;
	nf=0;
	for (j=j1;j<=j2;j++)
		if ((sym[j].type == NOTE)||(sym[j].flags>nf)) nf=sym[j].flags;

	if (bm->stem == 1) {
		y=bm->a*x+bm->b;
		y=y-BEAM_DEPTH-(nf-1)*BEAM_SHIFT;
		y=y-tspace;
		if (y>12) y=12;
	} else {
		y=bm->a*x+bm->b;
		y=y+BEAM_DEPTH+(nf-1)*BEAM_SHIFT;
		y=y+bspace;
		if (y<12) y=12;
	}

	if ((head == H_OVAL)||(head == H_EMPTY)) {
		iy=(y+3.0)/6.0;
		y=6*iy;
	}

	return y;
}

/* ----- drawBeamN: draw number on a beam ----- */
void drawBeamN (struct BEAM *bm, int num, float xn)
{
	float yn;

	if (bm->stem == -1) {
		yn = bm->a * xn + bm->b - 12;
	} else {
		yn = bm->a * xn + bm->b + 4;
	}
	PUT3("%.1f %.1f (%d) bnum\n", xn, yn, num)

}


/* ----- drawBeam: draw a single beam ----- */
void drawBeam (float x1, float x2, float dy, struct BEAM *bm)
{
	float y1,y2;

	y1=bm->a*x1+bm->b-bm->stem*dy;
	y2=bm->a*x2+bm->b-bm->stem*dy;
	PUT5("%.1f %.1f %.1f %.1f %.1f bm\n", x1,y1,x2,y2,bm->t)
}

/* ----- drawBeams: draw the beams for one word ----- */
void drawBeams (bm)
struct BEAM *bm;
{
	int j,j1,j2,j3,inbeam,k1,k2,num,p,r;
	float x1,x2,xn;

	j1=bm->i1;
	j2=bm->i2;

	/* make first beam over whole word */
	x1=sym[j1].xs;
	x2=sym[j2].xs;
	num=sym[j1].u;

	for (j=j1;j<=j2;j++) {    /* numbers for nplets on same beam */
		if (sym[j].p_plet>0) {
			p=sym[j].p_plet;
			r=sym[j].r_plet;
			j3=j+r-1;
			if (j3<=j2) {
				xn=0.5*(sym[j].xs+sym[j3].xs);
				drawBeamN (bm,p,xn);
				sym[j].p_plet=0;
			}
		}
	}

	drawBeam (x1,x2,0.0,bm);

	/* second beams where two or more flags */
	k1=0;
	inbeam=0;
	for (j=j1;j<=j2;j++) {
		if (sym[j].type!=NOTE) continue;
		if ((!inbeam) && (sym[j].flags>=2)) {
			k1=j;
			inbeam=1;
		}
		if (inbeam && ((sym[j].flags<2) || (j == j2))) {
			if ((sym[j].flags>=2) && (j == j2)) k2=j;
			x1=sym[k1].xs;
			x2=sym[k2].xs;
			inbeam=0;
			if (k1 == k2) {
				if (k1 == j1) drawBeam (x1+BEAM_STUB,x1,BEAM_SHIFT,bm);
				else        drawBeam (x1-BEAM_STUB,x1,BEAM_SHIFT,bm);
			}
			else
				drawBeam (x1,x2,BEAM_SHIFT,bm);
			inbeam=0;
		}
		k2=j;
	}

	/* third beams where three or more flags */
	k1=0;
	inbeam=0;
	for (j=j1;j<=j2;j++) {
		if (sym[j].type!=NOTE) continue;
		if ((!inbeam) && (sym[j].flags>=3)) {
			k1=j;
			inbeam=1;
		}
		if (inbeam && ((sym[j].flags<3) || (j == j2))) {
			if ((sym[j].flags>=3) && (j == j2)) k2=j;
			x1=sym[k1].xs;
			x2=sym[k2].xs;
			inbeam=0;
			if (k1 == k2) {
				if (k1 == j1) drawBeam (x1+BEAM_STUB,x1,2*BEAM_SHIFT,bm);
				else        drawBeam (x1-BEAM_STUB,x1,2*BEAM_SHIFT,bm);
			}
			else
				drawBeam (x1,x2,2*BEAM_SHIFT,bm);
			inbeam=0;
		}
		k2=j;
	}

	/* fourth beams where four or more flags */
	k1=0;
	inbeam=0;
	for (j=j1;j<=j2;j++) {
		if (sym[j].type!=NOTE) continue;
		if ((!inbeam) && (sym[j].flags>=4)) {
			k1=j;
			inbeam=1;
		}
		if (inbeam && ((sym[j].flags<4) || (j == j2))) {
			if ((sym[j].flags>=4) && (j == j2)) k2=j;
			x1=sym[k1].xs;
			x2=sym[k2].xs;
			inbeam=0;
			if (k1 == k2) {
				if (k1 == j1) drawBeam (x1+BEAM_STUB,x1,3*BEAM_SHIFT,bm);
				else        drawBeam (x1-BEAM_STUB,x1,3*BEAM_SHIFT,bm);
			}
			else
				drawBeam (x1,x2,3*BEAM_SHIFT,bm);
			inbeam=0;
		}
		k2=j;
	}

}

/* ----- extreme: return min or max, depending on s ----- */
float extreme (float s, float a, float b)
{

	if (s>0) {
		if (a>b) return a;
		return b;
	}
	else {
		if (a<b) return a;
		return b;
	}
}

/* ----- draw_bracket  ----- */
void draw_bracket (int p, int j1, int j2)
{	char *F = "draw_bracket";
	float x1,x2,y1,y2,xm,ym,s,s0,xx,yy,yx,dy;
	int j;

	x1=sym[j1].x-4;
	x2=sym[j2].x+4;
	y1=sym[j1].ymx+10;
	y2=sym[j2].ymx+10;

	if (sym[j1].stem == 1) { y1=sym[j1].ys+4; x1=x1+3; }
	if (sym[j2].stem == 1) { y2=sym[j2].ys+4; x2=x2+3; }

	if (y1<30) y1=30;
	if (y2<30) y2=30;

	xm=0.5*(x1+x2);
	ym=0.5*(y1+y2);

	s=(y2-y1)/(x2-x1);
	s0=(sym[j2].ymx-sym[j1].ymx)/(x2-x1);
	if (s0>0) {
		if (s<0) s=0; if (s>s0) s=s0;
	}
	else {
		if (s>0) s=0; if (s<s0) s=s0;
	}
	if (s*s < 0.2*0.2) s=0;   /* flat if below limit */


	dy=0;           /* shift up bracket if needed */
	for (j=j1;j<=j2;j++) {
		if ((sym[j].type == NOTE) || (sym[j].type == REST)) {
			xx=sym[j].x;
			yy=ym+(xx-xm)*s;
			yx=sym[j].ymx+10;
			if (sym[j].stem == 1) yx=sym[j].ys+5;
			if (yx-yy>dy) dy=yx-yy;
		}
	}
	ym=ym+dy;
	y1=ym+s*(x1-xm);
	y2=ym+s*(x2-xm);

/*
* shift up accomp chords, if needed
*/
	for (j=j1;j<=j2;j++) {
		if ((sym[j].type == NOTE) || (sym[j].type == REST)) {
			xx = sym[j].x;
			yy = ym + (xx - xm) * s;
			if (sym[j].achy < yy+4) {
				sym[j].achy = yy+4;
				V5 "%s: sym[%d] achy=%6.3f ann->y=%6.3f [%s:%d]\n",F,j,sym[j].achy,sym[j].ann->y,FL V;
			}
			if (sym[j].ann) {
				if (sym[j].ann->y < yy+4) sym[j].ann->y = yy+4;
				V5 "%s: sym[%d] achy=%6.3f ann->y=%6.3f [%s:%d]\n",F,j,sym[j].achy,sym[j].ann->y,FL V;
			} else {
				V5 "%s: sym[%d] achy=%6.3f\n",F,j,sym[j].achy V;
			}
		}
	}
//
	xx=xm-6;
	yy=ym+s*(xx-xm);
	PUT4("%.1f %.1f %.1f %.1f hbr ",  x1,y1,xx,yy)
//
	xx=xm+6;
	yy=ym+s*(xx-xm);
	PUT4("%.1f %.1f %.1f %.1f hbr ",  x2,y2,xx,yy)
//
	yy=0.5*(y1+y2);
	PUT3("%.1f %.1f (%d) bnum\n", xm, yy-4, p)
}

/* ----- drawNPletBkts  ----- */
void drawNPletBkts ()
{
	int i,j,k,p,r,c;

	for (i=0;i<nsym;i++) {
		if ((sym[i].type == NOTE) || (sym[i].type == REST)) {
			if (sym[i].p_plet>0) {
				p=sym[i].p_plet;
				r=sym[i].r_plet;
				c=r;
				k=i;
				for (j=i;j<nsym;j++) {
					if ((sym[j].type == NOTE) || (sym[j].type == REST)) {
						c--;
						k=j;
						if (c == 0) break;
					}
				}
				draw_bracket(p,i,k);
			}
		}
	}
}


/* ----- slur_direction: decide whether slur goes up or down --- */
float slur_direction (k1,k2)
int k1,k2;
{
	float s;
	int i,are_stems,are_downstems,are_bars,y_max,notes;

	are_stems=are_downstems=are_bars=0;
	notes=0;
	y_max=300;
	for (i=k1;i<=k2;i++) {
		if (sym[i].type == BAR) are_bars=1;
		if (sym[i].type == NOTE) {
			notes++;
			if (sym[i].stem != 0 )  are_stems=1;
			if (sym[i].stem == -1 ) are_downstems=1;
			if (sym[i].ymn<y_max) y_max=sym[i].ymn;
		}
	}
	s=-1;
	if (are_downstems) s=1;
	if (!are_stems) {
		s=1;
		if (y_max<12) s=-1;
	}

	/* next line tries to put long phrasings on top */
	if (are_bars && (notes>4)) s=1;

	return s;
}

/* ----- outSlur: output slur -- --- */
void outSlur (float x1, float y1, float x2, float y2, float s, float height, float shift)
{
	float alfa,beta,mx,my,xx1,yy1,xx2,yy2,dx,dy,dz,a,add;

	alfa=0.3;
	beta=0.45;

	/* for wide flat slurs, make shape more square */
	dy=y2-y1;
	if (dy<0) dy=-dy;
	dx=x2-x1;
	if (dx<0) dx=-dx;
	a=dy/dx;
	if ((a<0.7) && dx>40) {
		add=0.2*(dx-40)/100;
		alfa=0.3+add;
		if (alfa>0.7) alfa=0.7;
	}


	/* alfa, beta, and height determine Bezier control points pp1,pp2
	*
	*           X ==  == alfa == =| == =alfa ==  == =X
	*          /            |             \
	*        pp1            |              pp2
	*        /            height            \
	*      beta             |                beta
	*      /                |                 \
	*    p1                 m                  p2
	*
	*/


	mx=0.5*(x1+x2);
	my=0.5*(y1+y2);

	xx1=mx+alfa*(x1-mx);
	yy1=my+alfa*(y1-my)+height;
	xx1=x1+beta*(xx1-x1);
	yy1=y1+beta*(yy1-y1);

	xx2=mx+alfa*(x2-mx);
	yy2=my+alfa*(y2-my)+height;
	xx2=x2+beta*(xx2-x2);
	yy2=y2+beta*(yy2-y2);

	dx=0.03*(x2-x1);
	if (dx>10.0) dx=10.0;
	dy=1.0;
	dz=0.20;
	if (x2-x1>100) dz=dz+0.001*(x2-x1);
	if (dz>0.6) dz=0.6;

	PUT4("%.1f %.1f %.1f %.1f ",
			xx2-dx, yy2+shift+s*dy, xx1+dx, yy1+shift+s*dy)
	PUT3("%.1f %.1f 0 %.1f ", x1,y1+shift+s*dz,s*dz)
	PUT4("%.1f %.1f %.1f %.1f ", xx1,yy1+shift,xx2,yy2+shift)
	PUT4("%.1f %.1f %.1f %.1f SL\n", x2,y2+shift, x1,y1+shift)


/*PUT4("%.2f %.2f %.2f %.2f ", xx1,yy1+shift,xx2,yy2+shift)
	PUT4("%.2f %.2f %.2f %.2f sl\n", x2,y2+shift, x1,y1+shift)*/

	return;
}

/* ----- drawSlur (not a pretty routine, this) ----- */
void drawSlur (int k1, int k2, int nn, int level)
{
	float x01,x02,y01,y02;
	float x1,y1,x2,y2,yy,height,addx,addy;
	float s,shift,hmin,a;
	float x,y,z,h,dx,dy;
	int i;

	s=slur_direction (k1,k2);

	/* fix endpoints */
	if (sym[k1].type == NOTE) {            /* here if k1 points to note */
		x01=sym[k1].x;
		yy=sym[k1].ymn; if (s>0) yy=sym[k1].ymx;
		y01=extreme(s,yy+s*6,sym[k1].ys+s*2);
		if (sym[k1].word_end) {
			yy=sym[k1].ymn; if (s>0) yy=sym[k1].ymx;
			y01=yy+s*6;
			if ((sym[k1].stem == 1)&&(s == 1)) x01=x01+4;
		}
		if ((s>0) && (y01<sym[k1].dc.top+2.5)) y01=sym[k1].dc.top+2.5;
	}

	if (sym[k2].type == NOTE) {            /* here if k2 points to note */
		x02=sym[k2].x;
		yy=sym[k2].ymn; if (s>0) yy=sym[k2].ymx;
		y02=extreme(s,yy+s*6,sym[k2].ys+s*2);
		if (sym[k2].word_st) {
			yy=sym[k2].ymn; if (s>0) yy=sym[k2].ymx;
			y02=yy+s*6;
			if ((sym[k2].stem == -1)&&(s == -1)) x02=x02-3;
		}
		if ((s>0) && (y02<sym[k2].dc.top+2.5)) y02=sym[k2].dc.top+2.5;
	}

	if (sym[k1].type!=NOTE) {
		x01=sym[k1].x+sym[k1].wr;
		y01=y02+1.2*s;
		if (nn>1) {
			if(s == 1) { if (y01<28) y01=28; }
			else     { if (y01>-4) y01=-4; }
		}
	}

	if (sym[k2].type!=NOTE) {
		x02=sym[k2].x;
		y02=y01+1.2*s;
		if (nn>1) {
			if (s == 1) {if (y02<28) y02=28; }
			else      {if (y02>-4) y02=-4; }
		}
	}

	/* shift endpoints */
	addx=0.04*(x02-x01);
	if (addx>3.0) addx=3.0;
	addy=0.02*(x02-x01);
	if (addy>3.0) addy=3.0;
	x1 = x01+addx;
	x2 = x02-addx;
	y1=y01+s*addy;
	y2=y02+s*addy;

	a=(y2-y1)/(x2-x1);                    /* slur steepness */
	if (a >  SLUR_SLOPE) a= SLUR_SLOPE;
	if (a < -SLUR_SLOPE) a=-SLUR_SLOPE;
	if (a>0) {
		if (s == 1) y1=y2-a*(x2-x1);
		if (s == -1) y2=y1+a*(x2-x1);
	}
	else {
		if (s == -1) y1=y2-a*(x2-x1);
		if (s == 1) y2=y1+a*(x2-x1);
	}

	/* for big vertical jump, shift endpoints */
	y=y2-y1; if (y>8) y=8; if (y<-8) y=-8;
	z=y; if(z<0) z=-z; dx=0.5*z; dy=0.3*z;
	if (y>0) {
		if (s == 1) { x2=x2-dx; y2=y2-dy; }
		if (s == -1) { x1=x1+dx; y1=y1+dy; }
	}
	else {
		if (s == 1) { x1=x1+dx; y1=y1-dy; }
		if (s == -1) { x2=x2-dx; y2=y2+dy; }
	}

	h=0;
	for (i=k1+1; i<k2; i++)
		if (sym[i].type == NOTE) {
			x = sym[i].x;
			yy = sym[i].ymn; if (s>0) yy=sym[i].ymx;
			y = extreme (s, yy+6*s, sym[i].ys+2*s);
			z = (y2*(x-x1)+y1*(x2-x))/(x2-x1);
			h = extreme (s, h, y-z);
		}

	y1=y1+0.4*h;
	y2=y2+0.4*h;
	h=0.6*h;

	hmin=s*(0.03*(x2-x1)+8);
	if (nn>3) hmin=s*(0.12*(x2-x1)+12);
	height = extreme (s, hmin, 3.0*h);
	height = extreme (-s, height, s*50);

	y=y2-y1; if (y<0) y=-y;
	if ((s == 1)  && (height< 0.8*y)) height=0.8*y;
	if ((s == -1) && (height>-0.8*y)) height=-0.8*y;

	shift=3*s*level;

	outSlur (x1,y1,x2,y2,s,height,shift);

	return;
}


/* ----- prev_scut, next_scut: find place to terminate/start slur --- */
int next_scut (int i)
{
	int j,cut,ok;

	cut=nsym-1;
	for (j=i+1;j<nsym;j++) {
		ok=0;
		if (sym[j].type == BAR) {
			if (sym[j].u == B_RREP) ok=1;
			if (sym[j].u == B_DREP) ok=1;
			if (sym[j].u == B_FAT1) ok=1;
			if (sym[j].u == B_FAT2) ok=1;
			if (sym[j].u == B_FAT3) ok=1;
			if (sym[j].v == 2)      ok=1;
		}
		if(ok) {
			cut=j;
			break;
		}
	}
	return cut;
}

int prev_scut(i)
int i;
{
	int j,cut,ok;

	cut=-1;
	for (j=i;j>=0;j--) {
		ok=0;
		if (sym[j].type == BAR) {
			if (sym[j].u == B_LREP) ok=1;
			if (sym[j].u == B_DREP) ok=1;
			if (sym[j].u == B_FAT1) ok=1;
			if (sym[j].u == B_FAT2) ok=1;
			if (sym[j].u == B_FAT3) ok=1;
			if (sym[j].v == 2)      ok=1;
		}
		if(ok) {
			cut=j;
			break;
		}
	}

	if (cut == -1) {    /* return sym before first note */
		cut=0;
		for (j=0;j<nsym;j++) {
			if((sym[j].type == REST) || (sym[j].type == NOTE)) {
				cut=j-1;
				break;
			}
		}
	}

	return cut;
}


/* ----- drawChSlurs ----- */
void drawChSlurs(int k1, int k2, int nh1, int nh2, int nslur, int mhead1[], int mhead2[], int job)
{	char *F="drawChSlurs";

	int i,pbot,ptop,m1,m2,p1,p2,y,cut;
	float s,x1,y1,x2,y2,height,shift,addx,addy;

	if (nslur == 0) return;

	pbot=1000;
	ptop=-1000;
	for (i=0; i<sym[k1].npitch; i++) {
		p1 = sym[k1].vpos[i];
		if (p1 < pbot) pbot = p1;
		if (p1 > ptop) ptop = p1;
	} 
	for (i=0;i<nslur;i++) {
		m1 = mhead1[i];
		m2 = mhead2[i];
		p1 = sym[k1].vpos[m1];
		p2 = sym[k2].vpos[m2];
		s  = slur_direction(k1,k2);
		if (p1 == pbot) s=-1;
		if (p1 == ptop) s=1;

		x1=sym[k1].x;
		x2=sym[k2].x;
		if (job == 2) {
			cut=next_scut(k1);
			x2=sym[cut].x;
			if (cut == k1) x2=x1+30;
		}

		if (job == 1) {
			cut=prev_scut(k1);
			x1=sym[cut].x;
			if (cut == k1) x2=x1-30;
		}

		addx=0.04*(x2-x1);
		if (addx>3.0) addx=3.0;
		addy=0.02*(x2-x1);
		if (addy>3.0) addy=3.0;

		x1=x1+3+addx;
		x2=x2-3-addx;
		if ((s == 1)  && (sym[k1].stem == 1))  x1=x1+1.5;
		if ((s == -1) && (sym[k2].stem == -1)) x2=x2-1.5;

		y=3*(p1-18)+sym[k1].yadd;
		y1=y2=y+s*(4+addy);
		y=3*(p2-18)+sym[k2].yadd;
		y2=y+s*(4+addy);

		if ((s == 1) && !(y%6) && (sym[k1].dots>0)) {
			y2=y1=y+s*(5.5+addy);
			x1=x1-2;
			x2=x2+2;
		}
		height=s*(0.04*(x2-x1)+5);
		shift=0;
		outSlur (x1,y1,x2,y2,s,height,shift);
	}

}



/* ----- drawSlurs: draw slurs/ties between neighboring notes/chords */
void drawSlurs (k1,k2,job)
int k1,k2,job;
{
	int i,m1,m2;
	int mhead1[MAXHD],mhead2[MAXHD],nslur,nh1,nh2;

	if (nbuf+100>BUFFSZ)
		rx ("PS output exceeds reserved space per staff",
				" -- increase BUFFSZ1");

	nslur=0;

	if (job == 2) {                    /* half slurs from last note in line */
		nh1=sym[k1].npitch;
		for (i=1;i<=nh1;i++) {
			for (m1=0;m1<nh1;m1++) {
				if (sym[k1].sl1[m1] == i) {
					nslur=nslur+1;
					mhead1[nslur-1]=m1;
				}
				if (sym[k1].ti1[m1]) {
					nslur=nslur+1;
					mhead1[nslur-1]=m1;
				}
			}
		}
		drawChSlurs(k1,k1,nh1,nh1,nslur,mhead1,mhead1,job);
		return;
	}

	if (job == 1) {                    /* half slurs to first note in line */
		nh1=sym[k1].npitch;
		for (i=1;i<=nh1;i++) {
			for (m1=0;m1<nh1;m1++) {
				if (sym[k1].sl2[m1] == i) {
					nslur=nslur+1;
					mhead1[nslur-1]=m1;
				}
				if (sym[k1].ti2[m1]) {
					nslur=nslur+1;
					mhead1[nslur-1]=m1;
				}
			}
		}
		drawChSlurs(k1,k1,nh1,nh1,nslur,mhead1,mhead1,job);
		return;
	}

	/* real 2-note case: set up list of slurs/ties to draw */
	if ((sym[k1].type == NOTE) && (sym[k2].type == NOTE)) {
		nh1=sym[k1].npitch;
		nh2=sym[k2].npitch;

		for (m1=0;m1<nh1;m1++) {
			if (sym[k1].ti1[m1]) {
				for (m2=0;m2<nh2;m2++) {
					if (sym[k2].vpos[m2] == sym[k1].vpos[m1]) {
						nslur++;
						mhead1[nslur-1]=m1;
						mhead2[nslur-1]=m2;
						break;
					}
				}
			}
		}

		for (i=1;i<=nh1;i++) {
			for (m1=0;m1<nh1;m1++) {
				if (sym[k1].sl1[m1] == i) {
					nslur++;
					mhead1[nslur-1]=m1;
					mhead2[nslur-1]=-1;
					for (m2=0;m2<nh2;m2++) {
						if (sym[k2].sl2[m2] == i) mhead2[nslur-1]=m2;
					}
					if (mhead2[nslur-1] == -1) nslur--;
				}
			}
		}
	}

	drawChSlurs(k1,k2,nh1,nh2,nslur,mhead1,mhead2,job);
}



/* ----- drawPhrasing: draw phrasing slur between two symbols --- */
void drawPhrasing (k1,k2,level)
int k1,k2,level;
{
	int nn,i;

	if (k1 == k2) return;
	if (nbuf+100>BUFFSZ)
		rx ("PS output exceeds reserved space per staff",
				" -- increase BUFFSZ1");
	nn=0;
	for (i=k1;i<=k2;i++)
		if ((sym[i].type == NOTE)||(sym[i].type == REST)) nn++;

	drawSlur (k1,k2,nn,level);

}

/* ----- drawAllSlurs: draw all slurs/ties between neighboring notes  */
void drawAllSlurs ()
{
	int i,i1,i2;

	i1=-1;
	for (i=0;i<nsym;i++) {
		if (sym[i].type == NOTE) {
			i1=i;
			break;
		}
	}
	if (i1<0) return;
	drawSlurs(i1,i1,1);

	for (;;) {
		i2=-1;
		for (i=i1+1;i<nsym;i++) {
			if (sym[i].type == NOTE) {
				i2=i;
				break;
			}
		}
		if (i2<0) break;
		drawSlurs(i1,i2,0);
		i1=i2;
	}

	drawSlurs(i1,i1,2);

}

/* ----- drawAllPhrasings: draw all phrasing slurs for one staff ----- */
void drawAllPhrasings ()
{
	int i,j,k,cut,pass,num;

	for (pass=0;;pass++) {
		num=0;
		for (i=0;i<nsym;i++) {

			if (sym[i].slur_st) {
				k=-1;                       /* find matching slur end */
				for (j=i+1;j<nsym;j++) {
					if (sym[j].slur_st && (!sym[j].slur_end)) break;
					if (sym[j].slur_end) {
						k=j;
						break;
					}
				}
				if (k>=0) {
					cut=next_scut(i);
					if (cut<k) {
						drawPhrasing (i,cut,pass);
						cut=prev_scut(k);
						drawPhrasing (cut,k,pass);
					}
					else {
						drawPhrasing (i,k,pass);
					}
					num++;
					sym[i].slur_st--;
					sym[k].slur_end--;
				}
			}
		}
		if (num == 0) break;
	}

	/* do unbalanced slurs still left over */

	for (i=0;i<nsym;i++) {
		if (sym[i].slur_end) {
			cut=prev_scut(i);
			drawPhrasing (cut,i,0);
		}
		if (sym[i].slur_st) {
			cut=next_scut(i);
			drawPhrasing (i,cut,0);
		}
	}

}

/* ----- check_bars1 ---------- */
void check_bars1 (ip1,ip2)
	int   ip1,ip2;
{	char *F = "check_bars1";
	int   v,i,j,k1,k2;
	float dx;

	/* check for inelegant bar combinations within one line */
	i=j=ip1;
	for (;;) {
		if (xp[i].type == BAR && xp[j].type == BAR && i!=j) {
			dx=0;
			for (v=0;v<nvoice;v++) {
				k1=xp[j].p[v];
				k2=xp[i].p[v];
				if (k1>=0 && k2>=0) {
					if (symv[v][k1].u == B_RREP && symv[v][k2].u == B_LREP) {
						V2 "%s: RREP=%d + LREP=%d found.\n",F,symv[v][k1].rrep,symv[v][k1].lrep V;
						if ((symv[v][k1].rrep+symv[v][k1].lrep) < 1 && symv[v][k2].u < 2) {
							V6 "%s: RREP+LREP reduced to DREP.\n",F V;
							symv[v][k2].u = B_DREP;
							symv[v][k1].u = B_INVIS;
						}
						dx = -7.0;
					}
				}
			}
			xp[i].x += dx;
		}
		j=i;
		i=xp[i].next;
		if (i == ip2) break;
	}

}


/* ----- check_bars2 ---------- */
void check_bars2 (ip1,ip2)
	int   ip1,ip2;
{	char *F = "check_bars2";
	int   i,ip,v;

	/* check whether to split up last bar over two lines */
	ip=xp[ip2].prec;
	for (v=0;v<nvoice;v++) {
		strcpy(voice[v].insert_text,"");
		voice[v].insert_bnum  = 0;
		voice[v].insert_space = 0;
		voice[v].insert_num   = 0;
		voice[v].insert_btype = 0;

		i=xp[ip].p[v];
		if (i>=0) {
			if (symv[v][i].type == BAR) {
				if (symv[v][i].u == B_LREP) {
					symv[v][i].u=B_SNGL;
					voice[v].insert_btype=B_LREP;
					voice[v].insert_num=0;
				}
				elsif (symv[v][i].u == B_DREP) {
					symv[v][i].u=B_RREP;
					voice[v].insert_btype=B_LREP;
					voice[v].insert_num=0;
				}
				elsif ((symv[v][i].u == B_RREP) && (symv[v][i].v!=0)) {
					voice[v].insert_btype=B_INVIS;
					voice[v].insert_num=symv[v][i].v;
					symv[v][i].v=0;
				}
				elsif ((symv[v][i].u == B_SNGL) && (symv[v][i].v!=0)) {
					voice[v].insert_btype=B_INVIS;
					voice[v].insert_num=symv[v][i].v;
					symv[v][i].v=0;
				}

				/* if number or label on last bar, move to next line */
				if (symv[v][i].t || (strlen(symv[v][i].ach)>0) || (symv[v][i].ann)) {
					if (symv[v][i+1].type == BAR) {
						if (symv[v][i+1].t == 0) {
							symv[v][i+1].t = symv[v][i].t;
						}
						if (strlen(symv[v][i+1].ach) == 0) {
							strcpy(symv[v][i+1].ach,symv[v][i].ach);
						}
						if (symv[v][i].ann && symv[v][i].ann->t && symv[v][i].ann->t[0]) {
							V1 "%s: Copy annotation from symv[%d][%d] to symv[%d][%d].\n",F,v,i,v,i+1 V;
							if (!symv[v][i+1].ann) {
								V6 "%s: Init annotation for voice %c sym %d.\n",F,v,i+1 V;
								TextInit(&symv[v][i+1].ann,64);
							}
							TextAppend(symv[v][i+1].ann,symv[v][i].ann->t);
						}
					} else {
						if (!voice[v].insert_btype) voice[v].insert_btype=B_INVIS;
						voice[v].insert_space=symv[v][i].wr;
						voice[v].insert_bnum=symv[v][i].t;
						strcpy(voice[v].insert_text,symv[v][i].ach);
						strcpy(symv[v][i].ach,"");
						if (symv[v][i].ann) {
							strcpy(voice[v].insert_text,symv[v][i].ann->t);
							V6 "%s: Init annotation for voice %d sym %d.\n",F,v,i V;
							TextInit(&symv[v][i].ann,64);
							symv[v][i].t = 0;
						}
					}
				}
			}
		}
	}
}



/* ----- drawVocals ----- */
void drawVocals (FILE *fp, int nwl, float botnote, float bspace, float *botpos)
{
	int i,hyflag,l,j;
	float x,x0,yword,lastx,spc,vfsize,w,swfac,lskip;
	char word[81],t[81];

	if (nwl<=0) return;
	vfsize=cfmt.vocalfont.size;
	lskip=1.1*vfsize;
	set_font (fp, cfmt.vocalfont, 0);
	yword=-cfmt.vocalspace;
	swfac=1.05;
	if (strstr(cfmt.vocalfont.name,"Helvetica")) swfac=1.10;
	if (botnote-cfmt.vocalfont.size<yword)
		yword=botnote-cfmt.vocalfont.size;

	for (j=0;j<nwl;j++) {
		hyflag=0;
		lastx=-10;
		for (i=0;i<nsym;i++) {
			if (sym[i].wordp[j]) {
				strcpy(word,sym[i].wordp[j]);
				x0=sym[i].x;
				l=strlen(word);

				if (hyflag) {
					tex_str (word,t,&w);
					spc=x0-VOCPRE*vfsize*swfac*w-lastx;
					x = lastx+0.5*spc-0.5*swfac*vfsize*cwid('-');
					PUT2("%.1f %.1f whf ",x,yword)
					hyflag=0;
				}

				if ((l>1) && (word[l-1] == '^')) {
					word[l-1]='\0';
					hyflag=1;
				}

				if ((l == 1) && (word[0] == '_')) {
					if (lastx<0) lastx=sym[i-1].x+sym[i-1].wr;
					PUT3("%.1f %.1f %.1f wln ", lastx+3, sym[i].x+1, yword)
				}
				elsif ((l == 1) && (word[0] == '^')) {
					PUT2("%.1f %.1f whf ", x0, yword)
					lastx=x0+vfsize*swfac*w;
				}
				else {
					tex_str (word,t,&w);
					if (isdigit(word[0]))
						x0=x0-3*vfsize*swfac*cwid('1');
					else
						x0=x0-VOCPRE*vfsize*swfac*w;
					if (strcmp(t," ")) PUT3("(%s) %.1f %.1f wd ", t, x0, yword)
					lastx=x0+vfsize*swfac*w;
				}
			}
		}
		if (hyflag) PUT2("%.1f %.1f whf ",lastx+5,yword)
		yword=yword-lskip;
	}
	*botpos=yword + lskip - bspace;
}

/* ----- drawSyms: draw symbols at proper positions on staff ----- */
void drawSyms (
	FILE *fp,
	float bspace,
	float *bpos,
	int   is_top)
{	char *F = "drawSyms";
	int   i, inbeam, j, nwl;
	float x, y, top, xl, d, w, achy, anny, botnote, botpos, spc, swfac;
	struct BEAM bm;
//	Ksig *ks=0;
	char  t[81];
	V3 "%s: Called with vb=%d.\n",F,vb V;
	inbeam = do_words = 0;
	botnote = 0;
	nwl = 0;
	for (i=0; i<nsym; i++) {                      /* draw the symbols */
		V6 "%s: --------------------------------------------------------------------- %d\n",F,i V;
		V6 "%s: sym[%d] q=%d t=%d u=%d v=%d w=%d acc1=%d acc2=%d.\n",
			F,i,sym[i].q,sym[i].t,sym[i].u,sym[i].v,sym[i].w,sym[i].acc1,sym[i].acc2 V;
		for (j=0; j<NWLINE; j++) {
			if (sym[i].wordp[j]) {
				if (j+1 > nwl) nwl = j+1;
			}
		}
		if (nbuf+100>BUFFSZ) {
			rx ("PS output exceeds reserved space per staff"," -- increase BUFFSZ1");
		}
		x = sym[i].x;

		switch (sym[i].type) {

		case NOTE:
			V6 "%s: NOTE x=%.2f y=%.2f w=[%.2f,%.2f] p=[%.2f,%.2f] x=[%.2f,%.2f]\n",
				F,sym[i].x,sym[i].y,sym[i].wl,sym[i].wr,sym[i].pl,sym[i].pr,sym[i].xl,sym[i].xr V;
			xl=0; w=sym[i].wl;
			if (i>0) {
				xl = sym[i-1].x;
				w = w + sym[i-1].wr; 
			}
			d = x - xl;
			V6 "%s: x=%.2f xl=%.2f w=%.2f d=%.2f\n",F,x,xl,w,d V;

			if (sym[i].word_st && !sym[i].word_end) {
				if (calculate_beam (i,&bm)) inbeam=1;
			}
			if (inbeam) {
				top = drawNote(x,w,d,&sym[i],0,&achy,&anny);
				V5 "%s: drawNote returned top=%6.3f achy=%6.3f anny=%6.3f\n",F,top,achy,anny V;
				if (i == bm.i2) {
					inbeam=0;
					drawBeams (&bm);
				}
			} else {
				top = drawNote(x,w,d,&sym[i],1,&achy,&anny);
				V5 "%s: drawNote returned top=%6.3f achy=%6.3f anny=%6.3f\n",F,top,achy,anny V;
			}
			sym[i].achy = achy;
			V5 "%s: sym[%d] achy=%6.3f [%s:%d]\n",F,i,sym[i].achy,FL V;
			if (sym[i].ann) {
				sym[i].ann->y = anny;
				V5 "%s: sym[%d] ann->y=%6.3f [%s:%d]\n",F,i,sym[i].ann->y,FL V;
			}
			sym[i].dc.top = top;
			V5 "%s: NOTE achy=%6.3f anny=%6.3f top=%6.3f\n",F,achy,anny,top V;
			if (sym[i].ymn-5<botnote) botnote=sym[i].ymn-5;
			V6 "%s: NOTE x=%.2f y=%.2f w=[%.2f,%.2f] p=[%.2f,%.2f] x=[%.2f,%.2f]\n",
				F,sym[i].x,sym[i].y,sym[i].wl,sym[i].wr,sym[i].pl,sym[i].pr,sym[i].xl,sym[i].xr V;
			break;

		case REST:
			xl=0; w=sym[i].wl;
			if (i>0) {
				xl = sym[i-1].x;
				w = w + sym[i-1].wr; 
			}
			d = x - xl;
			// Copied from NOTE case by jc.
			y = sym[i].y;
			V6 "%s: REST at y=%.2f\n",F,y V;
			if (inbeam) y=rest_under_beam (sym[i].x,sym[i].head,&bm);
			drawRest(x,w,d,y,sym[i],&achy,&anny);
			sym[i].achy = achy;
			if (sym[i].ann) sym[i].ann->y = anny;
			V5 "%s: REST achy=%6.3f anny=%6.3f top=%6.3f\n",F,achy,anny,top V;
			break;

		case BAR:
			V6 "%s: BAR\n",F V;
			xl=0; w=sym[i].wl;
			if (i>0) {
				xl = sym[i-1].x;
				w = w + sym[i-1].wr; 
			}
			d = x - xl;
			V5 "%s: BAR x=%.2f xl=%.2f d=%.2f w=%.2f\n",F,x,xl,d,w V;
			if (sym[i].v) set_ending(i);
			drawBar(x,w,d,&sym[i]);
			break;

		case CLEF:
			V6 "%s: CLEF sym %d u=%d v=%d w=%d.\n",F,i,sym[i].u,sym[i].v,sym[i].w V;
			switch (sym[i].u) {
			case NOCLEF:		// No clef.
				V6 "%s: NOCLEF\n",F V;
				PUT1("%.1f nclef\n", x)
				break;
			case TREBLE:
				V6 "%s: TREBLE clef\n",F V;
				if (sym[i].v) PUT1("%.1f stclef\n", x)
				else          PUT1("%.1f tclef\n", x)
				break;
			case TENOR:
				V6 "%s: TENOR clef\n",F V;
			case TREBLEd8:
				V6 "%s: TREBLEd8 clef\n",F V;
				if (sym[i].v) PUT1("%.1f std8clef\n", x)
				else          PUT1("%.1f td8clef\n", x)
				break;
			case TREBLEu8:
				V6 "%s: TREBLEu8 clef\n",F V;
				if (sym[i].v) PUT1("%.1f stu8clef\n", x)
				else          PUT1("%.1f tu8clef\n", x)
				break;
			case BASS:
				V6 "%s: BASS clef\n",F V;
				if (sym[i].v) PUT1("%.1f sbclef\n", x)
				else          PUT1("%.1f bclef\n", x)
				break;
			case BASSd8:
				V6 "%s: BASSd8 clef\n",F V;
				if (sym[i].v) PUT1("%.1f sbd8clef\n", x)
				else          PUT1("%.1f bd8clef\n", x)
				break;
			case BASSu8:
				V6 "%s: BASSu8 clef\n",F V;
				if (sym[i].v) PUT1("%.1f sbu8clef\n", x)
				else          PUT1("%.1f bu8clef\n", x)
				break;
			case ALTO:
				V6 "%s: ALTO clef\n",F V;
				if (sym[i].v) PUT1("%.1f scclef\n", x)
				else          PUT1("%.1f cclef\n", x)
				break;
			case ALTOd8:
				V6 "%s: ALTOd8 clef\n",F V;
				if (sym[i].v) PUT1("%.1f scd8clef\n", x)
				else          PUT1("%.1f cd8clef\n", x)
				break;
			case ALTOu8:
				V6 "%s: ALTOu8 clef\n",F V;
				if (sym[i].v) PUT1("%.1f scu8clef\n", x)
				else          PUT1("%.1f cu8clef\n", x)
				break;
			default:
				V3 "%s: unknown clef type=%d u=%d v=%d.\n",F,sym[i].type,sym[i].u,sym[i].v V;
				if (sym[i].v) PUT1("%.1f stclef\n", x)
				else          PUT1("%.1f tclef\n", x)
			}
			voice[ivc].key->data.clef=sym[i].u;
			break;

		case TIMESIG:
			V6 "%s: TIMESIG sym[%d] u=%d v=%d w=%d x=%6.3f.\n",F,i,sym[i].u,sym[i].v,sym[i].w,x  V;
			draw_timesig(x,sym[i]);
			break;

		case KEYSIG:
			V6 "%s: KEYSIG voice[%d].key is %08lX %s\n",F,ivc,voice[ivc].key,dmpKsig(voice[ivc].key) V;
			V6 "%s: KEYSIG sym[%d].ksig is %08lX %s\n",F,i,sym[i].ksig,dmpKsig(sym[i].ksig) V;
			voice[ivc].key->data.sf = draw_ksig(i,x,sym[i]);
			V6 "%s: KEYSIG draw_ksig returned sf=%d.\n",F,voice[ivc].key->data.sf V;
			V6 "%s: KEYSIG voice[%d].key is %08lX %s\n",F,ivc,voice[ivc].key,dmpKsig(voice[ivc].key) V;
			V6 "%s: KEYSIG sym[%d].ksig is %08lX %s\n",F,i,sym[i].ksig,dmpKsig(sym[i].ksig) V;
			if (sym[i].ksig) asgnKsig(voice[ivc].key,sym[i].ksig);
			break;

		case INVISIBLE:
			V6 "%s: KEYSIG\n",F V;
			break;

		default:
			fprintf(stderr,">>> cannot draw symbol type %d\n", sym[i].type);
		}
	}

	drawNPletBkts ();

	if (ivc == ivc0) drawBarnums(fp);


	/* draw accomp chords */
	if (voice[ivc].do_ach) {
		set_font(fp,cfmt.achordfont,0);
		swfac=1.0;
		if (strstr(cfmt.achordfont.name,"Times-Roman"))    swfac=1.00;
		if (strstr(cfmt.achordfont.name,"Times-Courier"))  swfac=1.05;
		if (strstr(cfmt.achordfont.name,"Helvetica"))      swfac=1.10;
		if (strstr(cfmt.achordfont.name,"Helvetica-Bold")) swfac=1.15;

		for (i=0;i<nsym;i++) {
			if ((sym[i].type == NOTE)||(sym[i].type == REST)) {
				if (*sym[i].ach) {
					tex_str (sym[i].ach,t,&w);
					w = cfmt.achordfont.size*w;
					spc = w*GCHPRE;
					if (spc>4.0) spc = 4.0;
					V5 "%s: sym[%d] x=%6.3f achy=%6.3f spc=%6.3f ach !%s!\n",F,i,sym[i].x,sym[i].achy,spc,sym[i].ach V;
					PUT3("%.1f %.1f (%s) ach ", sym[i].x-spc, sym[i].achy, t)
				}
				V5 "%s: sym[%d].ann=%X\n",F,i,sym[i].ann V;
				if (sym[i].ann) {
					V5 "%s: sym[%d].ann=%X l=%d m=%d.\n",F,i,sym[i].ann,sym[i].ann->l,sym[i].ann->m V;
					if (*sym[i].ann->t) {
						V5 "%s: sym[%d].ann=!%s!\n",F,i,sym[i].ann->t V;
						tex_str (sym[i].ann->t,t,&w);
						w = cfmt.annotfont.size*w;
						spc = w*GCHPRE;
						if (spc > 4.0) spc = 4.0;
						V5 "%s: sym[%d] x=%6.3f anny=%6.3f spc=%6.3f ann !%s!\n",
							F,i,sym[i].x,sym[i].ann->y,spc,sym[i].ann->t V;
						PUT3("%.1f %.1f (%s) ann ", sym[i].x-spc, sym[i].ann->y, t)
					}
				}
			}
		}
	}


	drawAllSlurs ();
	drawAllPhrasings ();

/*|   if (is_top) drawEnds (); |*/
	if (ivc == ivc0) drawEnds ();
	num_ending=0;

	botpos=-bspace;
	if (botnote<botpos) botpos=botnote;

	if (nwl>0) drawVocals (fp,nwl,botnote,bspace,&botpos);

	*bpos=botpos;

}


/* ----- drawSysBars: draw bars extending over staves----- */
void drawSysBars (FILE *fp, int ip1, int ip2, float wid0, float h1, float dh)
{
	int i,v,ok,u,uu,p;
	float x;

	PUT2 ("gsave 0 %.2f T 1.0 %.4f scale ",h1,dh/24.0)
	i=ip1;
	for (;;) {
		if (xp[i].type == BAR) {
			p=xp[i].p[ivc0];
			u=symv[ivc0][p].u;
			if (u == B_LREP) u=B_FAT1;
			if (u == B_RREP) u=B_FAT2;
			ok=1;
			for (v=0;v<nvoice;v++) {
				if (v!=ivc0 && voice[v].draw) {
					p=xp[i].p[v];
					if (p<0) ok=0;
					else {
						uu=symv[v][p].u;
						if (uu == B_LREP) uu=B_FAT1;
						if (uu == B_RREP) uu=B_FAT2;
						if (uu!=u) ok=0;
					}
				}
			}

			if (ok) {
				x=xp[i].x+wid0;
				if (u == B_SNGL)
					PUT1("%.1f bar ", x)
				elsif (u == B_DBL)
					PUT1("%.1f dbar ", x)
				elsif (u == B_FAT1)
					PUT1("%.1f fbar1 ", x)
				elsif (u == B_FAT2)
					PUT1("%.1f fbar2 ", x)
				elsif (u == B_FAT3)
					PUT1("%.1f fbar3 ", x)
				elsif (u == B_DREP) {
					PUT1("%.1f fbar1 ", x-1)
					PUT1("%.1f fbar2 ", x+1)
				}
			}
		}

		i=xp[i].next;
		if (i == ip2) break;
	}

	PUT0 (" 0.0 bar grestore\n")

}


/* ----- count_symbols: count number of "real" symbols ---- */
int count_symbols ()
{
	int i,c;

	c=0;
	for (i=0;i<nsym;i++) {
		switch (sym[i].type) {
		case NOTE:
		case REST:
		case BAR:
			c++;
		}
	}
	return c;

}

/* ----- select_piece: choose limits for piece to put on one staff ---- */
int select_piece (int ip1)
{
	int i,num,ltype,count_this;

	/* find next symbol marked as eol */
	i=ip1;
	for (;;) {
		if (xp[i].eoln) break;
		if (xp[i].next == XP_END) break;
		i=xp[i].next;
	}
	i=xp[i].next;
	if (cfmt.barsperstaff == 0) return i;

	/* find first note or rest */
	i=ip1;
	for (;;) {
		if (xp[i].type == NOTE || xp[i].type == REST)
		if (xp[i].type == NOTE || xp[i].type == REST) break;
		i=xp[i].next;
		if (i == XP_END) return i;
	}
	i=xp[i].next;
	if (i == XP_END) return i;

	/* count bars until number is reached */
	num=0;
	ltype=0;
	for (;;) {
		count_this=0;
		if (xp[i].type == BAR) {
			count_this=1;
			if (ltype == BAR) count_this=0;
		}
		num=num+count_this;
		ltype=xp[i].type;
		i=xp[i].next;
		if (i == XP_END) return i;
		if (num == cfmt.barsperstaff) return i;
	}
	i=xp[i].next;

	fprintf(stderr,"Got here! i=%d\n", i);
	return i;

}

/* ----- is_topvc: check for top voice of set staved together --- */
int is_topvc (int jv)
{
	int iv,kv,ok;

	ok=0;
	for (iv=0;iv<jv;iv++) {
		if (voice[iv].staves >= jv-iv+1) {
			for (kv=iv;kv<jv;kv++)
				if (voice[kv].draw) ok=1;
		}
	}

	return 1-ok;
}


/* ----- vc_select: set flags for voice selection from -V option --- */
int vc_select ()
{
	char *s;
	int i,a,b,n;

	if (strlen(vcselstr) == 0) return nvoice;

	for (i=0;i<nvoice;i++) voice[i].select=0;
	s=vcselstr;
	for (;;) {
		if (*s == 0) break;
		if (!sscanf(s,"%d%n",&a,&n)) break;
		s+=n;
		b=a;
		if (*s == '-') {
			s++;
			if (*s == 0) break;
			if (!sscanf(s,"%d%n",&b,&n)) break;
			s+=n;
		}
		for (i=a-1;i<b;i++)
			if (i>=0 && i<nvoice) voice[i].select=1;


		if (*s == 0) {
			n=0;
			for (i=0;i<nvoice;i++) if (voice[i].select) n++;
			if (verbose>=4) {
				fprintf(stderr,"Selection <%s>   selected voices:",vcselstr);
				for (i=0;i<nvoice;i++) if (voice[i].select) fprintf(stderr," %d",i+1);
				fprintf(stderr,"\n");
			}
			return n;
		}
		if (*s == ',') s++;
		else break;
	}

	rx ("Bad voice selection: -V ", vcselstr);
	return 0;
}

/* ----- voice_label: label voice, or just return length if job == 0 -- */
float voice_label (FILE *fp, char *label, float h, float xc, float dx0, int job)
{
	float w,wid,dy,xcc;
	char lab[81],t[81];
	int n,i;
	char *p,*q,*l[10];

	if (strlen(label) == 0) return 0.0;

	strcpy(lab,label);
	n=0;
	p=lab;
	l[0]=p;
	while ((q=strstr(p,"\\\\"))) { *q='\0'; p=q+2; n++; l[n]=p; }
	n++;

	wid=0;
	for (i=0;i<n;i++) {
		tex_str(l[i],t,&w);
		w=cfmt.voicefont.size*w;
		if (w>wid) wid=w;
		if (job!=0) {
			xcc=xc;
			if (xcc+0.5*w>dx0) xcc=dx0-0.5*w;
			dy = 8.0 + 0.5*cfmt.voicefont.size*(n-1-2*i) + h;
			PUT3 (" %.2f %.2f M (%s) cshow\n",xcc,dy,t)
		}
	}
	return wid;
}



/* ----- mstave_deco: draw decorations over multiple staves ----- */
void mstave_deco (FILE *fp, int ip1, int ip2, float wid0, float hsys, float htab[])
{
	int   iv,jv,nv;
	float hbot,htop,ht,x0,y0,wid,wc,wcb,w,wmin;
	float wmin1=15.0, wmin2=10.0;  /* space to staff */

	wmin=wmin2;
	if (do_indent) wmin=wmin1;

	/* draw bar lines */
	if (mvoice>1) {
		for (iv=0;iv<nvoice;iv++) {
			hbot=10000; htop=-hbot; nv=1;
			for (jv=iv;jv<iv+voice[iv].staves;jv++) {
				if (voice[jv].draw) nv++;
				if (voice[jv].draw && (htab[jv]<hbot)) hbot=htab[jv];
				if (voice[jv].draw && (htab[jv]>htop)) htop=htab[jv];
			}
			if ((hbot<htop-0.001) && (nv>1))
				drawSysBars (fp,ip1,ip2,wid0,hsys-htop,htop-hbot+24.0);
		}
		PUT1 ("gsave 1.0 %.4f scale 0.0 bar grestore\n", (hsys+24.0)/24.0)
	}

	/* next part draws voice names (except on braces) */
	set_font (fp, cfmt.voicefont, 0);
	nv=0;
	for (iv=0;iv<nvoice;iv++) if (voice[iv].draw) nv++;

	/* get max label width, then center labels above each other */
	wid=w=0;
	for (iv=0;iv<nvoice;iv++) {
		if (voice[iv].draw) {
			if (do_indent)
				w=voice_label (fp, voice[iv].name, 0.0,0.0,0.0, 0);
			elsif (nv>1)
				w=voice_label (fp, voice[iv].sname, 0.0,0.0,0.0, 0);
			if (w>wid) wid=w;
		}
	}
	wc=wcb=0.5*wid+wmin;
	if (wcb<18.0) wcb=18.0;     /* label on brace needs a bit more room */

	for (iv=0;iv<nvoice;iv++) {
		if (voice[iv].draw && (voice[iv].brace == 0) ) {
			y0=hsys-htab[iv];
			if (do_indent)
				voice_label (fp, voice[iv].name,y0,-wc,-wmin,1);
			elsif (nv>1)
				voice_label (fp,voice[iv].sname,y0,-wc,-wmin,1);
		}
	}

	/* braces and brackets */
	for (iv=0;iv<nvoice;iv++) {
		hbot=10000; htop=-hbot; nv=0;
		for (jv=iv;jv<iv+voice[iv].brace;jv++) {
			if (voice[jv].draw) nv++;
			if (voice[jv].draw && (htab[jv]<hbot)) hbot=htab[jv];
			if (voice[jv].draw && (htab[jv]>htop)) htop=htab[jv];
		}
		if (hbot<htop+0.001) {
			ht=htop-hbot+24.0;
			y0=hsys-htop+0.5*ht;
			x0=-8;
			ht=ht-2.0;
			if (voice[iv].brace == 1) ht=ht+15.0;
			if (voice[iv].brace>2)  ht=ht-8.0;
			if ((nv>1)||(voice[iv].brace == 1))
				PUT3 (" %.4f %.1f %.1f brace\n", ht/120.0, x0, y0)
			if (do_indent)
				voice_label (fp, voice[iv].name, y0-12.0,-wcb,-wmin,1);
			elsif (nv>1)
				voice_label (fp, voice[iv].sname,y0-12.0,-wcb,-wmin,1);
		}

		hbot=10000; htop=-hbot; nv=0;
		for (jv=iv;jv<iv+voice[iv].bracket;jv++) {
			if (voice[jv].draw) nv++;
			if (voice[jv].draw && (htab[jv]<hbot)) hbot=htab[jv];
			if (voice[jv].draw && (htab[jv]>htop)) htop=htab[jv];
		}
		if ((hbot<htop+0.001) && ((nv>1)||(voice[iv].bracket == 1)))
			PUT2 ("\n %.1f -3 %.1f bracket\n", htop-hbot+24.0+6.0, hsys-htop-3.0)

	}

}



/* ----- outMusic: output for parsed symbol list ----- */
void outMusic (FILE *fp)
{	char *F = "outMusic";
	int ip1,ip2,mv,is_top,nsel,b,bnum;
	float realwidth,staffwidth,wid0,widv,lscale,lwidth,bpos;
	float spa1,spa2,hsys,htab[40],extra,indent,spax,x;

	V6 "%s: Called for nvoice %d.\n",F,nvoice V;
	V6 "%s: default_key is %s\n",F,dmpKsig(&default_key) V;
	/* save current meter and key, to continue after P: or T: field */
	for (ivc=0; ivc<nvoice; ivc++) {
		V6 "%s: Save voice %d meter and key (%s).\n",F,ivc,dmpKsig(voice[ivc].key) V;
		voice[ivc].meter1 = voice[ivc].meter;
//		voice[ivc].key1   = voice[ivc].key;
		voice[ivc].key1   = asgnKsig(voice[ivc].key1, voice[ivc].key);
	}
	if (!file_initialized && !epsf) {
		init_ps (fout,infostr,0,0.0,0.0,0.0,0.0);
		init_page (fout);
	}
	if (nvoice == 0) {init_parse_params(); return; }
	if (vb>1) print_vsyms();

	alfa_last = 0.1;
	beta_last = 0.0;
	lwidth = cfmt.staffwidth;
	lscale = cfmt.scale;
	check_margin(cfmt.leftmargin);

	/* dump buffer if not enough space for a staff line */
	check_buffer(fp, BUFFSZ1);

	V4 "%s: initialize meter and key for voices\n",F V;
	for (ivc=0; ivc<nvoice; ivc++) {
		voice[ivc].meter = voice[ivc].meter0;
/*		voice[ivc].key   = voice[ivc].key0; */
		V6 "%s: voice[%d].key0 is %s\n",F,ivc,dmpKsig(voice[ivc].key0) V;
		voice[ivc].key = asgnKsig(voice[ivc].key,voice[ivc].key0);
		if (!do_meter) voice[ivc].meter.insert=0;
	}

	V4 "%s: setup for horizontal positioning, decide which voices to draw\n",F V;
	nsel = vc_select();
	for (ivc=0; ivc<nvoice; ivc++)
		if (!voice[ivc].select) voice[ivc].nsym=0;

	mvoice = 0;
	ivc0 = -1;
	for (ivc=0; ivc<nvoice; ivc++) {
		voice[ivc].draw=0;
		if (voice[ivc].nsym>0) {
			mvoice++;
			if (ivc0<0) ivc0=ivc;
			voice[ivc].draw=1;
		}
	}
	if (mvoice == 0) { init_parse_params(); return; }

	for (ivc=0; ivc<nvoice; ivc++) {
		if (voice[ivc].draw) {
			setSymChrs(0,voice[ivc].nsym,symv[ivc]);
			set_beams(0,voice[ivc].nsym,symv[ivc]);
			setStems(0,voice[ivc].nsym,symv[ivc]);
			b=set_sym_times(0,voice[ivc].nsym,symv[ivc],voice[ivc].meter0);
			V6 "%s: symv[%d].key=%08lX %s\n",F,ivc,symv[ivc]->ksig,dmpKsig(symv[ivc]->ksig) V;
			setSymWidths (0,voice[ivc].nsym,symv[ivc],ivc);
			if (ivc == ivc0) bnum = b;
		}
	}
	barinit = bnum;

	if (mvoice == 1)
		set_style_pars (cfmt.strict1);
	else
		set_style_pars (cfmt.strict2);

	set_poslist ();
	set_xpwid ();
	set_spaces ();

	if (make_audio) output_audio ();

	/* loop over pieces to output */
	ip1 = xp[XP_START].next;
	for (;;) {
		mline++;
		ip1 = contract_ksigs(ip1);
		V6 "%s: contract_ksigs returned %d.\n",F,ip1 V;
		wid0 = 0;
		for (ivc=0; ivc<nvoice; ivc++) {
			nsym_st[ivc] = set_initsyms(ivc, &widv);
			if (widv>wid0) wid0=widv;
		}
		indent = (do_indent == 1) ? cfmt.indent : 0.0;

		ip2 = select_piece (ip1);
		ip2 = check_overflow (ip1,ip2,lwidth/lscale-wid0-indent);
		V5 "%s: output posits %d to %d\n",F,ip1,ip2-1 V;
		realwidth = set_glue (ip1,ip2,lwidth/lscale-wid0-indent);
		check_bars1 (ip1,ip2);
		check_bars2 (ip1,ip2);

		spa1 = spa2 = cfmt.staffsep;
		if (mvoice>1) {
			spa1 = cfmt.systemsep;
			spa2 = cfmt.sysstaffsep;
		}

		hsys = 0;
		mv = 0;
		for (ivc=0; ivc<nvoice; ivc++) {
			if (voice[ivc].draw) {
				mv++;
				nsym = copy_vsyms (ivc,ip1,ip2,wid0);
				PUT2("\n%% --- ln %d vc %d \n", mline,ivc+1)
				if (mv == 1) {
					x = lscale*(0.5*spa1+24.0);
					V5 "%s: Bskip(%.2f) spa1=%.2f [%s:%d]\n",F,x,spa1,FL V;
					Bskip(x);
				} else {
					x = lscale*(0.5*spa2+24.0);
					V5 "%s: Bskip(%.2f) spa2=%.2f [%s:%d]\n",F,x,spa2,FL V;
					Bskip(x);
				}
				PUT3("gsave %.3f %.3f scale %.2f setlinewidth\n",
						lscale, lscale, BASEWIDTH)
				if (do_indent) PUT1(" %.2f 0 T ",indent)
					staffwidth = realwidth+wid0;
				PUT1("%.2f staff\n", staffwidth)
				htab[ivc] = hsys;
				spax = spa2+voice[ivc].sep;
				if (voice[ivc].sep>1000.0) spax = voice[ivc].sep-2000.0;

				is_top = is_topvc(ivc);
				drawSyms (fp,0.5*spa2+spax-spa2,&bpos,is_top);

				if (mv == mvoice) mstave_deco (fp,ip1,ip2,wid0,hsys,htab);

				PUT0("grestore\n")
				x = -lscale * bpos;
				V5 "%s: Bskip(%.2f) lscale=%.2f bpos=%.2f [%s:%d]\n",F,x,lscale,bpos,FL V;
				Bskip(x);
				hsys=hsys+0.5*spa2+24.0-bpos;
			}
		}

		extra=-bpos-0.5*spa2;
		if (mvoice>1) {
			x = lscale*(spa1+bpos-0.5*spa1+extra);
			V5 "%s: Bskip(%.2f) spa1=%.2f bpos=%.2f extra=%.2f [%s:%d]\n",F,x,spa1,bpos,extra,FL V;
			Bskip(x);
		}
		buffer_eob (fp);

		do_meter=do_indent=0;
		ip1=ip2;
		if (ip1 == XP_END) break;
	}

	/* set things to continue parsing */
	for (ivc=0;ivc<nvoice;ivc++) {
		voice[ivc].nsym=0;
		voice[ivc].meter0 = voice[ivc].meter = voice[ivc].meter1;
//		voice[ivc].key0   = voice[ivc].key   = voice[ivc].key1;
		voice[ivc].key    = asgnKsig(voice[ivc].key, voice[ivc].key1);
		voice[ivc].key0   = asgnKsig(voice[ivc].key0,voice[ivc].key1);
	}
	init_parse_params ();

}

/* ----- doTextBlock ----- */
void doTextBlock(fpin,fp,job)
	FILE *fp,*fpin;
	int  job;
{	char *F = "doTextBlock";
	char w1[81],ln[BSIZE],ln1[BSIZE];
	float lwidth,baseskip,parskip;
	int i,ll,add_final_nl;

	baseskip = cfmt.textfont.size * cfmt.lineskipfac;
	parskip  = cfmt.textfont.size * cfmt.parskipfac;
	add_final_nl=0;
	if (job == OBEYLINES) add_final_nl=1;
	lwidth=cfmt.staffwidth;
	outMusic (fp);
	buffer_eob (fp);
	set_font (fp, cfmt.textfont, 0);
	ntxt=0;
	for (i=0;i<100;i++) {
		if (feof(fpin)) rx("EOF reached scanning text block","");
		strcpy (ln, "");
		getline(ln, BSIZE, fpin);
		ll=strlen(ln);
		linenum++;
		V6 "%s: %3d  %s \n",F, linenum, ln V;
		if ((ln[0] == '%') && (ln[1] == '%')) {
			strcpy(ln1,ln+2);
			strcpy(ln,ln1);
		}
		strcpy(w1,"");
		sscanf(ln,"%s",w1);
		if (!strcmp(w1,"endtext") || !strcmp(w1,"textend")) break;

		if (job!=SKIP) {
			if (isblankstr(ln)) {
				write_text_block (fp,job);
				ntxt=0;
			} else {
				add_to_text_block (ln,add_final_nl);
			}
		}
	}
	if (job!=SKIP) write_text_block (fp,job);
}


/* ----- doPSComment  ----- */
void doPSComment(fpin,fp,line)
	FILE *fp,*fpin;
	char line[];
{	char *F = "doPSComment";
	char w[81],fstr[81],unum1[41],unum2[41],unum3[41];
	float h1,h2,len,lwidth;
	int i,nch,job;

	lwidth=cfmt.staffwidth;
	line[0]=' ';
	line[1]=' ';
	for (i=0;i<strlen(line);i++) if (line[i] == '%') line[i]='\0';
	strcpy(w," ");
	sscanf(line,"%s%n", w, &nch);

	if (!strcmp(w,"begintext") || !strcmp(w,"textbegin")) {
		if (epsf && !within_block) return;
		strcpy(fstr,"");
		sscanf(line, "%*s %s", fstr);
		if (isblankstr(fstr)) strcpy(fstr,"obeylines");
		if    (!strcmp(fstr,"obeylines")) job=OBEYLINES;
		elsif (!strcmp(fstr,"align"))     job=ALIGN;
		elsif (!strcmp(fstr,"skip"))      job=SKIP;
		elsif (!strcmp(fstr,"ragged"))    job=RAGGED;
		else rx("bad argument for begintext: ",fstr);
		if (within_block && !do_this_tune) job=SKIP;
		doTextBlock(fpin,fp,job);
		return;
	}
	if (!strcmp(w,"text") || !strcmp(w,"center")) {
		if (epsf && !within_block) return;
		if (within_block && !do_this_tune) return;
		outMusic(fp);
		set_font(fp, cfmt.textfont, 0);
		ntxt=0;
		add_to_text_block(line+nch+1,1);
		if (!strcmp(w,"text")) {
			write_text_block(fp,OBEYLINES);
		} else {
			write_text_block(fp,OBEYCENTER);
		}
		buffer_eob(fp);
	} elsif (!strcmp(w,"sep")) {
		if (within_block && !do_this_tune) return;
		outMusic(fp);
		strcpy(unum1,"");
		strcpy(unum2,"");
		strcpy(unum3,"");
		sscanf(line,"%*s %s %s %s", unum1,unum2,unum3);
		V6 "%s: SEP unum1=\"%s\" unum2=\"%s\" unum3=\"%s\"\n",F,unum1,unum2,unum3 V;
		g_unum(unum1,unum1,&h1);
		g_unum(unum2,unum2,&h2);
		g_unum(unum3,unum3,&len);
		V5 "%s: SEP h1=%6.3f h2=%6.3f len=%6.3f (raw)\n",F,h1,h2,len V;
		if (h1*h1<0.00001) h1=0.5*CM;
		if (h2*h2<0.00001) h2=h1;
		if (len*len<0.0001) len=3.0*CM;
		V4 "%s: SEP h1=%6.3f h2=%6.3f len=%6.3f (adjusted)\n",F,h1,h2,len V;
		Bskip(h1);
		PUT2("%.1f %.1f sep0\n", lwidth/2-len/2, lwidth/2+len/2);
		Bskip(h2);
		buffer_eob(fp);
	} elsif (!strcmp(w,"vskip")) {
		if (within_block && !do_this_tune) return;
		outMusic(fp);
		strcpy(unum1,"");
		sscanf(line,"%*s %s", unum1);
		g_unum(unum1,unum1,&h1);
		if (h1*h1<0.00001) h1=0.5*CM;
		Bskip(h1);
		buffer_eob(fp);
	} elsif (!strcmp(w,"newpage")) {
		if (within_block && !do_this_tune) return;
		outMusic(fp);
		write_buffer(fp);
		use_buffer=0;
		write_pagebreak(fp);
	} else  {
		if (within_block) {
			interpret_format_line(line,&cfmt);
			ops_into_fmt(&cfmt);
		}
		else {
			interpret_format_line(line,&dfmt);
			ops_into_fmt(&dfmt);
			cfmt=dfmt;
		}
	}
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
void check_selected(fp,xref_str,npat,pat,sel_all,search_field)
	FILE *fp;
	int npat,sel_all,search_field;
	char xref_str[],pat[][STRL1];
{

	if (!do_this_tune) {
		if (is_selected(xref_str,npat,pat,sel_all,search_field)) {
			do_this_tune = 1;
			verbose=vb;
			clear_buffer();
/*    set to 0 to permit staff breaks in a tune */
			use_buffer=1;
			write_tunetop(fp);
		}
	}
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Stuff a value into a Str. If the length is < 0, we use strlen(val). First we
* malloc space for the string, if needed, and then we copy val to sp->v.  Note
* the use of the strings fields:
*	char *v;	-- Value
*	int   l;	-- Length in use
*	int   m;	-- Bytes malloc'd (zero if not malloc'd)
*	char *p;	-- Pointer into string
* The return value is sp.  A zero return value means that malloc() failed.
*/
Str *setStr(sp,val,len)
	Str *sp;
	char*val;
	int  len;
{	char*F="setStr";
	unless (len>=0) len = strlen(val);
	unless (sp->l > len || sp->m > len) {	// Gotta realloc the string
		if (sp->m && sp->v) free(sp->v);	// Free old value
		unless (sp->v = (char*)malloc(len+1)) {
			V1 "%s: ### OUT OF MEMORY (Can't get %d for Str) ###\n",F,len+1 V;
			return 0;
		}
		bzero(sp->v,len+1);
		sp->m = len+1;
		sp->l = 0;
		sp->p = 0;
		sp->q = 0;
	}
	bcopy(val,sp->v,len);
	sp->v[len] = 0;
	sp->l = len;
	return sp;
}

Vdef *vdefs = 0;	// List of voice definitions
Vdef *vdefz = 0;	// Last voice definition
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* The original code here accepted voice definitions only after the K header, *
* inside the tune.  The switch_voice does seem to need to use the key. So to *
* make voice definitions work inside the header, what we  do  is  accumulate *
* the definitions in a list and not parse them yet.  When we encounter the K *
* header, we will parse the voice definitions. We are essentially moving the *
* V lines to just after the K line.                                          *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
Vdef* voiceDef(lvoiceid,line)
	char *lvoiceid;
	Str  *line;
{	char *F="voiceDef";
	Vdef *vp=0;
	V3 "%s: Vdef %s \"%s\"\n",F,lvoiceid,line->v V;
	unless (vp = (Vdef*)malloc(sizeof(Vdef))) {
		V1 "%s: ### OUT OF MEMORY (Can't get %d for Vdef %s) ###\n",F,sizeof(Vdef),lvoiceid V;
		return 0;
	}
	bzero(vp,sizeof(Vdef));
	unless (setStr(&vp->vid,lvoiceid,-1)) {
		V1 "%s: ### OUT OF MEMORY (Can't get %d for Vdef %s vid) ###\n",F,line->l+1,lvoiceid V;
		free(vp);
		return 0;
	}
	unless (setStr(&vp->src,line->v,line->l)) {
		V1 "%s: ### OUT OF MEMORY (Can't get %d for Vdef %s string) ###\n",F,line->l+1,lvoiceid V;
		free(vp->vid.v);
		free(vp);
		return 0;
	}
	if (vdefz) {
		vdefz->next = vp;
		vdefz = vp;
	} else {
		vdefs = vdefz = vp;
	}
	if (vb > 4) {
		for (vp = vdefs; vp; vp = vp->next) {
			V5 "%s: Saved voice \"%s\"\n",F,vp->src.v V;
		}
	}
	return vp;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
void doLine(fp,type,xref_str,npat,pat,sel_all,search_field)
	FILE *fp;			// File needed if line continued
	int   type;			// Line type code
	char  xref_str[];
	int   npat;
	char  pat[][STRL1];
	int   sel_all;
	int   search_field;
{	char *F = "doLine";
	char  fnm[81],finf[MAXINF];
	Vdef *vp;
	FILE *feps;

	if ((vb>15) || ((verbose>10)&&within_block)) {
		V5 "%s: type %d\n",F,type V;
		print_linetype(type);
	}

	switch (type) {

	case XREF:                       /* start of new block */
		if (!epsf) write_buffer (fp);    /* flush stuff left from %% lines */
		if (within_block) fprintf(stderr,"\n+++ Last tune not closed properly\n");
		get_default_info ();
		within_block  = 1;
		within_tune   = 0;
		do_this_tune  = 0;
		numtitle=0;
		ntext=0;
		init_pdims();
		cfmt=dfmt;
		break;

	case TITLE:
		if (!within_block) break;
		if (within_tune) {             /* title within tune */
			if (do_this_tune ) {
				outMusic (fp);
				write_inside_title (fp);
				do_meter=do_indent=1;
				barinit=1;
			}
		} else {
			check_selected(fp,xref_str,npat,pat,sel_all,search_field);
		}
		break;

	case TEMPO:
		if (!within_block) break;
		if (within_tune) {             /* tempo within tune */
			if (do_this_tune ) {
				outMusic (fp);
				write_inside_tempo(fp);
			}
		} else {
			check_selected(fp,xref_str,npat,pat,sel_all,search_field);
		}
		break;

	case KEY:
		V4 "%s: KEY\n",F V;
		if (!within_block) {
			V4 "%s: Key not within block.\n",F V;
			break;
		}
		if (within_tune) {
			V4 "%s: Key within tune.\n",F V;
			if (do_this_tune) {
				V4 "%s: Doing this tune.\n",F V;
				handle_inside_field(type);
			}
		} else {
			V4 "%s: Key within headers; start of tune.\n",F V;
			check_selected(fp,xref_str,npat,pat,sel_all,search_field);
			if (do_this_tune) {
				V4 "%s: Doing this tune.\n",F V;
				tunenum++;
				V4 "%s: Start tune %d (%s) ========================\n",F,xrefnum,info.title V;
//				if (!default_key.knum) default_key.knum = ++ksignum;
				V4 "%s: Parse key into default_key ...\n",F V;
				parseKey(info.key,  &default_key, 1);
				V4 "%s: default_key is %s\n",F,dmpKsig(&default_key) V;
				halftones = get_halftones(default_key, transpose);
				set_transtab(halftones,&default_key);
				set_meter(info.meter,&default_meter);
				set_dlen (info.len,  &default_meter);
				check_margin(cfmt.leftmargin);
				write_heading(fp);
				nvoice = 0;
				init_parse_params();
				default_meter.insert=1;
				mline = 0;
				do_indent = do_meter=1;
				barinit = 1;
				writenum = 0;
				for (vp = vdefs; vp; vp = vp->next) {
					V5 "%s: voice definition \"%s\" \"%s\"\n",F,vp->vid.v,vp->src.v V;
					vp->vndx = switch_voice(vp->vid.v);
					vp->vptr = &voice[vp->vndx];
				}
				V4 "%s: Initialized this tune.\n",F V;
			} else {
				V4 "%s: Not doing this tune.\n",F V;
			}
		}
		V4 "%s: We are now within a tune.\n",F V;
		within_tune = 1;
		break;

	case MACRO:
		V3 "%s: MACRO\n",F V;
		if (parse_macro()) {
			V3 "Got macro definition.\n" V;
			break;
		}
		V2 "Macros not implemented yet.\n" V;
		break;

	case METER:
		if (!within_block) break;
		if (do_this_tune && within_tune) handle_inside_field(type);
		break;

	case DLEN:
		if (!within_block) break;
		if (do_this_tune && within_tune) handle_inside_field(type);
		break;

	case PARTS:
		if (!within_block) break;
		if (do_this_tune && within_tune) {
			outMusic (fp);
			write_parts(fp);
		}
		break;

	case VOICE:
		V4 "%s: VOICE \"%s\"\n",F,line.v V;
		if (do_this_tune && within_block) {
			if (!within_tune) {
				V2 "%s: Voice field in header: V:%s\n",F,lvoiceid V;
				voiceDef(lvoiceid,&line);
			} else {
				V4 "%s: switch_voice(%d)\n",F,lvoiceid V;
				ivc = switch_voice(lvoiceid);
			}
		}
		break;

	case BLANK:                      /* end of block or file */
	case E_O_F:
		if (do_this_tune) {
			outMusic (fp);
			put_words (fp);
			if (cfmt.writehistory) put_history (fp);
			if (epsf) {
				close_output_file ();
				if (choose_outname) {
					epsf_title (info.title, fnm);
					strcat (fnm,".eps");
				} else {
					nepsf++;
					sprintf (fnm, "%s%03d.eps", outf, nepsf);
				}
				sprintf (finf, "%s (%d)", in_file[0], xrefnum);
				if ((feps = fopen (fnm,"w")) == NULL)
						rx ("Cannot open output file ", fnm);
				init_ps (feps, finf, 1,
						cfmt.leftmargin-5, posy+bposy-5,
						cfmt.leftmargin+cfmt.staffwidth+5,
						cfmt.pageheight-cfmt.topmargin);
				init_epsf (feps);
				write_buffer (feps);
				fprintf(stderr,"\n[%s] %s", fnm, info.title);
				close_epsf (feps);
				fclose (feps);
				in_page=0;
				init_pdims ();
			} else {
				buffer_eob (fp);
				write_buffer (fp);
				if ((verbose == 0) && (tunenum%10 == 0)) fprintf(stderr,".");
				if (verbose == 2) fprintf(stderr,"%s - ", info.title);
			}
			verbose=0;
		}
		info=default_info;
		if (within_block && !within_tune)
			fprintf(stderr,"\n+++ Header not closed in tune %d\n", xrefnum);
		within_tune=within_block=do_this_tune=0;
		break;

	}
}

dummy() {return;}

Str  line = {0};

/* ----- doFile ----- */
void doFile(fpin,fpout,xref_str,npat,pat,sel_all,search_field)
	FILE *fpin,*fpout;
	int npat,sel_all,search_field;
	char xref_str[],pat[][STRL1];
{	char *F = "doFile";
	int type;

	unless (line.m > 0) {
		unless (line.v = (char*)malloc(BSIZE)) {
			bzero(line.v,BSIZE);
			V1 "%s: ### OUT OF MEMORY (Can't get %d for line) ###\n",F,BSIZE V;
			return;
		}
		line.m = BSIZE;
	}
	within_tune = within_block = do_this_tune = 0;
	linenum = 0;
	numtitle = 0;
	reset_info (&default_info);
	info = default_info;
	verbose = 0;
	if (vb >= 20) db = 3;
	if (vb >= 25) db = 5;

	type = read_line(fpin,0,&line);
	V4 "%s: type=%d from read_line()\n",F,type V;
	for (;;) {
		if (type == PSCOMMENT) {
			doPSComment(fpin,fpout,line.v);
		} else {
			doLine(fpout,type,xref_str,npat,pat,sel_all,search_field);
		}
		if (type == E_O_F) {
			V2 "%s: Got E_O_F=%d from read_line\n",F,E_O_F V;
			break;
		}
		type = read_line(fpin,do_this_tune,&line);
		V4 "%s: type=%d from read_line()\n",F,type V;
	}
	if (!epsf) {
		buffer_eob (fpout);
		write_buffer (fpout);
	}

}
