/*
 * Drawing functions.
 *
 * This file is part of abcm2ps.
 *
 * Copyright (C) 1998-2020 Jean-François Moine (http://moinejf.free.fr)
 * Adapted from abc2ps, Copyright (C) 1996-1998 Michael Methfessel
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 */

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

#include "abcm2ps.h"

struct BEAM {			/* packages info on one beam */
	struct SYMBOL *s1, *s2;
	float a, b;
	short nflags;
};

static char *acc_tb[] = { "", "sh", "nt", "ft", "dsh", "dft" };

/* scaling stuff */
static int scale_voice;		/* staff (0) or voice(1) scaling */
static float cur_scale = 1;	/* voice or staff scale */
static float cur_trans = 0;	/* != 0 when scaled staff */
static float cur_staff = 1;	/* current scaled staff */
static int cur_color = 0;	/* current color */

static void draw_note(float x,
		      struct SYMBOL *s,
		      int fl);
static void set_tie_room(void);

// set the symbol color


/* set the voice color */
void set_color(int new_color)
{
	if (new_color == cur_color)
		return;
	cur_color = new_color;
	a2b("%.2f %.2f %.2f setrgbcolor ",
		(float) (cur_color >> 16) / 255,
		(float) ((cur_color >> 8) & 0xff) / 255,
		(float) (cur_color & 0xff) / 255);
}

/* output debug annotations */
static void anno_out(struct SYMBOL *s, char type)
{
	if (s->linenum == 0)
		return;
	if (mbf[-1] != '\n')
		*mbf++ = '\n';
	a2b("%%A %c %d %d ", type, s->linenum, s->colnum);
	putxy(s->x - s->wl - 2, staff_tb[s->staff].y + s->ymn - 2);
	if (type != 'b' && type != 'e')		/* if not beam */
		a2b("%.1f %d", s->wl + s->wr + 4, s->ymx - s->ymn + 4);
	a2b("\n");
}

/* -- up/down shift needed to get k*6 -- */
static float rnd6(float y)
{
	int iy;

	iy = ((int) (y + 2.999) + 12) / 6 * 6 - 12;
	return iy - y;
}

/* -- compute the best vertical offset for the beams -- */
static float b_pos(int grace,
		   int stem,
		   int flags,
		   float b)
{
	float d1, d2, shift, depth;
	float top, bot;

	shift = !grace ? BEAM_SHIFT : 3.5;
	depth = !grace ? BEAM_DEPTH : 1.8;
	if (stem > 0) {
		bot = b - (flags - 1) * shift - depth;
		if (bot > 26)
			return 0;
		top = b;
	} else {
		top = b + (flags - 1) * shift + depth;
		if (top < -2)
			return 0;
		bot = b;
	}

	d1 = rnd6(top - BEAM_OFFSET);
	d2 = rnd6(bot + BEAM_OFFSET);
	if (d1 * d1 > d2 * d2)
		return d2;
	return d1;
}

/* duplicate a note for beaming continuation */
static struct SYMBOL *sym_dup(struct SYMBOL *s_orig)
{
	struct SYMBOL *s;
	int m;

	s = (struct SYMBOL *) getarena(sizeof *s);
	memcpy(s, s_orig, sizeof *s);
	s->flags |= ABC_F_INVIS;
	s->text = NULL;
	for (m = 0; m <= s->nhd; m++)
		s->u.note.notes[m].sl1 = 0;
	memset(&s->u.note.dc, 0, sizeof s->u.note.dc);

	s->gch = NULL;
	s->ly = NULL;
	s->extra = NULL;
	return s;
}

/* -- calculate a beam -- */
/* (the staves may be defined or not) */
static int calculate_beam(struct BEAM *bm,
			  struct SYMBOL *s1)
{
	struct SYMBOL *s, *s2;
	int notes, nflags, staff, voice, two_staves, two_dir, visible = 0;
	float x, y, ys, a, b, max_stem_err;
	float sx, sy, sxx, sxy, syy, a0, stem_xoff, scale;
	static float min_tb[2][6] = {
		{STEM_MIN, STEM_MIN,
			STEM_MIN2, STEM_MIN3, STEM_MIN4, STEM_MIN4},
		{STEM_CH_MIN, STEM_CH_MIN,
			STEM_CH_MIN2, STEM_CH_MIN3, STEM_CH_MIN4, STEM_CH_MIN4}
	};

	if (!(s1->sflags & S_BEAM_ST)) {	/* beam from previous music line */
		s = sym_dup(s1);
		s->prev = s1->prev;
		if (s->prev)
			s->prev->next = s;
		else
			voice_tb[s->voice].sym = s;
		s1->prev = s;
		s->next = s1;
		s->ts_prev = s1->ts_prev;
//		if (s->ts_prev)
			s->ts_prev->ts_next = s;
		s1->ts_prev = s;
		s->ts_next = s1;
		for (s2 = s->ts_prev; /*s2*/; s2 = s2->ts_prev) {
			switch (s2->type) {
			default:
				continue;
			case CLEF:
			case KEYSIG:
			case TIMESIG:
				break;
			}
			break;
		}
		s->x -= 12;
		if (s->x > s2->x + 12)
			s->x = s2->x + 12;
		s->sflags &= S_SEQST;
		s->sflags |= S_BEAM_ST | S_TEMP;
		s->u.note.slur_st = 0;
		s->u.note.slur_end = 0;
		s1 = s;
	}

	/* search last note in beam */
	notes = nflags = 0;	/* set x positions, count notes and flags */
	two_staves = two_dir = 0;
	staff = s1->staff;
	voice = s1->voice;
	stem_xoff = (s1->flags & ABC_F_GRACE) ? GSTEM_XOFF : s1->u.note.sdx;
	for (s2 = s1; ; s2 = s2->next) {
		if (s2->abc_type == ABC_T_NOTE) {
			if (s2->nflags > nflags)
				nflags = s2->nflags;
			notes++;
			if (s2->staff != staff)
				two_staves = 1;
			if (s2->stem != s1->stem)
				two_dir = 1;
			if (!visible
			 && !(s2->flags & ABC_F_INVIS)
			 && (!(s2->flags & ABC_F_STEMLESS)
			  || (s2->sflags & S_TREM2)))
				visible = 1;
			if (s2->sflags & S_BEAM_END)
				break;
		}
		if (!s2->next) {		/* beam towards next music line */
			for (; ; s2 = s2->prev) {
				if (s2->abc_type == ABC_T_NOTE)
					break;
			}
			s = sym_dup(s2);
			s->next = s2->next;
			if (s->next)
				s->next->prev = s;
			s2->next = s;
			s->prev = s2;
			s->ts_next = s2->ts_next;
			if (s->ts_next)
				s->ts_next->ts_prev = s;
			s2->ts_next = s;
			s->ts_prev = s2;
			s->sflags &= S_SEQST;
			s->sflags |= S_BEAM_END | S_TEMP;
			s->u.note.slur_st = 0;
			s->u.note.slur_end = 0;
			s->x += 12;
			if (s->x < realwidth - 12)
				s->x = realwidth - 12;
			s2 = s;
			notes++;
			break;
		}
	}
	if (!visible)
		return 0;

	bm->s2 = s2;			/* (don't display the flags) */
	if (staff_tb[staff].y == 0) {	/* staves not defined */
		if (two_staves)
			return 0;
	} else {			/* staves defined */
		if (!two_staves) {
			bm->s1 = s1;	/* beam already calculated */
if (s1->xs == s2->xs)
 bug("beam with null length", 1);
			bm->a = (s1->ys- s2->ys) / (s1->xs - s2->xs);
			bm->b = s1->ys - s1->xs * bm->a
				+ staff_tb[staff].y;
			bm->nflags = nflags;
			return 1;
		}
	}

	sx = sy = sxx = sxy = syy = 0;	/* linear fit through stem ends */
	for (s = s1; ; s = s->next) {
		if (s->abc_type != ABC_T_NOTE)
			continue;
		if ((scale = voice_tb[s->voice].scale) == 1)
			scale = staff_tb[s->staff].staffscale;
		if (s->stem >= 0)
			x = stem_xoff + s->u.note.notes[0].shhd;
		else
			x = -stem_xoff + s->u.note.notes[s->nhd].shhd;
		x *= scale;
		x += s->x;
		s->xs = x;
		y = s->ys + staff_tb[s->staff].y;
		sx += x; sy += y;
		sxx += x * x; sxy += x * y; syy += y * y;
		if (s == s2)
			break;
	}

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

	/* the next few lines modify the slope of the beam */
	if (!(s1->flags & ABC_F_GRACE)) {
		if (notes >= 3) {
			float hh;

			hh = syy - a * sxy - b * sy;	/* flatten if notes not in line */
			if (hh > 0
			 && hh / (notes - 2) > .5)
				a *= BEAM_FLATFAC;
		}
		if (a >= 0)
			a = BEAM_SLOPE * a / (BEAM_SLOPE + a);	/* max steepness for beam */
		else
			a = BEAM_SLOPE * a / (BEAM_SLOPE - a);
	} else {
		if (a > BEAM_SLOPE)
			a = BEAM_SLOPE;
		else if (a < -BEAM_SLOPE)
			a = -BEAM_SLOPE;
	}

	/* to decide if to draw flat etc. use normalized slope a0 */
	a0 = a * (s2->xs - s1->xs) / (20 * (notes - 1));

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

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

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

	/* have flat beams when asked */
	if ( cfmt.flatbeamgracing && s1->flags & ABC_F_GRACE)
		a = 0;

	if (cfmt.flatbeams) {
//		if (!(s1->flags & ABC_F_GRACE))
//			b = -11 + staff_tb[staff].y;
//		else
//			b = 35 + staff_tb[staff].y;
		a = 0;
	}
	b = (sy - a * sx) / notes;	/* recalculate b for new slope */

/*fixme: have a look again*/
	/* have room for the symbols in the staff */
	max_stem_err = 0;		/* check stem lengths */
	s = s1;
	if (two_dir) {				/* 2 directions */
/*fixme: more to do*/
		if (!(s1->flags & ABC_F_GRACE))
			ys = BEAM_SHIFT;
		else
			ys = 3.5;
		ys *= (nflags - 1);
		ys += BEAM_DEPTH;
		ys *= .5;
		if (s1->stem != s2->stem && s1->nflags < s2->nflags)
			ys *= s2->stem;
		else
			ys *= s1->stem;
		b += ys;
	} else if (!(s1->flags & ABC_F_GRACE)) {	/* normal notes */
		float stem_err, beam_h;

		beam_h = BEAM_DEPTH + BEAM_SHIFT * (nflags - 1);
		while (s->ts_prev->abc_type == ABC_T_NOTE
		    && s->ts_prev->time == s->time
		    && s->ts_prev->x > s1->xs)
			s = s->ts_prev;

		for (; s && s->time <= s2->time; s = s->ts_next) {
			if (s->abc_type != ABC_T_NOTE
			 || (s->flags & ABC_F_INVIS)
			 || (s->staff != staff
			  && s->voice != voice)) {
				continue;
			}
			x = s->voice == voice ? s->xs : s->x;
			ys = a * x + b - staff_tb[s->staff].y;
			if (s->voice == voice) {
				if (s->nhd == 0)
					stem_err = min_tb[0][(unsigned) s->nflags];
				else
					stem_err = min_tb[1][(unsigned) s->nflags];
				if (s->stem > 0) {
					if (s->pits[s->nhd] > 26) {
						stem_err -= 2;
						if (s->pits[s->nhd] > 28)
							stem_err -= 2;
					}
					stem_err -= ys - (float) (3 *
						(s->pits[s->nhd] - 18));
				} else {
					if (s->pits[0] < 18) {
						stem_err -= 2;
						if (s->pits[0] < 16)
							stem_err -= 2;
					}
					stem_err -= (float) (3 *
						(s->pits[0] - 18)) - ys;
				}
				stem_err += BEAM_DEPTH + BEAM_SHIFT * (s->nflags - 1);
			} else {
/*fixme: KO when two_staves*/
				if (s1->stem > 0) {
					if (s->stem > 0) {
/*fixme: KO when the voice numbers are inverted*/
						if (s->ymn > ys + 4
						 || s->ymx < ys - beam_h - 2)
							continue;
						if (s->voice > voice)
							stem_err = s->ymx - ys;
						else
							stem_err = s->ymn + 8 - ys;
					} else {
						stem_err = s->ymx - ys;
					}
				} else {
					if (s->stem < 0) {
						if (s->ymx < ys - 4
						 || s->ymn > ys - beam_h - 2)
							continue;
						if (s->voice < voice)
							stem_err = ys - s->ymn;
						else
							stem_err = ys - s->ymx + 8;
					} else {
						stem_err = ys - s->ymn;
					}
				}
				stem_err += 2 + beam_h;
			}
			if (stem_err > max_stem_err)
				max_stem_err = stem_err;
		}
	} else {				/* grace notes */
		for ( ; ; s = s->next) {
			float stem_err;

			ys = a * s->xs + b - staff_tb[s->staff].y;
			stem_err = GSTEM - 2;
			if (s->stem > 0)
				stem_err -= ys - (float) (3 *
					(s->pits[s->nhd] - 18));
			else
				stem_err += ys - (float) (3 *
					(s->pits[0] - 18));
			stem_err += 3 * (s->nflags - 1);
			if (stem_err > max_stem_err)
				max_stem_err = stem_err;
			if (s == s2)
				break;
		}
	}

	if (max_stem_err > 0)		/* shift beam if stems too short */
		b += s1->stem * max_stem_err;

	/* have room for the gracenotes, bars and clefs */
/*fixme: test*/
    if (!two_staves && !two_dir)
	for (s = s1->next; ; s = s->next) {
		struct SYMBOL *g;

		switch (s->type) {
		case NOTEREST:		/* cannot move rests in multi-voices */
			if (s->abc_type != ABC_T_REST)
				break;
			g = s->ts_next;
			if (!g || g->staff != staff
			 || g->type != NOTEREST)
				break;
//fixme:too much vertical shift if some space above the note
//fixme:this does not fix rest under beam in second voice (ts_prev)
			/*fall thru*/
		case BAR:
#if 1
			if (s->flags & ABC_F_INVIS)
#else
//??
			if (!(s->flags & ABC_F_INVIS))
#endif
				break;
			/*fall thru*/
		case CLEF:
			y = a * s->x + b;
			if (s1->stem > 0) {
				y = s->ymx - y
					+ BEAM_DEPTH + BEAM_SHIFT * (nflags - 1)
					+ 2;
				if (y > 0)
					b += y;
			} else {
				y = s->ymn - y
					- BEAM_DEPTH - BEAM_SHIFT * (nflags - 1)
					- 2;
				if (y < 0)
					b += y;
			}
			break;
		case GRACE:
			g = s->extra;
			for ( ; g; g = g->next) {
				if (g->type != NOTEREST)
					continue;
				y = a * g->x + b;
				if (s1->stem > 0) {
					y = g->ymx - y
						+ BEAM_DEPTH + BEAM_SHIFT * (nflags - 1)
						+ 2;
					if (y > 0)
						b += y;
				} else {
					y = g->ymn - y
						- BEAM_DEPTH - BEAM_SHIFT * (nflags - 1)
						- 2;
					if (y < 0)
						b += y;
				}
			}
			break;
		}
		if (s == s2)
			break;
	}

	if (a == 0)		/* shift flat beams onto staff lines */
		b += b_pos(s1->flags & ABC_F_GRACE, s1->stem, nflags,
				b - staff_tb[staff].y);

	/* adjust final stems and rests under beam */
	for (s = s1; ; s = s->next) {
		float dy;

		switch (s->abc_type) {
		case ABC_T_NOTE:
			s->ys = a * s->xs + b - staff_tb[s->staff].y;
			if (s->stem > 0) {
				s->ymx = s->ys + 2.5;
#if 0
//fixme: hack
				if (s->ts_prev
				 && s->ts_prev->stem > 0
				 && s->ts_prev->staff == s->staff
				 && s->ts_prev->ymn < s->ymx
				 && s->ts_prev->x == s->x
				 && s->u.note.notes[0].shhd == 0) {
					s->ts_prev->x -= 5;	/* fix stem clash */
					s->ts_prev->xs -= 5;
				}
#endif
			} else {
				s->ymn = s->ys - 2.5;
			}
			break;
		case ABC_T_REST:
			y = a * s->x + b - staff_tb[s->staff].y;
			dy = BEAM_DEPTH + BEAM_SHIFT * (nflags - 1)
				+ (s->head != H_FULL ? 4 : 9);
			if (s1->stem > 0) {
				y -= dy;
				if (s1->multi == 0 && y > 12)
					y = 12;
				if (s->y <= y)
					break;
			} else {
				y += dy;
				if (s1->multi == 0 && y < 12)
					y = 12;
				if (s->y >= y)
					break;
			}
			if (s->head != H_FULL) {
				int iy;

				iy = ((int) y + 3 + 12) / 6 * 6 - 12;
				y = iy;
			}
			s->y = y;
			break;
		}
		if (s == s2)
			break;
	}

	/* save beam parameters */
	if (staff_tb[staff].y == 0)	/* if staves not defined */
		return 0;
	bm->s1 = s1;
	bm->a = a;
	bm->b = b;
	bm->nflags = nflags;
	return 1;
}

/* -- draw a single beam -- */
/* (the staves are defined) */
static void draw_beam(float x1,
		      float x2,
		      float dy,
		      float h,
		      struct BEAM *bm,
		      int n)			/* beam number (1..n) */
{
	struct SYMBOL *s;
	float y1, dy2;

	s = bm->s1;
	if ((s->sflags & S_TREM2) && n > s->nflags - s->aux
	 && s->head != H_EMPTY) {
		if (s->head >= H_OVAL) {
			x1 = s->x + 6;
			x2 = bm->s2->x - 6;
		} else {
			x1 += 5;
			x2 -= 6;
		}
	}

	y1 = bm->a * x1 + bm->b - dy;
	x2 -= x1;
	dy2 = bm->a * x2;

	putf(h);
	putx(x2);
	putf(dy2);
	putxy(x1, y1);
	a2b("bm\n");
}

/* -- draw the beams for one word -- */
/* (the staves are defined) */
static void draw_beams(struct BEAM *bm)
{
	struct SYMBOL *s, *s1, *s2;
	int i, beam_dir;
	float shift, bshift, bstub, bh, da;

	s1 = bm->s1;
/*fixme: KO if many staves with different scales*/
//fixme: useless?
//	set_scale(s1);
	s2 = bm->s2;
	if (!(s1->flags & ABC_F_GRACE)) {
		bshift = BEAM_SHIFT;
		bstub = BEAM_STUB;
		shift = .34;		/* (half width of the stem) */
		bh = BEAM_DEPTH;
	} else {
		bshift = 3.5;
		bstub = 3.2;
		shift = .29;
		bh = 1.8;
	}

/*fixme: quick hack for stubs at end of beam and different stem directions*/
	beam_dir = s1->stem;
	if (s1->stem != s2->stem
	 && s1->nflags < s2->nflags)
		beam_dir = s2->stem;
	if (beam_dir < 0)
		bh = -bh;
	if (cur_trans == 0 && cur_scale != 1) {
		bm->a /= cur_scale;
		bm->b = s1->ys - s1->xs * bm->a
			+ staff_tb[s1->staff].y;
		bshift *= cur_scale;
	}

	/* make first beam over whole word and adjust the stem lengths */
	draw_beam(s1->xs - shift, s2->xs + shift, 0., bh, bm, 1);
	da = 0;
	for (s = s1; ; s = s->next) {
		if (s->abc_type == ABC_T_NOTE
		 && s->stem != beam_dir)
			s->ys = bm->a * s->xs + bm->b
				- staff_tb[s->staff].y
				+ bshift * (s->nflags - 1) * s->stem
				- bh;
		if (s == s2)
			break;
	}

	if (s1->sflags & S_FEATHERED_BEAM) {
		da = bshift / (s2->xs - s1->xs);
		if (s1->dur > s2->dur) {
			da = -da;
			bshift = da * s1->xs;
		} else {
			bshift = da * s2->xs;
		}
		da = da * beam_dir;
	}

	/* other beams with two or more flags */
	shift = 0;
	for (i = 2; i <= bm->nflags; i++) {
		shift += bshift;
		if (da != 0)
			bm->a += da;
		for (s = s1; ; s = s->next) {
			struct SYMBOL *k1, *k2;
			float x1;

			if (s->abc_type != ABC_T_NOTE
			 || s->nflags < i) {
				if (s == s2)
					break;
				continue;
			}
			if ((s->sflags & S_TREM1)
			 && i > s->nflags - s->aux) {
				if (s->head >= H_OVAL)
					x1 = s->x;
				else
					x1 = s->xs;
				draw_beam(x1 - 5, x1 + 5,
					  (shift + 2.5) * beam_dir,
					  bh, bm, i);
				if (s == s2)
					break;
				continue;
			}
			k1 = s;
			for (;;) {
				if (s == s2)
					break;
				if (s->next->type == NOTEREST) {
					if (s->next->sflags & S_TREM1) {
						if (s->next->nflags - s->next->aux < i)
							break;
					} else if (s->next->nflags < i) {
						break;
					}
				}
				if ((s->next->sflags & S_BEAM_BR1)
				 || ((s->next->sflags & S_BEAM_BR2) && i > 2))
					break;
				s = s->next;
			}
			k2 = s;
			while (k2->abc_type != ABC_T_NOTE)
				k2 = k2->prev;
			x1 = k1->xs;
			if (k1 == k2) {
				if (k1 == s1) {
					x1 += bstub;
				} else if (k1 == s2) {
					x1 -= bstub;
				} else if ((k1->sflags & S_BEAM_BR1)
					|| ((k1->sflags & S_BEAM_BR2)
					 && i > 2)) {
					x1 += bstub;
				} else {
					struct SYMBOL *k;

					k = k1->next;
					while (k->abc_type != ABC_T_NOTE)
						k = k->next;
					if ((k->sflags & S_BEAM_BR1)
					 || ((k->sflags & S_BEAM_BR2)
					  && i > 2)) {
						x1 -= bstub;
					} else {
						k1 = k1->prev;
						while (k1->abc_type != ABC_T_NOTE)
							k1 = k1->prev;
						if (k1->nflags < k->nflags
						 || (k1->nflags == k->nflags
						  && k1->dots < k->dots))
							x1 += bstub;
						else
							x1 -= bstub;
					}
				}
			}
			draw_beam(x1, k2->xs,
#if 1
				  shift * beam_dir,
#else
				  shift * k1->stem,	/*fixme: more complicated */
#endif
				  bh, bm, i);
			if (s == s2)
				break;
		}
	}
	if (s1->sflags & S_TEMP)
		unlksym(s1);
	else if (s2->sflags & S_TEMP)
		unlksym(s2);
}

/* -- draw a system brace or bracket -- */
static void draw_sysbra(float x, int staff, int flag)
{
	int i, end;
	float yt, yb;

	while (cursys->staff[staff].empty) {
//	    || staff_tb[staff].stafflines == 0) {
		if (cursys->staff[staff].flags & flag)
			return;
		staff++;
	}
	i = end = staff;
	for (;;) {
		if (!cursys->staff[i].empty)
//		 && staff_tb[i].stafflines != 0)
			end = i;
		if (cursys->staff[i].flags & flag)
			break;
		i++;
	}
	yt = staff_tb[staff].y + staff_tb[staff].topbar
				* staff_tb[staff].staffscale;
	yb = staff_tb[end].y + staff_tb[end].botbar
				* staff_tb[end].staffscale;
	a2b("%.1f %.1f %.1f %s\n",
	     yt - yb, x, yt,
	     (flag & (CLOSE_BRACE | CLOSE_BRACE2)) ? "brace" : "bracket");
}

/* -- draw the left side of the staves -- */
static void draw_lstaff(float x)
{
	int i, j, l, nst;
	float yb;

	if (cfmt.alignbars)
		return;
	nst = cursys->nstaff;
	l = 0;
	for (i = 0; ; i++) {
		if (cursys->staff[i].flags & (OPEN_BRACE | OPEN_BRACKET))
			l++;
		if (!cursys->staff[i].empty)
//		 && staff_tb[i].stafflines != 0)
			break;
		if (cursys->staff[i].flags & (CLOSE_BRACE | CLOSE_BRACKET))
			l--;
		if (i == nst)
			break;
	}
	for (j = nst; j > i; j--) {
		if (!cursys->staff[j].empty)
//		 && staff_tb[j].stafflines != 0)
			break;
	}
	if (i == j && l == 0)
		return;
	set_sscale(-1);
	yb = staff_tb[j].y + staff_tb[j].botbar
				* staff_tb[j].staffscale;
	a2b("%.1f %.1f %.1f bar\n",
	     staff_tb[i].y + staff_tb[i].topbar * staff_tb[i].staffscale - yb,
	     x, yb);
	for (i = 0; i <= nst; i++) {
		if (cursys->staff[i].flags & OPEN_BRACE)
			draw_sysbra(x, i, CLOSE_BRACE);
		if (cursys->staff[i].flags & OPEN_BRACKET)
			draw_sysbra(x, i, CLOSE_BRACKET);
		if (cursys->staff[i].flags & OPEN_BRACE2)
			draw_sysbra(x - 6, i, CLOSE_BRACE2);
		if (cursys->staff[i].flags & OPEN_BRACKET2)
			draw_sysbra(x - 6, i, CLOSE_BRACKET2);
	}
}

/* -- draw a staff -- */
static void draw_staff(int staff,
			float x1, float x2)
{
	char *stafflines;
	int i, l, thick = -1;
	float y, w;

	/* draw the staff */
	set_sscale(staff);
	y = staff_tb[staff].y;
	stafflines = staff_tb[staff].stafflines;
	l = strlen(stafflines);
	for (i = 0; i < l; i++) {
		if (stafflines[i] != '.') {
			w = x2 - x1;
			for ( ; i < l; i++) {
				if (stafflines[i] != '.') {
					if (stafflines[i] != '|') {
						if (thick != 1) {
							if (thick >= 0)
								a2b("stroke\n");
							a2b("1.5 SLW ");
							thick = 1;
						}
					} else {
						if (thick != 0) {
							if (thick >= 0)
								a2b("stroke\n");
							a2b("dlw ");
							thick = 0;
						}
					}
					putx(w);
					putxy(x1, y);
					a2b("M 0 RL ");
				}
				y += 6;
			}
			a2b("stroke\n");
			break;
		}
		y += 6;
	}
}

/* -- draw the time signature -- */
static void draw_timesig(float x,
			 struct SYMBOL *s)
{
	unsigned i, staff, l, l2;
	char *f, meter[64];
	float dx, y;

	if (s->u.meter.nmeter == 0)
		return;
	staff = s->staff;
	x -= s->wl;
	y = staff_tb[staff].y;
	for (i = 0; i < s->u.meter.nmeter; i++) {
		l = strlen(s->u.meter.meter[i].top);
		if (l > sizeof s->u.meter.meter[i].top)
			l = sizeof s->u.meter.meter[i].top;
		if (s->u.meter.meter[i].bot[0] != '\0') {
			sprintf(meter, "(%.8s)(%.2s)",
				s->u.meter.meter[i].top,
				s->u.meter.meter[i].bot);
			f = "tsig";
			l2 = strlen(s->u.meter.meter[i].bot);
			if (l2 > sizeof s->u.meter.meter[i].bot)
				l2 = sizeof s->u.meter.meter[i].bot;
			if (l2 > l)
				l = l2;
		} else switch (s->u.meter.meter[i].top[0]) {
			case 'C':
				if (s->u.meter.meter[i].top[1] != '|') {
					f = "csig";
				} else {
					f = "ctsig";
					l--;
				}
				dx = (float) (13 * l);
				putxy(x - 5 + dx * .5, y + 12);
				a2b("%s\n", f);
				x += dx;
				continue;
			case 'c':
				if (s->u.meter.meter[i].top[1] != '.') {
					f = "imsig";
				} else {
					f = "iMsig";
					l--;
				}
				meter[0] = '\0';
				break;
			case 'o':
				if (s->u.meter.meter[i].top[1] != '.') {
					f = "pmsig";
				} else {
					f = "pMsig";
					l--;
				}
				meter[0] = '\0';
				break;
			case '(':
			case ')':
				sprintf(meter, "(\\%s)",
					s->u.meter.meter[i].top);
				f = "stsig";
				break;
			default:
				sprintf(meter, "(%.8s)",
					s->u.meter.meter[i].top);
				f = "stsig";
				break;
		}
		if (meter[0] != '\0')
			a2b("%s ", meter);
		dx = (float) (13 * l);
		putxy(x + dx * .5, y);
		a2b("%s\n", f);
		x += dx;
	}
}

/* -- draw an accidental -- */
static void draw_acc(int acc, int microscale)
{
	int n, d;

	n = parse.micro_tb[acc >> 3];
	if (acc >> 3 != 0 && microscale) {
		if (microscale) {
			d = microscale;
			n = acc >> 3;
		} else {
			d = ((n & 0xff) + 1) * 2;
			n = (n >> 8) + 1;
		}
		a2b("%d %s%d ", n, acc_tb[acc & 0x07], d);
	} else {
		if (acc >> 3 != 0
		 && cfmt.nedo)		// %%MIDI temperamentequal <nedo>
			n = ((((n >> 8) + 1) * 12) - 1) * 256 + cfmt.nedo - 1;
		a2b("%s%d ", acc_tb[acc & 0x07], n);
	}
}

// draw helper lines
static void draw_hl(float x, float staffb, int up,
		int y, char *stafflines, char *hltype)
{
	int i, l;

	l = strlen(stafflines);

	// lower ledger lines
	if (!up) {
		for (i = 0; i < l - 1; i++) {
			if (stafflines[i] != '.')
				break;
		}
		i = i * 6 - 6;
		for ( ; i >= y; i -= 6) {
			putxy(x, staffb + i);
			a2b("%s ", hltype);
		}
		return;
	}

	// upper ledger lines
	i = l * 6;
	for ( ; i <= y; i += 6) {
		putxy(x, staffb + i);
		a2b("%s ", hltype);
	}
}

/* -- draw a key signature -- */
static void draw_keysig(struct VOICE_S *p_voice,
			float x,
			struct SYMBOL *s)
{
	int old_sf = s->aux;
	int staff = p_voice->staff;
	float staffb = staff_tb[staff].y;
	int i, clef_ix, shift;
	const signed char *p_seq;

	static const char sharp_cl[] = {24, 9, 15, 21, 6, 12, 18};
	static const char flat_cl[] = {12, 18, 24, 9, 15, 21, 6};
	// (the ending 0 is needed to avoid array overflow)
	static const signed char sharp1[] = {-9, 12, -9, -9, 12, -9, 0};
	static const signed char sharp2[] = {12, -9, 12, -9, 12, -9, 0};
	static const signed char flat1[] = {9, -12, 9, -12, 9, -12, 0};
	static const signed char flat2[] = {-12, 9, -12, 9, -12, 9, 0};

	clef_ix = s->u.key.clef_delta;
	if (clef_ix & 1)
		clef_ix += 7;
	clef_ix /= 2;
	while (clef_ix < 0)
		clef_ix += 7;
	clef_ix %= 7;

	/* normal accidentals */
	if (s->u.key.nacc == 0 && !s->u.key.empty) {

		/* put neutrals if 'accidental cancel' */
		if (cfmt.cancelkey || s->u.key.sf == 0) {

			/* when flats to sharps, or sharps to flats, */
			if (s->u.key.sf == 0
			 || old_sf * s->u.key.sf < 0) {

				/* old sharps */
				shift = sharp_cl[clef_ix];
				p_seq = shift > 9 ? sharp1 : sharp2;
				for (i = 0; i < old_sf; i++) {
					putxy(x, staffb + shift);
					a2b("nt0 ");
					shift += *p_seq++;
					x += 5.5;
				}

				/* old flats */
				shift = flat_cl[clef_ix];
				p_seq = shift < 18 ? flat1 : flat2;
				for (i = 0; i > old_sf; i--) {
					putxy(x, staffb + shift);
					a2b("nt0 ");
					shift += *p_seq++;
					x += 5.5;
				}
				if (s->u.key.sf != 0)
					x += 3;		/* extra space */
			}
		}

		/* new sharps */
		if (s->u.key.sf > 0) {
			shift = sharp_cl[clef_ix];
			p_seq = shift > 9 ? sharp1 : sharp2;
			for (i = 0; i < s->u.key.sf; i++) {
				putxy(x, staffb + shift);
				a2b("sh0 ");
				shift += *p_seq++;
				x += 5.5;
			}
			if (cfmt.cancelkey && s->u.key.sf < old_sf) {
				x += 2;
				for (; i < old_sf; i++) {
					putxy(x, staffb + shift);
					a2b("nt0 ");
					shift += *p_seq++;
					x += 5.5;
				}
			}
		}

		/* new flats */
		if (s->u.key.sf < 0) {
			shift = flat_cl[clef_ix];
			p_seq = shift < 18 ? flat1 : flat2;
			for (i = 0; i > s->u.key.sf; i--) {
				putxy(x, staffb + shift);
				a2b("ft0 ");
				shift += *p_seq++;
				x += 5.5;
			}
			if (cfmt.cancelkey && s->u.key.sf > old_sf) {
				x += 2;
				for (; i > old_sf; i--) {
					putxy(x, staffb + shift);
					a2b("nt0 ");
					shift += *p_seq++;
					x += 5.5;
				}
			}
		}
	} else {
		int acc, last_acc, last_shift;

		/* explicit accidentals */
		last_acc = s->u.key.accs[0];
		last_shift = 100;
		for (i = 0; i < s->u.key.nacc; i++) {
			acc = s->u.key.accs[i];
			shift = s->u.key.clef_delta * 3	// clef shift
				+ 3 * (s->u.key.pits[i] - 18);
			if (i != 0
			 && (shift > last_shift + 18
			  || shift < last_shift - 18))
				x -= 5.5;		// no clash
			else if (acc != last_acc)
				x += 3;
			last_acc = acc;
			if (shift < 0)
				draw_hl(x, staffb, 0,
					shift,		/* lower ledger line */
					staff_tb[s->staff].stafflines, "hl");
			else if (shift > 24)
				draw_hl(x, staffb, 1,
					shift,		/* upper ledger line */
					staff_tb[s->staff].stafflines, "hl");
			last_shift = shift;
			putxy(x, staffb + shift);
			draw_acc(acc, s->u.key.microscale);
			x += 5.5;
		}
	}
	if (old_sf != 0 || s->u.key.sf != 0 || s->u.key.nacc != 0)
		a2b("\n");
}

/* -- convert the standard measure bars -- */
static int bar_cnv(int bar_type)
{
	switch (bar_type) {
	case B_OBRA:
/*	case B_CBRA: */
	case (B_OBRA << 4) + B_CBRA:
		return 0;			/* invisible */
//	case B_COL:
//		return B_BAR;			/* dotted */
#if 0
	case (B_CBRA << 4) + B_BAR:
		return B_BAR;
#endif
	case (B_SINGLE << 8) | B_LREP:
	case (B_BAR << 4) + B_COL:
		bar_type |= (B_OBRA << 8);		/* ||: and |: -> [|: */
		break;
	case (B_BAR << 8) + (B_COL << 4) + B_COL:
		bar_type |= (B_OBRA << 12);		/* |:: -> [|:: */
		break;
	case (B_BAR << 12) + (B_COL << 8) + (B_COL << 4) + B_COL:
		bar_type |= (B_OBRA << 16);		/* |::: -> [|::: */
		break;
	case (B_COL << 4) + B_BAR:
	case (B_COL << 8) + (B_COL << 4) + B_BAR:
	case (B_COL << 12) + (B_COL << 8) + (B_COL << 4) + B_BAR:
		bar_type <<= 4;
		bar_type |= B_CBRA;			/* :..| -> :..|] */
		break;
	case (B_COL << 4) + B_COL:
		bar_type = cfmt.dblrepbar;		/* :: -> dble repeat bar */
		break;
	}
	return bar_type;
}

/* -- draw a measure bar -- */
static void draw_bar(struct SYMBOL *s, float bot, float h)
{
	int staff, bar_type;
	float x, yb;
	char *psf;

	staff = s->staff;
	yb = staff_tb[staff].y;
	x = s->x;

	/* if measure repeat, draw the '%' like glyphs */
	if (s->u.bar.len != 0) {
		struct SYMBOL *s2;

		set_scale(s);
		if (s->u.bar.len == 1) {
			for (s2 = s->prev; s2->abc_type != ABC_T_REST; s2 = s2->prev)
				;
			putxy(s2->x, yb + 12);
			a2b("mrep\n");
		} else {
			putxy(x, yb + 12);
			a2b("mrep2\n");
			if (s->voice == cursys->top_voice) {
/*fixme				set_font(s->gcf); */
				set_font(cfmt.anf);
				putxy(x, yb + staff_tb[staff].topbar + 4);
				a2b("M(%d)showc\n", s->u.bar.len);
			}
		}
	}

	/* don't put a line between the staves if there is no bar above */
	if (staff != 0
	 && s->ts_prev
// (a staff may not be displayed)
//	 && (s->ts_prev->type != BAR || s->ts_prev->staff != staff - 1))
	 && s->ts_prev->type != BAR)
		h = staff_tb[staff].topbar * staff_tb[staff].staffscale;

	bar_type = bar_cnv(s->u.bar.type);
	if (bar_type == 0)
		return;				/* invisible */
	for (;;) {
		psf = "bar";
		switch (bar_type & 0x07) {
		case B_BAR:
			if (s->u.bar.dotted)
				psf = "dotbar";
			x -= 1;
			break;
		case B_OBRA:
		case B_CBRA:
			psf = "thbar";
			x -= 3;
			break;
		case B_COL:
			x -= 2;
			break;
		}
		switch (bar_type & 0x07) {
		default:
			set_sscale(-1);
			a2b("%.1f %.1f %.1f %s ", h, x, bot, psf);
			break;
		case B_COL:
			set_sscale(staff);
			putxy(x + 1, staff_tb[staff].y);
			a2b("rdots ");
			break;
		}
		bar_type >>= 4;
		if (bar_type == 0)
			break;
		x -= 3;
	}
	a2b("\n");
}

/* -- draw a rest -- */
/* (the staves are defined) */
static void draw_rest(struct SYMBOL *s)
{
	int i, j, y, l;
//	int no_head;
	char *stafflines;
	float x, dotx, staffb;
	static char *rest_tb[NFLAGS_SZ] = {
		"r128", "r64", "r32", "r16", "r8",
		"r4",
		"r2", "r1", "r0", "r00"
	};

	/* don't display the rests of invisible staves */
	/* (must do this here for voices out of their normal staff) */
	if (staff_tb[s->staff].empty)
		return;

	/* if rest alone in the measure or measure repeat, center */
	if (s->dur == voice_tb[s->voice].meter.wmeasure
	 || ((s->sflags & S_REPEAT) && s->doty >= 0)) {
		struct SYMBOL *s2;

		/* don't use next/prev: there is no bar in voice averlay */
		s2 = s->ts_next;
		while (s2 && s2->time != s->time + s->dur)
			s2 = s2->ts_next;
		if (s2)
			x = s2->x;
		else
			x = realwidth;
		s2 = s;
		while (!(s2->sflags & S_SEQST))
			s2 = s2->ts_prev;
		s2 = s2->ts_prev;
		x = (x + s2->x) * .5;

		/* center the associated decorations */
		if (s->u.note.dc.n > 0)
			deco_update(s, x - s->x);
		s->x = x;
	} else {
		x = s->x + s->u.note.notes[0].shhd * cur_scale;
	}
	if (s->flags & ABC_F_INVIS)
//	 && !(s->sflags & S_OTHER_HEAD))	//fixme: before new deco struct
		return;

	staffb = staff_tb[s->staff].y;		/* bottom of staff */

	if (s->sflags & S_REPEAT) {
		putxy(x, staffb + 12);
		if (s->doty < 0) {
			a2b("srep\n");
		} else {
			a2b("mrep\n");
			if (s->doty > 2
			 && s->voice == cursys->top_voice) {
/*fixme				set_font(s->gcf); */
				set_font(cfmt.anf);
				putxy(x, staffb + 24 + 4);
				a2b("M(%d)showc\n", s->doty);
			}
		}
		return;
	}

	y = s->y;

	i = C_XFLAGS - s->nflags;		/* rest_tb index */
	stafflines = staff_tb[s->staff].stafflines;
	l = strlen(stafflines);
	if (i == 7 && y == 12
	 && l <= 2)
		y -= 6;				/* semibreve a bit lower */

	putxy(x, y + staffb);				/* rest */
	a2b("%s ", s->u.note.notes[0].head ?
			s->u.note.notes[0].head : rest_tb[i]);

	/* output ledger line(s) when greater than minim */
	if (i >= 6) {
		j = y / 6;
		switch (i) {
		default:
			if (j >= l - 1 || stafflines[j + 1] != '|') {
				putxy(x, y + staffb);
				a2b("hl1 ");
			}
			if (i == 9) {		// longa
				y -= 6;
				j--;
			}
			break;
		case 7:
			y += 6;
			j++;
		case 6:
			break;
		}
		if (j >= l || stafflines[j] != '|') {
			putxy(x, y + staffb);
			a2b("hl1 ");
		}
	}

	dotx = 8;
	for (i = 0; i < s->dots; i++) {
		a2b("%.1f 3 dt ", dotx);
		dotx += 3.5;
	}
	a2b("\n");
}

/* -- draw grace notes -- */
/* (the staves are defined) */
static void draw_gracenotes(struct SYMBOL *s)
{
	int yy;
	float x0, y0, x1, y1, x2, y2, x3, y3, bet1, bet2, dy1, dy2;
	struct SYMBOL *g, *last;
	struct BEAM bm;

	/* draw the notes */
	bm.s2 = NULL;				/* (draw flags) */
	for (g = s->extra; g; g = g->next) {
		if (g->type != NOTEREST)
			continue;
		if ((g->sflags & (S_BEAM_ST | S_BEAM_END)) == S_BEAM_ST) {
			if (annotate)
				anno_out(g, 'b');
			if (calculate_beam(&bm, g))
				draw_beams(&bm);
		}
		draw_note(g->x, g, bm.s2 == NULL);
		if (annotate)
			anno_out(s, 'g');
		if (g == bm.s2)
			bm.s2 = NULL;			/* (draw flags again) */

		if (g->flags & ABC_F_SAPPO) {	/* (on 1st note only) */
			if (!g->next) {			/* if one note */
				x1 = 9;
				y1 = g->stem > 0 ? 5 : -5;
			} else {			/* many notes */
				x1 = (g->next->x - g->x) * .5 + 4;
				y1 = (g->ys + g->next->ys) * .5 - g->y;
				if (g->stem > 0)
					y1 -= 1;
				else
					y1 += 1;
			}
			putxy(x1, y1);
			a2b("g%ca\n", g->stem > 0 ? 'u' : 'd');
		}
		if (annotate
		 && (g->sflags & (S_BEAM_ST | S_BEAM_END)) == S_BEAM_END)
			anno_out(g, 'e');
		if (!g->next)
			break;			/* (keep the last note) */
	}

	/* slur */
	if (voice_tb[s->voice].key.instr == K_HP	/* no slur when bagpipe */
	 || voice_tb[s->voice].key.instr == K_Hp
	 || pipeformat
	 || !cfmt.graceslurs
	 || s->u.note.slur_st		/* explicit slur */
	 || !s->next
	 || s->next->abc_type != ABC_T_NOTE)
		return;
	last = g;
	if (last->stem >= 0) {
		yy = 127;
		for (g = s->extra; g; g = g->next) {
			if (g->type != NOTEREST)
				continue;
			if (g->y < yy) {
				yy = g->y;
				last = g;
			}
		}
		x0 = last->x;
		y0 = last->y - 5;
		if (s->extra != last) {
			x0 -= 4;
			y0 += 1;
		}
		s = s->next;
		x3 = s->x - 1;
		if (s->stem < 0)
			x3 -= 4;
		y3 = 3 * (s->pits[0] - 18) - 5;
		dy1 = (x3 - x0) * .4;
		if (dy1 > 3)
			dy1 = 3;
		dy2 = dy1;
		bet1 = .2;
		bet2 = .8;
		if (y0 > y3 + 7) {
			x0 = last->x - 1;
			y0 += .5;
			y3 += 6.5;
			x3 = s->x - 5.5;
			dy1 = (y0 - y3) * .8;
			dy2 = (y0 - y3) * .2;
			bet1 = 0;
		} else if (y3 > y0 + 4) {
			y3 = y0 + 4;
			x0 = last->x + 2;
			y0 = last->y - 4;
		}
	} else {
		yy = -127;
		for (g = s->extra; g; g = g->next) {
			if (g->type != NOTEREST)
				continue;
			if (g->y > yy) {
				yy = g->y;
				last = g;
			}
		}
		x0 = last->x;
		y0 = last->y + 5;
		if (s->extra != last) {
			x0 -= 4;
			y0 -= 1;
		}
		s = s->next;
		x3 = s->x - 1;
		if (s->stem >= 0)
			x3 -= 2;
		y3 = 3 * (s->pits[s->nhd] - 18) + 5;
		dy1 = (x0 - x3) * .4;
		if (dy1 < -3)
			dy1 = -3;
		dy2 = dy1;
		bet1 = .2;
		bet2 = .8;
		if (y0 < y3 - 7) {
			x0 = last->x - 1;
			y0 -= .5;
			y3 -= 6.5;
			x3 = s->x - 5.5;
			dy1 = (y0 - y3) * .8;
			dy2 = (y0 - y3) * .2;
			bet1 = 0;
		} else if (y3 < y0 - 4) {
			y3 = y0 - 4;
			x0 = last->x + 2;
			y0 = last->y + 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;

	a2b("%.2f %.2f %.2f %.2f %.2f %.2f ",
		x1 - x0, y1 - y0,
		x2 - x0, y2 - y0,
		x3 - x0, y3 - y0);
	putxy(x0, y0 + staff_tb[s->staff].y);
	a2b("gsl\n");
}

/* -- set the y offset of the dots -- */
static void setdoty(struct SYMBOL *s,
		    signed char *y_tb)
{
	int m, m1, y, doty;

	/* set the normal offsets */
	doty = s->doty;
	for (m = 0; m <= s->nhd; m++) {
		y = 3 * (s->pits[m] - 18);	/* note height on staff */
		if ((y % 6) == 0) {
			if (doty != 0)
				y -= 3;
			else
				y += 3;
		}
		y_tb[m] = y;
	}

	/* dispatch and recenter the dots in the staff space */
	for (m = 0; m < s->nhd; m++) {
		if (y_tb[m + 1] > y_tb[m])
			continue;
		m1 = m;
		while (m1 > 0) {
			if (y_tb[m1] > y_tb[m1 - 1] + 6)
				break;
			m1--;
		}
		if (3 * (s->pits[m1] - 18) - y_tb[m1]
				< y_tb[m + 1] - 3 *
					(s->pits[m + 1] - 18)) {
			while (m1 <= m)
				y_tb[m1++] -= 6;
		} else {
			y_tb[m + 1] = y_tb[m] + 6;
		}
	}
}

/* -- draw m-th head with accidentals and dots -- */
/* (the staves are defined) */
static void draw_basic_note(float x,
			    struct SYMBOL *s,
			    int m,
			    signed char *y_tb)
{
	struct note *note = &s->u.note.notes[m];
	int y, head, dots, nflags, acc;
//	int no_head;
	int old_color = -1;
	float staffb, shhd;
	char *p;
	char hd[32];

	staffb = staff_tb[s->staff].y;		/* bottom of staff */
	y = 3 * (s->pits[m] - 18);		/* note height on staff */
	shhd = note->shhd * cur_scale;

	if (s->flags & ABC_F_INVIS)
		return;

	putxy(x + shhd, y + staffb);		/* output x and y */

//	/* special case when no head */
//	if (s->nohdi1 >= 0
//	 && m >= s->nohdi1 && m < s->nohdi2) {
//		a2b("xydef");			/* set x y */
//		return;
//	}

	identify_note(s, note->len, &head, &dots, &nflags);
	acc = note->acc;

	/* output a ledger line if horizontal shift / chord
	 * and note on a line */
	if (y % 6 == 0
	 && shhd != (s->stem > 0 ? s->u.note.notes[0].shhd :
				s->u.note.notes[s->nhd].shhd)) {
		int yy;

		yy = 0;
		if (y >= 30) {
			yy = y;
			if (yy % 6)
				yy -= 3;
		} else if (y <= -6) {
			yy = y;
			if (yy % 6)
				yy += 3;
		}
		if (yy) {
			putxy(x + shhd, yy + staffb);
			a2b("hl ");
		}
	}

	/* draw the head */
	if (note->invisible) {
		p = "xydef";
	} else if ((p = note->head) != NULL) {
		snprintf(hd, sizeof hd, "%.*s", note->hlen, p);
		p = hd;
		a2b("2 copy xydef ");		/* set x y */
	} else if (s->flags & ABC_F_GRACE) {
		p = "ghd";
	} else if (s->type == CUSTOS) {
		p = "custos";
	} else if ((s->sflags & S_PERC) && acc != 0) {
		sprintf(hd, "p%shd", acc_tb[acc & 0x07]);
		acc = 0;
		p = hd;
	} else {
		switch (head) {
		case H_OVAL:
			if (note->len < BREVE) {
				p = "HD";
				break;
			}
			if (s->head != H_SQUARE) {
				p = "HDD";
				break;
			}
			/* fall thru */
		case H_SQUARE:
			p = note->len < BREVE * 2 ? "breve" : "longa";

			/* don't display dots on last note of the tune */
			if (!tsnext && s->next
			 && s->next->type == BAR && !s->next->next)
				dots = 0;
			break;
		case H_EMPTY:
			p = "Hd"; break;
		default:
			p = "hd"; break;
		}
	}
	if (note->color >= 0) {
		old_color = cur_color;
		set_color(note->color);
	}
	a2b("%s", p);

	/* draw the dots */
/*fixme: to see for grace notes*/
	if (dots) {
		float dotx;
		int doty;

		dotx = 7.7 + s->xmx - note->shhd;
		doty = y_tb[m] - y;
		if (scale_voice)
			doty /= cur_scale;
		while (--dots >= 0) {
			a2b(" %.1f %d dt", dotx, doty);
			dotx += 3.5;
		}
	}

	/* draw the accidental */
	if (acc != 0) {
		x -= note->shac * cur_scale;
		a2b(" ");
		putx(x);
		a2b(s->flags & ABC_F_GRACE ? "gsc " : "y ");
		draw_acc(acc, s->u.note.microscale);
		if (s->flags & ABC_F_GRACE)
			a2b(" grestore");
	}

	if (old_color >= 0) {
		a2b("\n");
		set_color(old_color);
	}
}

/* -- draw a note or a chord -- */
/* (the staves are defined) */
static void draw_note(float x,
		      struct SYMBOL *s,
		      int fl)
{
	int m, ma;
	float staffb, slen, shhd;
	char c, *hltype;
	signed char y_tb[MAXHD];

	if (s->dots)
		setdoty(s, y_tb);
	if (s->head >= H_OVAL)
		x += 1;
	staffb = staff_tb[s->staff].y;

	/* output the ledger lines */
	if (!(s->flags & ABC_F_INVIS)) {
		if (s->flags & ABC_F_GRACE) {
			hltype = "ghl";
		} else {
			switch (s->head) {
			default:
				hltype = "hl";
				break;
			case H_OVAL:
				hltype = "hl1";
				break;
			case H_SQUARE:
				hltype = "hl2";
				break;
			}
		}
		shhd = (s->stem > 0 ? s->u.note.notes[0].shhd :
					s->u.note.notes[s->nhd].shhd)
				* cur_scale;
		if (s->pits[0] < 22)
			draw_hl(x + shhd, staffb, 0,
				3 * (s->pits[0] - 18),	/* lower ledger lines */
				staff_tb[s->staff].stafflines, hltype);
		if (s->pits[s->nhd] > 22)
			draw_hl(x + shhd, staffb, 1,
				3 * (s->pits[s->nhd] - 18), /* upper ledger lines */
				staff_tb[s->staff].stafflines, hltype);
	}

	/* draw the master note, first or last one */
	if (cfmt.setdefl)
		set_defl(s->stem >= 0 ? DEF_STEMUP : 0);
	ma = s->stem >= 0 ? 0 : s->nhd;
	draw_basic_note(x, s, ma, y_tb);

	/* draw the stem and flags */
	if (!(s->flags & (ABC_F_INVIS | ABC_F_STEMLESS))) {
		char c2;

		c = s->stem >= 0 ? 'u' : 'd';
		slen = (s->ys - s->y) / voice_tb[s->voice].scale;
		if (!fl || s->nflags - s->aux <= 0) {	/* stem only */
			c2 = (s->flags & ABC_F_GRACE) ? 'g' : 's';
			if (s->nflags > 0) {	/* (fix for PS low resolution) */
				if (s->stem >= 0)
					slen -= 1;
				else
					slen += 1;
			}
			a2b(" %.1f %c%c", slen, c2, c);
		} else {				/* stem and flags */
			if (cfmt.straightflags)
				c = 's';		/* straight flag */
			c2 = (s->flags & ABC_F_GRACE) ? 'g' : 'f';
			a2b(" %d %.1f s%c%c", s->nflags - s->aux, slen, c2, c);
		}
	} else if (s->sflags & S_XSTEM) {	/* cross-staff stem */
		struct SYMBOL *s2;

		s2 = s->ts_prev;
			slen = (s2->stem > 0 ? s2->y : s2->ys) - s->y;
		slen += staff_tb[s2->staff].y - staffb;
/*fixme:KO when different scales*/
		slen /= voice_tb[s->voice].scale;
		a2b(" %.1f su", slen);
	}

	/* draw the tremolo bars */
	if (!(s->flags & ABC_F_INVIS)
	 && fl
	 && (s->sflags & S_TREM1)) {
		float x1;

		x1 = x + (s->stem > 0 ? s->u.note.notes[0].shhd :
					s->u.note.notes[s->nhd].shhd)
				* cur_scale;
		slen = 3 * (s->pits[s->stem > 0 ? s->nhd : 0] - 18);
		if (s->head >= H_OVAL) {
			if (s->stem > 0)
				slen += 5 + 5.4 * s->aux;
			else
				slen -= 5 + 5.4;
		} else {
			x1 += ((s->flags & ABC_F_GRACE)
					? GSTEM_XOFF : STEM_XOFF)
							* s->stem;
			if (s->stem > 0)
				slen += 6 + 5.4 * s->aux;
			else
				slen -= 6 + 5.4;
		}
		slen /= voice_tb[s->voice].scale;
		a2b(" %d ", s->aux);
		putxy(x1, staffb + slen);
		a2b("trem");
	}

	/* draw the other note heads */
	for (m = 0; m <= s->nhd; m++) {
		if (m == ma)
			continue;
		a2b(" ");
		draw_basic_note(x, s, m, y_tb);
	}
	a2b("\n");
}

/* -- find where to terminate/start a slur -- */
static struct SYMBOL *next_scut(struct SYMBOL *s)
{
	struct SYMBOL *prev;

	prev = s;
	for (s = s->next; s; s = s->next) {
		if (s->type == BAR
		 && ((s->sflags & S_RRBAR)
			|| s->u.bar.type == B_THIN_THICK
			|| s->u.bar.type == B_THICK_THIN
			|| (s->u.bar.repeat_bar
			 && s->text
			 && s->text[0] != '1')))
			return s;
		prev = s;
	}
	/*fixme: KO when no note for this voice at end of staff */
	return prev;
}

struct SYMBOL *prev_scut(struct SYMBOL *s)
{
	while (s->prev) {
		s = s->prev;
		if (s->type == BAR
		 && ((s->sflags & S_RRBAR)
		  || s->u.bar.type == B_THIN_THICK
		  || s->u.bar.type == B_THICK_THIN
		  || (s->u.bar.repeat_bar
		   && s->text
		   && s->text[0] != '1')))
			return s;
	}

	/* return a symbol of any voice starting before the start of the voice */
	s = voice_tb[s->voice].sym;
	while (s->type != CLEF)
		s = s->ts_prev;		/* search a main voice */
	if (s->next && s->next->type == KEYSIG)
		s = s->next;
	if (s->next && s->next->type == TIMESIG)
		s = s->next;
	return s;
}

/* -- decide whether a slur goes up or down -- */
static int slur_direction(struct SYMBOL *k1,
			  struct SYMBOL *k2)
{
	struct SYMBOL *s;
	int some_upstem, low;

	if ((k1->flags & ABC_F_GRACE) && k1->stem > 0)
		return -1;

	some_upstem = low = 0;
	for (s = k1; ; s = s->next) {
		if (s->abc_type == ABC_T_NOTE) {
			if (!(s->flags & ABC_F_STEMLESS)) {
				if (s->stem < 0)
					return 1;
				some_upstem = 1;
			}
			if (s->pits[0] < 22) /* if under middle staff */
				low = 1;
		}
		if (s == k2)
			break;
	}
	if (!some_upstem && !low)
		return 1;
	return -1;
}

/* -- output a slur / tie -- */
static void slur_out(float x1,
		     float y1,
		     float x2,
		     float y2,
		     int s,
		     float height,
		     int dotted,
		     int staff)	/* if < 0, the staves are defined */
{
	float alfa, beta, mx, my, xx1, yy1, xx2, yy2, dx, dy, dz;
	float scale_y;

	alfa = .3;
	beta = .45;

	/* for wide flat slurs, make shape more square */
	dy = y2 - y1;
	if (dy < 0)
		dy = -dy;
	dx = x2 - x1;
	if (dx > 40. && dy / dx < .7) {
		alfa = .3 + .002 * (dx - 40.);
		if (alfa > .7)
			alfa = .7;
	}

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

	mx = .5 * (x1 + x2);
	my = .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 = .03 * (x2 - x1);
//	if (dx > 10.)
//		dx = 10.;
//	dy = 1.6 * s;
	dy = 2 * s;
	dz = .2 + .001 * (x2 - x1);
	if (dz > .6)
		dz = .6;
	dz *= s;
	
	scale_y = scale_voice ? cur_scale : 1;
	if (!dotted)
		a2b("%.2f %.2f %.2f %.2f %.2f %.2f 0 %.2f ",
			(xx2 - dx - x2) / cur_scale,
				(yy2 + dy - y2 - dz) / scale_y,
			(xx1 + dx - x2) / cur_scale,
				(yy1 + dy - y2 - dz) / scale_y,
			(x1 - x2) / cur_scale,
				(y1 - y2 - dz) / scale_y,
				dz);
	a2b("%.2f %.2f %.2f %.2f %.2f %.2f ",
		(xx1 - x1) / cur_scale, (yy1 - y1) / scale_y,
		(xx2 - x1) / cur_scale, (yy2 - y1) / scale_y,
		(x2 - x1) / cur_scale, (y2 - y1) / scale_y);
	putxy(x1, y1);
	if (staff >= 0)
		a2b("y%d ", staff);
	a2b(dotted ? "dSL\n" : "SL\n");
}

/* -- check if slur sequence in a multi-voice staff -- */
static int slur_multi(struct SYMBOL *k1,
		      struct SYMBOL *k2)
{
	for (;;) {
		if (k1->multi != 0)	/* if multi voice */
			/*fixme: may change*/
			return k1->multi;
		if (k1 == k2)
			break;
		k1 = k1->next;
	}
	return 0;
}

/* -- draw a phrasing slur between two symbols -- */
/* (the staves are not yet defined) */
/* (not a pretty routine, this) */
static int draw_slur(struct SYMBOL *k1_orig,
		     struct SYMBOL *k2,
		     int m1,
		     int m2,
		     int slur_type)
{
	struct SYMBOL *k1, *k;
	float x1, y1, x2, y2, height, addy;
	float a, y, z, h, dx, dy;
	int s, nn, upstaff, two_staves;

	k1 = k1_orig;
	while (k1->voice != k2->voice)
		k1 = k1->ts_next;

/*fixme: if two staves, may have upper or lower slur*/
	switch (slur_type & 0x07) {	/* (ignore dot bit) */
	case SL_ABOVE: s = 1; break;
	case SL_BELOW: s = -1; break;
	default:
		if ((s = slur_multi(k1, k2)) == 0)
			s = slur_direction(k1, k2);
		break;
	}

	nn = 1;
	upstaff = k1->staff;
	two_staves = 0;
	if (k1 != k2)
	    for (k = k1->next; k; k = k->next) {
		if (k->type == NOTEREST) {
			nn++;
			if (k->staff != upstaff) {
				two_staves = 1;
				if (k->staff < upstaff)
					upstaff = k->staff;
			}
		}
		if (k == k2)
			break;
	}
/*fixme: KO when two staves*/
if (two_staves) error(0, k1, "*** multi-staves slurs not treated yet");

	/* fix endpoints */
//	x1 = k1->x + k1->xmx;		/* take the max right side */
	x1 = k1_orig->x + k1_orig->u.note.notes[0].shhd;
	if (k1_orig != k2) {
//		x2 = k2->x;
		x2 = k2->x + k2->u.note.notes[0].shhd;
	} else {		/* (the slur starts on last note of the line) */
		for (k = k2->ts_next; k; k = k->ts_next)
			if (k->sflags & S_NEW_SY)
				break;
		if (!k)
			x2 = realwidth;
		else
			x2 = k->x;
	}

	if (m1 >= 0) {
		y1 = (float) (3 * (k1->pits[m1] - 18) + 5 * s);
	} else {
		y1 = (float) (s > 0 ? k1->ymx + 2 : k1->ymn - 2);
		if (k1->abc_type == ABC_T_NOTE) {
			if (s > 0) {
				if (k1->stem > 0) {
					x1 += 5;
					if ((k1->sflags & S_BEAM_END)
					 && k1->nflags >= -1	/* if with a stem */
//fixme: check if at end of tuplet
					 && (!(k1->sflags & S_IN_TUPLET))) {
//					  || k1->ys > y1 - 3)) {
						if (k1->nflags > 0) {
							x1 += 2;
							y1 = k1->ys - 3;
						} else {
							y1 = k1->ys - 6;
						}
					} else {
						y1 = k1->ys + 3;
					}
				} else {
					y1 = k1->y + 8;
				}
			} else {
				if (k1->stem < 0) {
					x1 -= 1;
					if ((k1->sflags & S_BEAM_END)
					 && k1->nflags >= -1
					 && (!(k1->sflags & S_IN_TUPLET)
					  || k1->ys < y1 + 3)) {
						if (k1->nflags > 0) {
							x1 += 2;
							y1 = k1->ys + 3;
						} else {
							y1 = k1->ys + 6;
						}
					} else {
						y1 = k1->ys - 3;
					}
				} else {
					y1 = k1->y - 8;
				}
			}
		}
	}
	if (m2 >= 0) {
		y2 = (float) (3 * (k2->pits[m2] - 18) + 5 * s);
	} else {
		y2 = (float) (s > 0 ? k2->ymx + 2 : k2->ymn - 2);
		if (k2->abc_type == ABC_T_NOTE) {
			if (s > 0) {
				if (k2->stem > 0) {
					x2 += 1;
					if ((k2->sflags & S_BEAM_ST)
					 && k2->nflags >= -1
					 && (!(k2->sflags & S_IN_TUPLET)))
//						|| k2->ys > y2 - 3))
						y2 = k2->ys - 6;
					else
						y2 = k2->ys + 3;
				} else {
					y2 = k2->y + 8;
				}
			} else {
				if (k2->stem < 0) {
					x2 -= 5;
					if ((k2->sflags & S_BEAM_ST)
					 && k2->nflags >= -1
					 && (!(k2->sflags & S_IN_TUPLET)))
//						|| k2->ys < y2 + 3))
						y2 = k2->ys + 6;
					else
						y2 = k2->ys - 3;
				} else {
					y2 = k2->y - 8;
				}
			}
		}
	}

	if (k1->abc_type != ABC_T_NOTE) {
		y1 = y2 + 1.2 * s;
		x1 = k1->x + k1->wr * .5;
		if (x1 > x2 - 12)
			x1 = x2 - 12;
	}

	if (k2->abc_type != ABC_T_NOTE) {
		if (k1->abc_type == ABC_T_NOTE)
			y2 = y1 + 1.2 * s;
		else
			y2 = y1;
		if (k1 != k2)
			x2 = k2->x - k2->wl * .3;
	}

	if (nn >= 3) {
		if (k1->next->type != BAR
		 && k1->next->x < x1 + 48) {
			if (s > 0) {
				y = k1->next->ymx - 2;
				if (y1 < y)
					y1 = y;
			} else {
				y = k1->next->ymn + 2;
				if (y1 > y)
					y1 = y;
			}
		}
		if (k2->prev
		 && k2->prev->type != BAR
		 && k2->prev->x > x2 - 48) {
			if (s > 0) {
				y = k2->prev->ymx - 2;
				if (y2 < y)
					y2 = y;
			} else {
				y = k2->prev->ymn + 2;
				if (y2 > y)
					y2 = y;
			}
		}
	}

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

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

	/* special case for grace notes */
	if (k1->flags & ABC_F_GRACE)
		x1 = k1->x - GSTEM_XOFF * .5;
	if (k2->flags & ABC_F_GRACE)
		x2 = k2->x + GSTEM_XOFF * 1.5;

	h = 0;
	a = (y2 - y1) / (x2 - x1);
	if (k1 != k2
	 && k1->voice == k2->voice) {
	    addy = y1 - a * x1;
	    for (k = k1->next; k != k2 ; k = k->next) {
		if (k->staff != upstaff)
			continue;
		switch (k->type) {
		case NOTEREST:
			if (s > 0) {
				y = 3 * (k->pits[k->nhd] - 18) + 6;
				if (y < k->ymx)
					y = k->ymx;
				y -= a * k->x + addy;
				if (y > h)
					h = y;
			} else {
				y = 3 * (k->pits[0] - 18) - 6;
				if (y > k->ymn)
					y = k->ymn;
				y -= a * k->x + addy;
				if (y < h)
					h = y;
			}
			break;
		case GRACE: {
			struct SYMBOL *g;

			for (g = k->extra; g; g = g->next) {
#if 1
				if (g->type != NOTEREST)
					continue;
				if (s > 0) {
					y = 3 * (g->pits[g->nhd] - 18) + 6;
					if (y < g->ymx)
						y = g->ymx;
					y -= a * g->x + addy;
					if (y > h)
						h = y;
				} else {
					y = 3 * (g->pits[0] - 18) - 6;
					if (y > g->ymn)
						y = g->ymn;
					y -= a * g->x + addy;
					if (y < h)
						h = y;
				}
#else
				y = g->y - a * k->x - addy;
				if (s > 0) {
					y += GSTEM + 2;
					if (y > h)
						h = y;
				} else {
					y -= 2;
					if (y < h)
						h = y;
				}
#endif
			}
			break;
		    }
		}
	    }
	    y1 += .45 * h;
	    y2 += .45 * h;
	    h *= .65;
	}

	if (nn > 3)
		height = (.08 * (x2 - x1) + 12) * s;
	else
		height = (.03 * (x2 - x1) + 8) * s;
	if (s > 0) {
		if (height < 3 * h)
			height = 3 * h;
		if (height > 40)
			height = 40;
	} else {
		if (height > 3 * h)
			height = 3 * h;
		if (height < -40)
			height = -40;
	}

	y = y2 - y1;
	if (y < 0)
		y = -y;
	if (s > 0) {
		if (height < .8 * y)
			height = .8 * y;
	} else {
		if (height > -.8 * y)
			height = -.8 * y;
	}
	height *= cfmt.slurheight;

	slur_out(x1, y1, x2, y2, s, height, slur_type & SL_DOTTED, upstaff);

	/* have room for other symbols */
	dx = x2 - x1;
	a = (y2 - y1) / dx;
/*fixme: it seems to work with .4, but why?*/
	addy = y1 - a * x1 + .4 * height;
	if (k1->voice == k2->voice)
	    for (k = k1; k != k2; k = k->next) {
		if (k->staff != upstaff)
			continue;
		y = a * k->x + addy;
		if (k->ymx < y)
			k->ymx = y;
		else if (k->ymn > y)
			k->ymn = y;
		if (k->next == k2) {
			dx = x2;
			if (k2->sflags & S_SL1)
				dx -= 5;
		} else {
			dx = k->next->x;
		}
		if (k != k1)
			x1 = k->x;
		dx -= x1;
		y_set(upstaff, s > 0, x1, dx, y);
	}
	return (s > 0 ? SL_ABOVE : SL_BELOW) | (slur_type & SL_DOTTED);
}

/* -- draw the slurs between 2 symbols --*/
static void draw_slurs(struct SYMBOL *first,
		       struct SYMBOL *last)
{
	struct SYMBOL *s, *s1, *k, *gr1, *gr2;
	int i, m1, m2, gr1_out, slur_type, cont;

	gr1 = gr2 = NULL;
	s = first;
	for (;;) {
		if (!s || s == last) {
			if (!gr1
			 || !(s = gr1->next)
			 || s == last)
				break;
			gr1 = NULL;
		}
		if (s->type == GRACE) {
			gr1 = s;
			s = s->extra;
			continue;
		}
		if ((s->type != NOTEREST && s->type != SPACE)
		 || (s->u.note.slur_st == 0
			&& !(s->sflags & S_SL1))) {
			s = s->next;
			continue;
		}
		k = NULL;		/* find matching slur end */
		s1 = s->next;
		gr1_out = 0;
		for (;;) {
			if (!s1) {
				if (gr2) {
					s1 = gr2->next;
					gr2 = NULL;
					continue;
				}
				if (!gr1 || gr1_out)
					break;
				s1 = gr1->next;
				gr1_out = 1;
				continue;
			}
			if (s1->type == GRACE) {
				gr2 = s1;
				s1 = s1->extra;
				continue;
			}
			if (s1->type == BAR
			 && ((s1->sflags & S_RRBAR)
			  || s1->u.bar.type == B_THIN_THICK
			  || s1->u.bar.type == B_THICK_THIN
			  || (s1->u.bar.repeat_bar
			   && s1->text
			   && s1->text[0] != '1'))) {
				k = s1;
				break;
			}
			if (s1->type != NOTEREST && s1->type != SPACE) {
				s1 = s1->next;
				continue;
			}
			if (s1->u.note.slur_end
			 || (s1->sflags & S_SL2)) {
				k = s1;
				break;
			}
			if (s1->u.note.slur_st
			 || (s1->sflags & S_SL1)) {
				if (gr2) {	/* if in grace note sequence */
					for (k = s1; k->next; k = k->next)
						;
					k->next = gr2->next;
					if (gr2->next)
						gr2->next->prev = k;
//					gr2->u.note.slur_st = SL_AUTO;
					k = NULL;
				}
				draw_slurs(s1, last);
				if (gr2
				 && gr2->next) {
					gr2->next->prev->next = NULL;
					gr2->next->prev = gr2;
				}
			}
			if (s1 == last)
				break;
			s1 = s1->next;
		}
		if (!s1) {
			k = next_scut(s);
		} else if (!k) {
			s = s1;
			if (s == last)
				break;
			continue;
		}

		/* if slur in grace note sequence, change the linkages */
		if (gr1) {
			for (s1 = s; s1->next; s1 = s1->next)
				;
			s1->next = gr1->next;
			if (gr1->next)
				gr1->next->prev = s1;
			gr1->u.note.slur_st = SL_AUTO;
		}
		if (gr2) {
			gr2->prev->next = gr2->extra;
			gr2->extra->prev = gr2->prev;
			gr2->u.note.slur_st = SL_AUTO;
		}
		if (s->u.note.slur_st) {
			slur_type = s->u.note.slur_st & 0x0f;
			s->u.note.slur_st >>= 4;
			m1 = -1;
		} else {
			for (m1 = 0; m1 <= s->nhd; m1++)
				if (s->u.note.notes[m1].sl1)
					break;
			slur_type = s->u.note.notes[m1].sl1 & 0x0f;
			s->u.note.notes[m1].sl1 >>= 4;
			if (s->u.note.notes[m1].sl1 == 0) {
				for (i = m1 + 1; i <= s->nhd; i++)
					if (s->u.note.notes[i].sl1)
						break;
				if (i > s->nhd)
					s->sflags &= ~S_SL1;
			}
		}
		m2 = -1;
		cont = 0;
		if ((k->type == NOTEREST || k->type == SPACE)
		  && (k->u.note.slur_end
		   || (k->sflags & S_SL2))) {
			if (k->u.note.slur_end) {
				k->u.note.slur_end--;
			} else {
				for (m2 = 0; m2 <= k->nhd; m2++)
					if (k->u.note.notes[m2].sl2)
						break;
				k->u.note.notes[m2].sl2--;
				if (k->u.note.notes[m2].sl2 == 0) {
					for (i = m2 + 1; i <= k->nhd; i++)
						if (k->u.note.notes[i].sl2)
							break;
					if (i > k->nhd)
						k->sflags &= ~S_SL2;
				}
			}
		} else {
			if (k->type != BAR
			 || (!(k->sflags & S_RRBAR)
			  && k->u.bar.type != B_THIN_THICK
			  && k->u.bar.type != B_THICK_THIN
			  && (!k->u.bar.repeat_bar
			   || !k->text
			   || k->text[0] == '1')))
				cont = 1;
		}
		slur_type = draw_slur(s, k, m1, m2, slur_type);
		if (cont) {
/*fixme: the slur types are inverted*/
			voice_tb[k->voice].slur_st <<= 4;
			voice_tb[k->voice].slur_st += slur_type;
		}

		/* if slur in grace note sequence, restore the linkages */
		if (gr1
		 && gr1->next) {
			gr1->next->prev->next = NULL;
			gr1->next->prev = gr1;
		}
		if (gr2) {
			gr2->prev->next = gr2;
			gr2->extra->prev = NULL;
			gr2 = NULL;
		}

		if (s->u.note.slur_st
		 || (s->sflags & S_SL1))
			continue;
		if (s == last)
			break;
		s = s->next;
	}
}

/* -- draw a tuplet -- */
/* (the staves are not yet defined) */
/* See http://moinejf.free.fr/abcm2ps-doc/tuplets.xhtml
 * about the value of 'aux' */
static struct SYMBOL *draw_tuplet(struct SYMBOL *t,	/* tuplet in extra */
				  struct SYMBOL *s)	/* main note */
{
	struct SYMBOL *s1, *s2, *sy, *next, *g;
	int r, upstaff, nb_only, some_slur, dir;
	float x1, x2, y1, y2, xm, ym, a, s0, yy, yx, dy;

	next = s;
	if ((t->aux & 0xf000) == 0x1000)	/* if 'when' == never */
		goto done;

	/* treat the nested tuplets starting on this symbol */
	for (g = t->next; g; g = g->next) {
		if (g->type == TUPLET) {
			sy = draw_tuplet(g, s);
			if (sy->time > next->time)
				next = sy;
		}
	}

	/* search the first and last notes/rests of the tuplet */
	r = t->u.tuplet.r_plet;
	s1 = NULL;
	some_slur = 0;
	upstaff = s->staff;
	for (s2 = s; s2; s2 = s2->next) {
		if (s2 != s
		 && (s2->sflags & S_IN_TUPLET)) {
			for (g = s2->extra; g; g = g->next) {
				if (g->type == TUPLET) {
					sy = draw_tuplet(g, s2);
					if (sy->time > next->time)
						next = sy;
				}
			}
		}
		if (s2->type != NOTEREST) {
			if (s2->type == GRACE) {
				for (g = s2->extra; g; g = g->next) {
					if (g->type != NOTEREST)
						continue;
					if (g->u.note.slur_st
					 || (g->sflags & S_SL1))
						some_slur = 1;
				}
			}
			continue;
		}
		if (s2->u.note.slur_st		/* if slur start/end */
		 || s2->u.note.slur_end
		 || (s2->sflags & (S_SL1 | S_SL2)))
			some_slur = 1;
		if (s2->staff < upstaff)
			upstaff = s2->staff;
		if (!s1)
			s1 = s2;
		if (--r <= 0)
			break;
	}
	if (!s2)
		goto done;			/* no solution... */
	if (s2->time > next->time)
		next = s2;

	dir = t->aux & 0x000f;
	if (!dir)
		dir = s1->stem > 0 ? SL_ABOVE : SL_BELOW;

	if (s1 == s2) {				/* tuplet with 1 note (!) */
		nb_only = 1;
	} else if ((t->aux & 0x0f00) == 0x0100) {	/* 'what' == slur */
		nb_only = 1;
		draw_slur(s1, s2, -1, -1, dir);
	} else {

		/* search if a bracket is needed */
		if ((t->aux & 0xf000) == 0x2000	/* if 'when' == always */
		 || s1->abc_type != ABC_T_NOTE || s2->abc_type != ABC_T_NOTE) {
			nb_only = 0;
		} else {
			nb_only = 1;
			for (sy = s1; ; sy = sy->next) {
				if (sy->type != NOTEREST) {
					if (sy->type == GRACE
					 || sy->type == SPACE)
						continue;
					nb_only = 0;
					break;
				}
				if (sy == s2)
					break;
				if (sy->sflags & S_BEAM_END) {
					nb_only = 0;
					break;
				}
			}
			if (nb_only
			 && !(s1->sflags & (S_BEAM_ST | S_BEAM_BR1 | S_BEAM_BR2))) {
				for (sy = s1->prev; sy; sy = sy->prev) {
					if (sy->type == NOTEREST) {
						if (sy->nflags >= s1->nflags)
							nb_only = 0;
						break;
					}
				}
			}
			if (nb_only && !(s2->sflags & S_BEAM_END)) {
				for (sy = s2->next; sy; sy = sy->next) {
					if (sy->type == NOTEREST) {
						if (!(sy->sflags & (S_BEAM_BR1 | S_BEAM_BR2))
						 && sy->nflags >= s2->nflags)
							nb_only = 0;
						break;
					}
				}
			}
		}
	}

	/* if number only, draw it */
	if (nb_only) {
		float a, b;

		if ((t->aux & 0x00f0) == 0x0010) /* if 'which' == none */
			goto done;
		xm = (s2->x + s1->x) * .5;
		if (s1 == s2)			/* tuplet with 1 note */
			a = 0;
		else
			a = (s2->ys - s1->ys) / (s2->x - s1->x);
		b = s1->ys - a * s1->x;
		yy = a * xm + b;
		if (dir == SL_ABOVE) {
			ym = y_get(s1->staff, 1, xm - 3, 6);
			if (ym > yy)
				b += ym - yy;
			b += 2;
		} else {
			ym = y_get(s1->staff, 0, xm - 3, 6);
			if (ym < yy)
				b += ym - yy;
			b -= 10;
		}
		for (sy = s1; ; sy = sy->next) {
			if (sy->x >= xm)
				break;
		}
		if (s1->stem * s2->stem > 0) {
			if (s1->stem > 0)
				xm += GSTEM_XOFF;
			else
				xm -= GSTEM_XOFF;
		}
		ym = a * xm + b;
		if ((t->aux & 0x00f0) == 0)	/* if 'which' == number */
			a2b("(%d)", t->u.tuplet.p_plet);
		else
			a2b("(%d:%d)", t->u.tuplet.p_plet, t->u.tuplet.q_plet);
		putxy(xm, ym);
		a2b("y%d bnum\n", s1->staff);

		if (dir == SL_ABOVE) {
			ym += 8;
			if (sy->ymx < ym)
				sy->ymx = (short) ym;
			y_set(s1->staff, 1, xm - 3, 6, ym);
		} else {
			if (sy->ymn > ym)
				sy->ymn = (short) ym;
			y_set(s1->staff, 0, xm - 3, 6, ym);
		}
		goto done;
	}

	/* draw the slurs when inside the tuplet */
	if (some_slur) {
		draw_slurs(s1, s2);
		if (s1->u.note.slur_st
		 || (s1->sflags & S_SL1))
			return next;
		for (sy = s1->next; sy != s2; sy = sy->next) {
			if (sy->u.note.slur_st		/* if slur start/end */
			 || sy->u.note.slur_end
			 || (sy->sflags & (S_SL1 | S_SL2)))
				return next;		/* don't draw now */
		}

		/* don't draw the tuplet when a slur ends on the last note */
		if (s2->u.note.slur_end
		 || (s2->sflags & S_SL2))
			return next;
	}

	if ((t->aux & 0x0f00) != 0)		/* if 'what' != square */
		fprintf(stderr, "'what' value of %%%%tuplets not yet coded\n");

/*fixme: two staves not treated*/
/*fixme: to optimize*/
	dir = t->aux & 0x000f;			/* 'where' */
	if (!dir)
		dir = s1->multi >= 0 ? SL_ABOVE : SL_BELOW;
    if (dir == SL_ABOVE) {

	/* sole or upper voice: the bracket is above the staff */
	x1 = s1->x - 4;
	y1 = 24;
	if (s1->staff == upstaff) {
		sy = s1;
		if (sy->abc_type != ABC_T_NOTE) {
			for (sy = sy->next; sy != s2; sy = sy->next)
				if (sy->abc_type == ABC_T_NOTE)
					break;
		}
		ym = y_get(upstaff, 1, sy->x, 0);
		if (ym > y1)
			y1 = ym;
		if (s1->stem > 0)
			x1 += 3;
	}
	y2 = 24;
	if (s2->staff == upstaff) {
		sy = s2;
		if (sy->abc_type != ABC_T_NOTE) {
			for (sy = sy->prev; sy != s1; sy = sy->prev)
				if (sy->abc_type == ABC_T_NOTE)
					break;
		}
		ym = y_get(upstaff, 1, sy->x, 0);
		if (ym > y2)
			y2 = ym;
	}

	/* end the backet according to the last note duration */
	if (s2->dur > s2->prev->dur) {
		if (s2->next)
			x2 = s2->next->x - s2->next->wl - 5;
		else
			x2 = realwidth - 6;
	} else {
		x2 = s2->x + 4;
		r = s2->stem >= 0 ? 0 : s2->nhd;
		if (s2->u.note.notes[r].shhd > 0)
			x2 += s2->u.note.notes[r].shhd;
		if (s2->staff == upstaff
		 && s2->stem > 0)
			x2 += 3.5;
	}

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

	a = (y2 - y1) / (x2 - x1);
	s0 = 3 * (s2->pits[s2->nhd] -
			s1->pits[s1->nhd]) / (x2 - x1);
	if (s0 > 0) {
		if (a < 0)
			a = 0;
		else if (a > s0)
			a = s0;
	} else {
		if (a > 0)
			a = 0;
		else if (a < s0)
			a = s0;
	}
	if (a * a < .1 * .1)
		a = 0;

	/* shift up bracket if needed */
	dy = 0;
	for (sy = s1; ; sy = sy->next) {
		if (sy->dur == 0	/* not a note or a rest */
		 || sy->staff != upstaff) {
			if (sy == s2)
				break;
			continue;
		}
		yy = ym + (sy->x - xm) * a;
		yx = y_get(upstaff, 1, sy->x, 0);
		if (yx - yy > dy)
			dy = yx - yy;
		if (sy == s2)
			break;
	}

	ym += dy;
	y1 = ym + a * (x1 - xm);
	y2 = ym + a * (x2 - xm);
	putxy(x2 - x1, y2 - y1);
	putxy(x1, y1 + 4);
	a2b("y%d tubr", upstaff);

	/* shift the slurs / decorations */
	ym += 8;
	for (sy = s1; ; sy = sy->next) {
		if (sy->staff == upstaff) {
			yy = ym + (sy->x - xm) * a;
			if (sy->ymx < yy)
				sy->ymx = yy;
			if (sy == s2)
				break;
			y_set(upstaff, 1, sy->x, sy->next->x - sy->x, yy);
		} else if (sy == s2) {
			break;
		}
	}

    } else {	/* lower voice of the staff: the bracket is below the staff */
/*fixme: think to all that again..*/
	x1 = s1->x - 7;

	if (s2->dur > s2->prev->dur) {
		sy = s2->next;
		if (!sy			// maybe a note in an overlay voice
		 || sy->time != s2->time + s2->dur) {
			for (sy = s2->ts_next; sy; sy = sy->ts_next) {
				if ((sy->sflags & S_SEQST)
				 && sy->time >= s2->time + s2->dur)
					break;
			}
		}
		x2 = sy ? sy->x - sy->wl - 5 : realwidth - 6;
	} else {
		x2 = s2->x + 2;
		if (s2->u.note.notes[s2->nhd].shhd > 0)
			x2 += s2->u.note.notes[s2->nhd].shhd;
	}

	if (s1->staff == upstaff) {
		sy = s1;
		if (sy->abc_type != ABC_T_NOTE) {
			for (sy = sy->next; sy != s2; sy = sy->next)
				if (sy->abc_type == ABC_T_NOTE)
					break;
		}
		y1 = y_get(upstaff, 0, sy->x, 0);
	} else {
		y1 = 0;
	}
	if (s2->staff == upstaff) {
		sy = s2;
		if (sy->abc_type != ABC_T_NOTE) {
			for (sy = sy->prev; sy != s1; sy = sy->prev)
				if (sy->abc_type == ABC_T_NOTE)
					break;
		}
		y2 = y_get(upstaff, 0, sy->x, 0);
	} else {
		y2 = 0;
	}

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

	a = (y2 - y1) / (x2 - x1);
	s0 = 3 * (s2->pits[0] - s1->pits[0]) / (x2 - x1);
	if (s0 > 0) {
		if (a < 0)
			a = 0;
		else if (a > s0)
			a = s0;
	} else {
		if (a > 0)
			a = 0;
		else if (a < s0)
			a = s0;
	}
	if (a * a < .1 * .1)
		a = 0;

	/* shift down bracket if needed */
	dy = 0;
	for (sy = s1; ; sy = sy->next) {
		if (sy->dur == 0	/* not a note nor a rest */
		 || sy->staff != upstaff) {
			if (sy == s2)
				break;
			continue;
		}
		yy = ym + (sy->x - xm) * a;
		yx = y_get(upstaff, 0, sy->x, 0);
		if (yx - yy < dy)
			dy = yx - yy;
		if (sy == s2)
			break;
	}

	ym += dy - 10;
	y1 = ym + a * (x1 - xm);
	y2 = ym + a * (x2 - xm);
	putxy(x2 - x1, y2 - y1);
	putxy(x1, y1 + 4);
	a2b("y%d tubrl",upstaff);

	/* shift the slurs / decorations */
	ym -= 2;
	for (sy = s1; ; sy = sy->next) {
		if (sy->staff == upstaff) {
			if (sy == s2)
				break;
			yy = ym + (sy->x - xm) * a;
			if (sy->ymn > yy)
				sy->ymn = (short) yy;
			y_set(upstaff, 0, sy->x, sy->next->x - sy->x, yy);
		}
		if (sy == s2)
			break;
	}
    } /* lower voice */

	if ((t->aux & 0x00f0) == 0x10) {	/* if 'which' == none */
		a2b("\n");
		goto done;
	}
	yy = .5 * (y1 + y2);
	if ((t->aux & 0x00f0) == 0)		/* if 'which' == number */
		a2b("(%d)", t->u.tuplet.p_plet);
	else
		a2b("(%d:%d)", t->u.tuplet.p_plet, t->u.tuplet.q_plet);
	putxy(xm, yy);
	a2b("y%d bnumb\n", upstaff);

	// let some room for the number
	if (dir == SL_ABOVE)
		y_set(upstaff, 1, xm - 3, 6, ym + 3);
	else
		y_set(upstaff, 0, xm - 3, 6, ym);

done:
	s->sflags &= ~S_IN_TUPLET;
	return next;
}

/* -- draw the ties between two notes/chords -- */
static void draw_note_ties(struct SYMBOL *k1,
			   struct SYMBOL *k2,
			   int ntie,
			   unsigned char *mhead1,
			   unsigned char *mhead2,
			   int job)
{
	int i, s, m1, m2, p, p1, p2, y, staff;
	float x1, x2, h, sh;

	for (i = 0; i < ntie; i++) {
		m1 = mhead1[i];
		p1 = k1->pits[m1];
		m2 = mhead2[i];
		p2 = job != 2 ? k2->pits[m2] : p1;
		s = (k1->u.note.notes[m1].ti1 & 0x07) == SL_ABOVE ? 1 : -1;

		x1 = k1->x;
		sh = k1->u.note.notes[m1].shhd;		/* head shift */
		if (s > 0) {
			if (m1 < k1->nhd && p1 + 1 == k1->pits[m1 + 1])
				if (k1->u.note.notes[m1 + 1].shhd > sh)
					sh = k1->u.note.notes[m1 + 1].shhd;
		} else {
			if (m1 > 0 && p1 == k1->pits[m1 - 1] + 1)
				if (k1->u.note.notes[m1 - 1].shhd > sh)
					sh = k1->u.note.notes[m1 - 1].shhd;
		}
//		x1 += sh;
		x1 += sh * 0.6;

		x2 = k2->x;
		if (job != 2) {
			sh = k2->u.note.notes[m2].shhd;
			if (s > 0) {
				if (m2 < k2->nhd && p2 + 1 == k2->pits[m2 + 1])
					if (k2->u.note.notes[m2 + 1].shhd < sh)
						sh = k2->u.note.notes[m2 + 1].shhd;
			} else {
				if (m2 > 0 && p2 == k2->pits[m2 - 1] + 1)
					if (k2->u.note.notes[m2 - 1].shhd < sh)
						sh = k2->u.note.notes[m2 - 1].shhd;
			}
			x2 += sh * 0.6;
		}

		staff = k1->staff;
		switch (job) {
		case 0:
			if (p1 == p2 || (p1 & 1))
				p = p1;
			else
				p = p2;
			break;
		case 3:				/* clef or staff change */
			s = -s;
			// fall thru
		case 1:				/* no starting note */
			x1 = k1->x;
			if (x1 > x2 - 20)
				x1 = x2 - 20;
			p = p2;
			staff = k2->staff;
			break;
/*		case 2:				 * no ending note */
		default:
			if (k1 != k2) {
				if (k2->type == BAR)
					x2 -= 2;
				else
					x2 -= k2->wl;
			} else {
				struct SYMBOL *k;
				int time;

				time = k1->time + k1->dur;
				for (k = k1->ts_next; k; k = k->ts_next)
					if (k->time > time)
						break;
				if (!k)
					x2 = realwidth;
				else
					x2 = k->x;
			}
			if (x2 < x1 + 16)
				x2 = x1 + 16;
			p = p1;
			break;
		}
		if (x2 - x1 > 20) {
			x1 += 3.5;
			x2 -= 3.5;
		} else {
			x1 += 1.5;
			x2 -= 1.5;
		}

		if (k1->dots && !(p1 & 1)
		 && ((s > 0 && k1->doty >= 0)
		  || (s < 0 && k1->doty < 0)))
			x1 += 6;		// avoid clash with dots

		y = 3 * (p - 18);
//fixme: clash when 2 ties on second interval chord
//		if (p & 1)
//			y += 2 * s;
#if 0
		if (job != 1 && job != 3) {
			if (s > 0) {
//				if (k1->nflags > -2 && k1->stem > 0
//				 && k1->nhd == 0)
//					x1 += 4.5;
				if (!(p & 1) && k1->dots > 0)
					y = 3 * (p - 18) + 6;
			}
//		} else {
//			if (s < 0) {
//				if (k2->nflags > -2 && k2->stem < 0
//				 && k2->nhd == 0)
//					x2 -= 4.5;
//			}
		}
#endif

		h = (.04 * (x2 - x1) + 10) * s;
		h *= cfmt.tieheight;
		slur_out(x1, staff_tb[staff].y + y,
			 x2, staff_tb[staff].y + y,
			 s, h, k1->u.note.notes[m1].ti1 & SL_DOTTED, -1);
	}
}

/* -- draw ties between neighboring notes/chords -- */
static void draw_ties(struct SYMBOL *k1,
		      struct SYMBOL *k2,
		      int job)		/* 0: normal
					 * 1: no starting note
					 * 2: no ending note
					 * 3: no start for clef or staff change */
{
	struct SYMBOL *k3;
	int i, m1, nh1, pit, ntie, tie2, ntie3, time;
	unsigned char mhead1[MAXHD], mhead2[MAXHD], mhead3[MAXHD];

	time = k1->time + k1->dur;
	ntie = ntie3 = 0;
	nh1 = k1->nhd;

	/* half ties from last note in line or before new repeat */
	if (job == 2) {
		for (i = 0; i <= nh1; i++) {
			if (k1->u.note.notes[i].ti1)
				mhead3[ntie3++] = i;
		}
		draw_note_ties(k1, k2 ? k2 : k1, ntie3, mhead3, mhead3, job);
		return;
	}

	/* set up list of ties to draw */
	for (i = 0; i <= nh1; i++) {
		if (k1->u.note.notes[i].ti1 == 0)
			continue;
		tie2 = -1;
		pit = k1->u.note.notes[i].pit;
		for (m1 = k2->nhd; m1 >= 0; m1--) {
			switch (k2->u.note.notes[m1].pit - pit) {
			case 1:			/* maybe ^c - _d */
			case -1:		/* _d - ^c */
				if (k1->u.note.notes[i].acc != k2->u.note.notes[m1].acc)
					tie2 = m1;
			default:
				continue;
			case 0:
				tie2 = m1;
				break;
			}
			break;
		}
		if (tie2 >= 0) {		/* 1st or 2nd choice */
			mhead1[ntie] = i;
			mhead2[ntie++] = tie2;
		} else {
			mhead3[ntie3++] = i;	/* no match */
		}
	}

	/* draw the ties */
	draw_note_ties(k1, k2, ntie, mhead1, mhead2, job);

	/* if any bad tie, try an other voice of the same staff */
	if (ntie3 == 0)
		return;				/* no bad tie */
	k3 = k1->ts_next;
	while (k3 && k3->time < time)
		k3 = k3->ts_next;
	while (k3 && k3->time == time) {
		if (k3->abc_type != ABC_T_NOTE
		 || k3->staff != k1->staff) {
			k3 = k3->ts_next;
			continue;
		}
		ntie = 0;
		for (i = ntie3; --i >= 0; ) {
			pit = k1->u.note.notes[mhead3[i]].pit;
			for (m1 = k3->nhd; m1 >= 0; m1--) {
				if (k3->u.note.notes[m1].pit == pit) {
					mhead1[ntie] = mhead3[i];
					mhead2[ntie++] = m1;
					ntie3--;
//					mhead3[i] = mhead3[--ntie3];
					break;
				}
			}
		}
		if (ntie > 0) {
			draw_note_ties(k1, k3,
					ntie, mhead1, mhead2,
					job == 1 ? 1 : 0);
			if (ntie3 == 0)
				return;
		}
		k3 = k3->ts_next;
	}

//	if (ntie3 != 0)
		error(1, k1, "Bad tie");
}

static void draw_ties_g(struct SYMBOL *s1,
			struct SYMBOL *s2,
			int job)
{
	struct SYMBOL *g;

	if (s1->type == GRACE) {
		for (g = s1->extra; g; g = g->next) {
			if (g->sflags & S_TI1)
				draw_ties(g, s2, job);
		}
	} else {
		draw_ties(s1, s2, job);
	}
}

/* -- try to get the symbol of a ending tie when combined voices -- */
static struct SYMBOL *tie_comb(struct SYMBOL *s)
{
	struct SYMBOL *s1;
	int time, st;

	time = s->time + s->dur;
	st = s->staff;
	for (s1 = s->ts_next; s1; s1 = s1->ts_next) {
		if (s1->staff != st)
			continue;
		if (s1->time == time) {
			if (s1->abc_type == ABC_T_NOTE)
				return s1;
			continue;
		}
		if (s1->time > time)
			return s;		// bad tie
	}
	return NULL;				// no ending tie
}

/* -- draw all ties between neighboring notes -- */
static void draw_all_ties(struct VOICE_S *p_voice)
{
	struct SYMBOL *s1, *s2, *s3, *s4, *rtie;
	struct SYMBOL tie;
	int clef_chg, time;

	for (s1 = p_voice->sym; s1; s1 = s1->next) {
		switch (s1->type) {
		case CLEF:
		case KEYSIG:
		case TIMESIG:
			continue;
		}
		break;
	}
	rtie = p_voice->rtie;			/* tie from 1st repeat bar */
	for (s2 = s1; s2; s2 = s2->next) {
//		if (s2->abc_type == ABC_T_NOTE
		if (s2->dur
		 || s2->type == GRACE)
			break;
		if (s2->type != BAR
		 || !s2->u.bar.repeat_bar
		 || !s2->text)
			continue;
		if (s2->text[0] == '1')	/* 1st repeat bar */
			rtie = p_voice->tie;
		else
			p_voice->tie = rtie;
	}
	if (!s2)
		return;
	if (p_voice->tie) {			/* tie from previous line */
		p_voice->tie->x = s1->x + s1->wr;
		s1 = p_voice->tie;
		p_voice->tie = NULL;
		s1->staff = s2->staff;
		s1->ts_next = s2->ts_next;	/* (for tie to other voice) */
		s1->time = s2->time - s1->dur;	/* (if after repeat sequence) */
		draw_ties(s1, s2, 1);		/* tie to 1st note */
	}

	/* search the start of ties */
	clef_chg = 0;
	for (;;) {
		for (s1 = s2; s1; s1 = s1->next) {
			if (s1->sflags & S_TI1)
				break;
			if (!rtie)
				continue;
			if (s1->type != BAR
			 || !s1->u.bar.repeat_bar
			 || !s1->text)
				continue;
			if (s1->text[0] == '1') {	/* 1st repeat bar */
				rtie = NULL;
				continue;
			}
			if (s1->u.bar.type == B_BAR)
				continue;		// not a repeat
			for (s2 = s1->next; s2; s2 = s2->next)
				if (s2->abc_type == ABC_T_NOTE)
					break;
			if (!s2) {
				s1 = NULL;
				break;
			}
			memcpy(&tie, rtie, sizeof tie);
//			tie.x = s1->x + s1->wr;
			tie.x = s1->x;
			tie.next = s2;
			tie.staff = s2->staff;
			tie.time = s2->time - tie.dur;
			draw_ties(&tie, s2, 1);
		}
		if (!s1)
			break;

		/* search the end of the tie
		 * and notice the clef changes (may occur in an other voice) */
		time = s1->time + s1->dur;
		for (s2 = s1->next; s2; s2 = s2->next) {
			if (s2->dur != 0)
				break;
			if (s2->type == BAR
			 && s2->u.bar.repeat_bar
			 && s2->text) {
				if (s2->text[0] != '1')
					break;
				rtie = s1;	/* 1st repeat bar */
			}
		}
		if (!s2) {
			for (s4 = s1->ts_next; s4; s4 = s4->ts_next) {
				if (s4->staff != s1->staff)
					continue;
				if (s4->time < time)
					continue;
				if (s4->time > time) {
					s4 = NULL;
					break;
				}
				if (s4->dur != 0)
					break;
			}
			if (!s4) {
				draw_ties_g(s1, NULL, 2);
				p_voice->tie = s1;
				break;
			}
		} else {
			if (s2->abc_type != ABC_T_NOTE
			 && s2->abc_type != ABC_T_BAR) {
				error(1, s1, "Bad tie");
				continue;
			}
			if (s2->time == time) {
				s4 = s2;
			} else {
				s4 = tie_comb(s1);
				if (s4 == s1) {
					error(1, s1, "Bad tie");
					continue;
				}
			}
		}
		for (s3 = s1->ts_next; s3; s3 = s3->ts_next) {
			if (s3->staff != s1->staff)
				continue;
			if (s3->time > time)
				break;
			if (s3->type == CLEF) {
				clef_chg = 1;
				continue;
			}
#if 0
			if (s3->type == BAR) {
//				if ((s3->sflags & S_RRBAR)
//				 || s3->u.bar.type == B_THIN_THICK
//				 || s3->u.bar.type == B_THICK_THIN) {
//					s4 = s3;
//					break;
//				}
				if (!s3->u.bar.repeat_bar
				 || !s3->text)
					continue;
				if (s3->text[0] != '1') {
					s4 = s3;
					break;
				}
				rtie = s1;		/* 1st repeat bar */
			}
#endif
		}

		/* ties with clef or staff change */
		if (clef_chg || s1->staff != s4->staff) {
			float x, dx;

			clef_chg = 0;
			dx = (s4->x - s1->x) * 0.4;
			x = s4->x;
			s4->x -= dx;
			if (s4->x > s1->x + 32.)
				s4->x = s1->x + 32.;
			draw_ties_g(s1, s4, 2);
			s4->x = x;
			x = s1->x;
			s1->x += dx;
			if (s1->x < s4->x - 24.)
				s1->x = s4->x - 24.;
			draw_ties(s1, s4, 3);
			s1->x = x;
			continue;
		}
		draw_ties_g(s1, s4, s4->abc_type == ABC_T_NOTE ? 0 : 2);
	}
	p_voice->rtie = rtie;
}

/* -- draw all phrasing slurs for one staff -- */
/* (the staves are not yet defined) */
static void draw_all_slurs(struct VOICE_S *p_voice)
{
	struct SYMBOL *s, *k;
	int i, m2, slur_type;
	unsigned char slur_st;

	s = p_voice->sym;
	if (!s)
		return;
	slur_type = p_voice->slur_st;
	p_voice->slur_st = 0;

	/* the starting slur types are inverted */
	slur_st = 0;
	while (slur_type != 0) {
		slur_st <<= 4;
		slur_st |= (slur_type & 0x0f);
		slur_type >>= 4;
	}

	/* draw the slurs inside the music line */
	draw_slurs(s, NULL);

	/* do unbalanced slurs still left over */
	for ( ; s; s = s->next) {
		if (s->type != NOTEREST && s->type != SPACE)
			continue;
		while (s->u.note.slur_end
		    || (s->sflags & S_SL2)) {
			if (s->u.note.slur_end) {
				s->u.note.slur_end--;
				m2 = -1;
			} else {
				for (m2 = 0; m2 <= s->nhd; m2++)
					if (s->u.note.notes[m2].sl2)
						break;
				s->u.note.notes[m2].sl2--;
				if (s->u.note.notes[m2].sl2 == 0) {
					for (i = m2 + 1; i <= s->nhd; i++)
						if (s->u.note.notes[i].sl2)
							break;
					if (i > s->nhd)
						s->sflags &= ~S_SL2;
				}
			}
			slur_type = slur_st & 0x0f;
			k = prev_scut(s);
			draw_slur(k, s, -1, m2, slur_type);
			if (k->type != BAR
			 || (!(k->sflags & S_RRBAR)
			  && k->u.bar.type != B_THIN_THICK
			  && k->u.bar.type != B_THICK_THIN
			  && (!k->u.bar.repeat_bar
			   || !k->text
			   || k->text[0] == '1')))
				slur_st >>= 4;
		}
	}
	s = p_voice->sym;
	while (slur_st != 0) {
		slur_type = slur_st & 0x0f;
		slur_st >>= 4;
		k = next_scut(s);
		draw_slur(s, k, -1, -1, slur_type);
		if (k->type != BAR
		 || (!(k->sflags & S_RRBAR)
		  && k->u.bar.type != B_THIN_THICK
		  && k->u.bar.type != B_THICK_THIN
		  && (!k->u.bar.repeat_bar
		   || !k->text
		   || k->text[0] == '1'))) {
/*fixme: the slur types are inverted again*/
			p_voice->slur_st <<= 4;
			p_voice->slur_st += slur_type;
		}
	}
}

/* -- work out accidentals to be applied to each note -- */
static void setmap(int sf,	/* number of sharps/flats in key sig (-7 to +7) */
		   unsigned char *map)	/* for 7 notes only */
{
	int j;

	for (j = 7; --j >= 0; )
		map[j] = A_NULL;
	switch (sf) {
	case 7: map[6] = A_SH;
	case 6: map[2] = A_SH;
	case 5: map[5] = A_SH;
	case 4: map[1] = A_SH;
	case 3: map[4] = A_SH;
	case 2: map[0] = A_SH;
	case 1: map[3] = A_SH;
		break;
	case -7: map[3] = A_FT;
	case -6: map[0] = A_FT;
	case -5: map[4] = A_FT;
	case -4: map[1] = A_FT;
	case -3: map[5] = A_FT;
	case -2: map[2] = A_FT;
	case -1: map[6] = A_FT;
		break;
	}
}

/* output a tablature string escaping the parenthesis */
static void tbl_out(char *s, float x, int j, char *f)
{
	char *p;

	a2b("(");
	p = s;
	for (;;) {
		while (*p != '\0' && *p != '(' && *p != ')' )
			p++;
		if (p != s) {
			a2b("%.*s", (int) (p - s), s);
			s = p;
		}
		if (*p == '\0')
			break;
		a2b("\\");
		p++;
	}
	a2b(")%.1f y %d %s ", x, j, f);
}

/* -- draw the tablature with w: -- */
static void draw_tblt_w(struct VOICE_S *p_voice,
			int nly,
			float y,
			struct tblt_s *tblt)
{
	struct SYMBOL *s;
	struct lyrics *ly;
	struct lyl *lyl;
	char *p;
	int j, l;

	a2b("/y{%.1f yns%d}def ", y, p_voice->staff);
	set_font(VOCALFONT);
	a2b("%.1f 0 y %d %s\n", realwidth, nly, tblt->head);
	for (j = 0; j < nly ; j++) {
		for (s = p_voice->sym; s; s = s->next) {
			ly = s->ly;
			if (!ly
			 || (lyl = ly->lyl[j]) == NULL) {
				if (s->type == BAR) {
					if (!tblt->bar)
						continue;
					p = &tex_buf[16];
					*p-- = '\0';
					l = bar_cnv(s->u.bar.type);
					while (l != 0) {
						*p-- = "?|[]:???"[l & 0x07];
						l >>= 4;
					}
					p++;
					tbl_out(p, s->x, j, tblt->bar);
				}
				continue;
			}
			tbl_out(lyl->t, s->x, j, tblt->note);
		}
		a2b("\n");
	}
}

/* -- draw the tablature with automatic pitch -- */
static void draw_tblt_p(struct VOICE_S *p_voice,
			float y,
			struct tblt_s *tblt)
{
	struct SYMBOL *s;
	int j, pitch, octave, sf, tied, acc;
	unsigned char workmap[70];	/* sharps/flats - base: lowest 'C' */
	unsigned char basemap[7];
	static int scale[7] = {0, 2, 4, 5, 7, 9, 11};	/* index = natural note */
	static int acc_pitch[6] = {0, 1, 0, -1, 2, -2};	/* index = enum accidentals */

	sf = p_voice->key.sf;
	setmap(sf, basemap);
	for (j = 0; j < 10; j++)
		memcpy(&workmap[7 * j], basemap, 7);
	a2b("gsave 0 %.1f yns%d T(%.2s)%s\n", y, p_voice->staff,
		tblt->instr, tblt->head);
	tied = 0;
	for (s = p_voice->sym; s; s = s->next) {
		switch (s->type) {
		case NOTEREST:
			if (s->abc_type == ABC_T_REST)
				continue;
			if (tied) {
				tied = s->u.note.notes[0].ti1;
				continue;
			}
			break;
		case KEYSIG:
			sf = s->u.key.sf;
			setmap(sf, basemap);
			for (j = 0; j < 10; j++)
				memcpy(&workmap[7 * j], basemap, 7);
			continue;
		case BAR:
			if (s->flags & ABC_F_INVIS)
				continue;
			for (j = 0; j < 10; j++)
				memcpy(&workmap[7 * j], basemap, 7);
			continue;
		default:
			continue;
		}
		pitch = s->u.note.notes[0].pit + 19;
		acc = s->u.note.notes[0].acc;
		if (acc != 0) {
			workmap[pitch] = acc == A_NT
				? A_NULL
				: (acc & 0x07);
		}
		pitch = scale[pitch % 7]
			+ acc_pitch[workmap[pitch]]
			+ 12 * (pitch / 7)
			- tblt->pitch;
		octave = 0;
		while (pitch < 0) {
			pitch += 12;
			octave--;
		}
		while (pitch >= 36) {
			pitch -= 12;
			octave++;
		}
		if ((acc & 0xf8) == 0) {
			a2b("%d %d %.2f %s\n", octave, pitch, s->x, tblt->note);
		} else {
			int n, d;
			float micro_p;

			n = parse.micro_tb[acc >> 3];
			d = (n & 0xff) + 1;
			n = (n >> 8) + 1;
			switch (acc & 0x07) {
			case A_FT:
			case A_DF:
				n = -n;
				break;
			}
			micro_p = (float) pitch + (float) n / d;
			a2b("%d %.3f %.2f %s\n", octave, micro_p, s->x, tblt->note);
		}
		tied = s->u.note.notes[0].ti1;
	}
	a2b("grestore\n");
}

/* -- draw the lyrics under (or above) notes -- */
/* !! this routine is tied to set_width() !! */
static void draw_lyric_line(struct VOICE_S *p_voice,
			    int j)
{
	struct SYMBOL *s;
	struct lyrics *ly;
	struct lyl *lyl;
	int hyflag, l, lflag;
	int ft, curft, defft;
	char *p;
	float lastx, w;
	float x0, shift;

	hyflag = lflag = 0;
	if (p_voice->hy_st & (1 << j)) {
		hyflag = 1;
		p_voice->hy_st &= ~(1 << j);
	}
	for (s = p_voice->sym; /*s*/; s = s->next)
		if (s->type != CLEF
		 && s->type != KEYSIG && s->type != TIMESIG)
			break;
	if (s->prev)
		lastx = s->prev->x;
	else
		lastx = tsfirst->x;
	x0 = 0;
	for ( ; s; s = s->next) {
		ly = s->ly;
		if (!ly
		 || (lyl = ly->lyl[j]) == NULL) {
			switch (s->type) {
			case NOTEREST:
				if (s->abc_type == ABC_T_NOTE)
					break;
				/* fall thru */
			case MREST:
				if (lflag) {
					putx(x0 - lastx);
					putx(lastx + 3);
					a2b("y wln ");
					lflag = 0;
					lastx = s->x + s->wr;
				}
			}
			continue;
		}
#if 1
		ft = lyl->f - cfmt.font_tb;
		get_str_font(&curft, &defft);
		if (ft != curft) {
			set_str_font(ft, defft);
		}
#else
		if (lyl->f != f) {		/* font change */
			f = lyl->f;
			str_font(f - cfmt.font_tb);
			if (lskip < f->size * 1.1)
				lskip = f->size * 1.1;
		}
#endif
		p = lyl->t;
		w = lyl->w;
		shift = lyl->s;
		if (hyflag) {
			if (*p == LY_UNDER) {		/* '_' */
				*p = LY_HYPH;
			} else if (*p != LY_HYPH) {	/* not '-' */
				putx(s->x - shift - lastx);
				putx(lastx);
				a2b("y hyph ");
				hyflag = 0;
				lastx = s->x + s->wr;
			}
		}
		if (lflag
		 && *p != LY_UNDER) {		/* not '_' */
			putx(x0 - lastx + 3);
			putx(lastx + 3);
			a2b("y wln ");
			lflag = 0;
			lastx = s->x + s->wr;
		}
		if (*p == LY_HYPH		/* '-' */
		 || *p == LY_UNDER) {		/* '_' */
			if (x0 == 0 && lastx > s->x - 18)
				lastx = s->x - 18;
			if (*p == LY_HYPH)
				hyflag = 1;
			else
				lflag = 1;
			x0 = s->x - shift;
			continue;
		}
		x0 = s->x - shift;
		l = strlen(p) - 1;
		if (p[l] == LY_HYPH) {		/* '-' at end */
			p[l] = '\0';
			hyflag = 1;
		}
		putx(x0);
		a2b("y M ");
		put_str(p, A_LYRIC);
		lastx = x0 + w;
	}
	if (hyflag) {
		x0 = realwidth - 10;
		if (x0 < lastx + 10)
			x0 = lastx + 10;
		putx(x0 - lastx);
		putx(lastx);
		a2b("y hyph ");
		if (cfmt.hyphencont)
			p_voice->hy_st |= (1 << j);
	}

	/* see if any underscore in the next line */
	for (s = tsnext; s; s = s->ts_next)
		if (s->voice == p_voice - voice_tb)
			break;
	for ( ; s; s = s->next) {
		if (s->abc_type == ABC_T_NOTE) {
			if (s->ly && s->ly->lyl[j]
			 && s->ly->lyl[j]->t[0] == LY_UNDER) {
				lflag = 1;
				x0 = realwidth - 15;
				if (x0 < lastx + 12)
					x0 = lastx + 12;
			}
			break;
		}
	}
	if (lflag) {
		putx(x0 - lastx + 3);
		putx(lastx + 3);
		a2b("y wln");
	}
	a2b("\n");
}

static float draw_lyrics(struct VOICE_S *p_voice,
			 int nly,
			 float *h,
			 float y,
			 int incr)	/* 1: below, -1: above */
{
	int j, top;
	float sc;

	/* check if the lyrics contain tablatures */
	if (p_voice->tblts[0]) {
		if (p_voice->tblts[0]->pitch == 0)
			return y;		/* yes */
		if (p_voice->tblts[1]
		 && p_voice->tblts[1]->pitch == 0)
			return y;		/* yes */
	}

	str_font(VOCALFONT);
	outft = -1;				/* force font output */
	sc = staff_tb[p_voice->staff].staffscale;

	/* under the staff */
	if (incr > 0) {
		if (y > -cfmt.vocalspace)
			y = -cfmt.vocalspace;
		y += h[0] / 6;			// descent
		y *= sc;
		for (j = 0; j < nly; j++) {
			y -= h[j] * 1.1;
			a2b("/y{%.1f yns%d}! ", y, p_voice->staff);
			draw_lyric_line(p_voice, j);
		}
		return (y - h[j - 1] / 6) / sc;
	}

	/* above the staff */
	top = staff_tb[p_voice->staff].topbar + cfmt.vocalspace;
	if (y < top)
		y = top;
	y += h[nly - 1] / 6;			// descent
	y *= sc;
	for (j = nly; --j >= 0; ) {
		a2b("/y{%.1f yns%d}! ", y, p_voice->staff);
		draw_lyric_line(p_voice, j);
		y += h[j] * 1.1;
	}
	return y / sc;
}

/* -- draw all the lyrics and the tablatures -- */
/* (the staves are not yet defined) */
static void draw_all_lyrics(void)
{
	struct VOICE_S *p_voice;
	struct SYMBOL *s;
	int staff, voice, nly, i;
	struct {
		short a, b;
		float top, bot;
	} lyst_tb[MAXSTAFF];
	struct {
		int nly;
		float h[MAXLY];
	} lyvo_tb[MAXVOICE];
	char above_tb[MAXVOICE];
	char rv_tb[MAXVOICE];
	float top, bot, y, sc;

	/* check if any lyric */
	for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
		if (p_voice->have_ly
		 || p_voice->tblts[0])
			break;
	}
	if (!p_voice)
		return;

	/* compute the number of lyrics per voice - staff
	 * and their y offset on the staff */
	memset(above_tb, 0, sizeof above_tb);
	memset(lyvo_tb, 0, sizeof lyvo_tb);
	memset(lyst_tb, 0, sizeof lyst_tb);
	staff = -1;
	top = bot = 0;
	for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
		if (!p_voice->sym)
			continue;
		voice = p_voice - voice_tb;
		if (p_voice->staff != staff) {
			top = 0;
			bot = 0;
			staff = p_voice->staff;
		}
		nly = -1;
		if (p_voice->have_ly) {
			for (s = p_voice->sym; s; s = s->next) {
				struct lyrics *ly;
				float x, w;

				ly = s->ly;
				if (!ly)
					continue;
/*fixme:should get the real width*/
				x = s->x;
				if (ly->lyl[0]) {
					x -= ly->lyl[0]->s;
					w = ly->lyl[0]->w;
				} else {
					w = 10;
				}
				y = y_get(p_voice->staff, 1, x, w);
				if (top < y)
					top = y;
				y = y_get(p_voice->staff, 0, x, w);
				if (bot > y)
					bot = y;
				for (i = 0; i < MAXLY; i++) {
					if (!ly->lyl[i])
						continue;
					if (i > nly)
						nly = i;
					if (lyvo_tb[voice].h[i] < ly->lyl[i]->f->size)
						lyvo_tb[voice].h[i] =
							ly->lyl[i]->f->size;
				}
			}
		} else {
			y = y_get(p_voice->staff, 1, 0, realwidth);
			if (top < y)
				top = y;
			y = y_get(p_voice->staff, 0, 0, realwidth);
			if (bot > y)
				bot = y;
		}
		lyst_tb[staff].top = top;
		lyst_tb[staff].bot = bot;
		if (nly < 0)
			continue;
		nly++;
		lyvo_tb[voice].nly = nly;
		if (p_voice->posit.voc != 0)
			above_tb[voice] = p_voice->posit.voc == SL_ABOVE;
		else if (p_voice->next
/*fixme:%%staves:KO - find an other way..*/
		      && p_voice->next->staff == staff
		      && p_voice->next->have_ly)
			above_tb[voice] = 1;
		if (above_tb[voice])
			lyst_tb[staff].a = 1;
		else
			lyst_tb[staff].b = 1;
	}

	/* draw the lyrics under the staves */
	i = 0;
	for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
		struct tblt_s *tblt;

		if (!p_voice->sym)
			continue;
		if (!p_voice->have_ly
		 && !p_voice->tblts[0])
			continue;
		voice = p_voice - voice_tb;
		if (above_tb[voice]) {
			rv_tb[i++] = voice;
			continue;
		}
		staff = p_voice->staff;
		if (lyvo_tb[voice].nly > 0)
			lyst_tb[staff].bot = draw_lyrics(p_voice, lyvo_tb[voice].nly,
							 lyvo_tb[voice].h,
							 lyst_tb[staff].bot, 1);
		sc = staff_tb[p_voice->staff].staffscale;
		for (nly = 0; nly < 2; nly++) {
			if ((tblt = p_voice->tblts[nly]) == NULL)
				break;
//			if (tblt->hu > 0) {
//				lyst_tb[staff].bot -= tblt->hu;
//				lyst_tb[staff].b = 1;
//			}
			if (tblt->pitch == 0)
				draw_tblt_w(p_voice,
					lyvo_tb[voice].nly,
					lyst_tb[staff].bot * sc - tblt->hu,
					tblt);
			else
				draw_tblt_p(p_voice,
//					lyst_tb[staff].bot,
					lyst_tb[staff].bot * sc - tblt->hu,
					tblt);
			if (tblt->hu > 0) {
				lyst_tb[staff].bot -= tblt->hu / sc;
				lyst_tb[staff].b = 1;
			}
			if (tblt->ha != 0) {
				lyst_tb[staff].top += tblt->ha / sc;
				lyst_tb[staff].a = 1;
			}
		}
	}

	/* draw the lyrics above the staff */
	while (--i >= 0) {
		voice = rv_tb[i];
		p_voice = &voice_tb[voice];
		staff = p_voice->staff;
		lyst_tb[staff].top = draw_lyrics(p_voice, lyvo_tb[voice].nly,
						 lyvo_tb[voice].h,
						 lyst_tb[staff].top, -1);
	}

	/* set the max y offsets of all symbols */
	for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
		if (!p_voice->sym)
			continue;
		staff = p_voice->staff;
		if (lyst_tb[staff].a) {
			top = lyst_tb[staff].top + 2;
			for (s = p_voice->sym; s; s = s->next) {
/*fixme: may have lyrics crossing a next symbol*/
				if (s->ly) {
/*fixme:should set the real width*/
					y_set(staff, 1, s->x - 2, 10, top);
				}
			}
		}
		if (lyst_tb[staff].b) {
			bot = lyst_tb[staff].bot - 2;
			if (lyvo_tb[p_voice - voice_tb].nly > 0) {
				for (s = p_voice->sym; s; s = s->next) {
					if (s->ly) {
/*fixme:should set the real width*/
						y_set(staff, 0, s->x - 2, 10, bot);
					}
				}
			} else {
				y_set(staff, 0, 0, realwidth, bot);
			}
		}
	}
}

/* -- draw the symbols near the notes -- */
/* (the staves are not yet defined) */
/* order:
 * - beams
 * - decorations near the notes
 * - measure bar numbers
 * - decorations tied to the notes
 * - tuplets and slurs
 * - elements tied to the staves (voltas, chord symbols/annotations,
 *				  remaining decorations)
 * - lyrics
 * The buffer output is delayed until the definition of the staff system,
 * so, global variables must be saved (see music.c delayed_output()).
 */
void draw_sym_near(void)
{
	struct VOICE_S *p_voice;
	struct SYMBOL *s, *g;
	int i, staff;

	/* calculate the beams but don't draw them (the staves are not yet defined) */
	for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
		struct BEAM bm;
		int first_note = 1;

		for (s = p_voice->sym; s; s = s->next) {
			for (g = s->extra; g; g = g->next) {
				if (g->type != NOTEREST)
					continue;
				if ((g->sflags & (S_BEAM_ST | S_BEAM_END))
							== S_BEAM_ST)
					calculate_beam(&bm, g);
			}
			if (s->abc_type != ABC_T_NOTE)
				continue;
			if (((s->sflags & S_BEAM_ST) && !(s->sflags & S_BEAM_END))
			 || (first_note && !(s->sflags & S_BEAM_ST))) {
				first_note = 0;
				calculate_beam(&bm, s);
			}
		}
	}

	/* initialize the y offsets */
	for (staff = 0; staff <= nstaff; staff++) {
		for (i = 0; i < YSTEP; i++) {
			staff_tb[staff].top[i] = 0;
			staff_tb[staff].bot[i] = 24;
		}
	}

	set_tie_room();
	draw_deco_near();

	/* set the min/max vertical offsets */
	for (s = tsfirst; s; s = s->ts_next) {
		int y;

		if (s->flags & ABC_F_INVIS)
			continue;
		if (s->type == GRACE) {
			for (g = s->extra; g; g = g->next) {
				y_set(s->staff, 1, g->x - 2, 4,
						g->ymx + 1);
				y_set(s->staff, 0, g->x - 2, 4,
						g->ymn - 1);
			}
			continue;
		}
		if (s->type != MREST) {
			y_set(s->staff, 1, s->x - s->wl, s->wl + s->wr, s->ymx + 2);
			y_set(s->staff, 0, s->x - s->wl, s->wl + s->wr, s->ymn - 2);
		} else {
			y_set(s->staff, 1, s->x - 16, 32, s->ymx + 2);
		}
		if (s->abc_type != ABC_T_NOTE)
			continue;

		/* have room for the accidentals */
		if (s->u.note.notes[s->nhd].acc) {
			y = s->y + 8;
			if (s->ymx < y)
				s->ymx = y;
			y_set(s->staff, 1, s->x, 0., y);
		}
		if (s->u.note.notes[0].acc) {
			y = s->y;
			if ((s->u.note.notes[0].acc & 0x07) == A_SH
			 || s->u.note.notes[0].acc == A_NT)
				y -= 7;
			else
				y -= 5;
			if (s->ymn > y)
				s->ymn = y;
			y_set(s->staff, 0, s->x, 0., y);
		}
	}

	if (cfmt.measurenb >= 0)
		draw_measnb();

	draw_deco_note();

	for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
		s = p_voice->sym;
		if (!s)
			continue;
//color
		set_color(s->color);
		set_sscale(p_voice->staff);

		/* draw the tuplets near the notes */
		for ( ; s; s = s->next) {
			if (s->sflags & S_IN_TUPLET) {
				for (g = s->extra; g; g = g->next) {
					if (g->type == TUPLET) {
						s = draw_tuplet(g, s);
						break;
					}
				}
			}
		}
		draw_all_slurs(p_voice);

		/* draw the tuplets over the slurs */
		for (s = p_voice->sym; s; s = s->next) {
			if (s->sflags & S_IN_TUPLET) {
				for (g = s->extra ; g; g = g->next) {
					if (g->type == TUPLET) {
						s = draw_tuplet(g, s);
						break;
					}
				}
			}
		}
	}

	/* set the top and bottom for all symbols to be out of the staves */
	{
		int top, bot;

		for (staff = 0; staff <= nstaff; staff++) {
			top = staff_tb[staff].topbar + 2;
			bot = staff_tb[staff].botbar - 2;
			for (i = 0; i < YSTEP; i++) {
				if (top > staff_tb[staff].top[i])
					staff_tb[staff].top[i] = (float) top;
				if (bot < staff_tb[staff].bot[i])
					staff_tb[staff].bot[i] = (float) bot;
			}
		}
	}
	set_color(0);
//	draw_deco_note();
	draw_deco_staff();
	set_sscale(-1);		/* restore the scale parameters */
	draw_all_lyrics();
}

/* -- draw the name/subname of the voices -- */
static void draw_vname(float indent)
{
	struct VOICE_S *p_voice;
	int n, staff;
	struct {
		int nl;
		char *v[8];
	} staff_d[MAXSTAFF], *staff_p;
	char *p, *q;
	float y;

	for (staff = cursys->nstaff; staff >= 0; staff--) {
		if (!cursys->staff[staff].empty)
			break;
	}
	if (staff < 0)
		return;

	memset(staff_d, 0, sizeof staff_d);
	n = 0;
	for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
		if (!p_voice->sym)
			continue;
		staff = cursys->voice[p_voice - voice_tb].staff;
		if (cursys->staff[staff].empty)
			continue;
		if (p_voice->new_name) {
			p_voice->new_name = 0;
			p = p_voice->nm;
		} else {
			p = p_voice->snm;
		}
		if (!p)
			continue;
		if (cursys->staff[staff].flags & CLOSE_BRACE2) {
			while (!(cursys->staff[staff].flags & OPEN_BRACE2))
				staff--;
		} else if (cursys->staff[staff].flags & CLOSE_BRACE) {
			while (!(cursys->staff[staff].flags & OPEN_BRACE))
				staff--;
		}
		staff_p = &staff_d[staff];
		for (;;) {
			staff_p->v[staff_p->nl++] = p;
			p = strstr(p, "\\n");
			if (!p
			 || staff_p->nl >= MAXSTAFF)
				break;
			p += 2;
		}
		n++;
	}
	if (n == 0)
		return;
	str_font(VOICEFONT);
	indent = -indent * .5;			/* center */
	for (staff = nstaff; staff >= 0; staff--) {
		staff_p = &staff_d[staff];
		if (staff_p->nl == 0)
			continue;
		y = staff_tb[staff].y
			+ staff_tb[staff].topbar * .5
				* staff_tb[staff].staffscale
			+ 9 * (staff_p->nl - 1)
			- cfmt.font_tb[VOICEFONT].size * .3;
		n = staff;
		if (cursys->staff[staff].flags & OPEN_BRACE2) {
			while (!(cursys->staff[n].flags & CLOSE_BRACE2))
				n++;
		} else if (cursys->staff[staff].flags & OPEN_BRACE) {
			while (!(cursys->staff[n].flags & CLOSE_BRACE))
				n++;
		}
		if (n != staff)
			y -= (staff_tb[staff].y - staff_tb[n].y) * .5;
		for (n = 0; n < staff_p->nl; n++) {
			p = staff_p->v[n];
			q = strstr(p, "\\n");
			if (q)
				*q = '\0';
			a2b("%.1f %.1f M ", indent, y);
			put_str(p, A_CENTER);
			y -= 18.;
			if (q)
				*q = '\\';
		}
	}
}

/* -- set the y offset of the staves and return the whole height -- */
static float set_staff(void)
{
	struct SYSTEM *sy;
	struct SYMBOL *s;
	int i, staff, prev_staff;
	float y, staffsep, dy, maxsep, mbot, v;
	char empty[MAXSTAFF];

	/* search the empty staves in each parts */
	memset(empty, 1, sizeof empty);
	for (staff = 0; staff <= nstaff; staff++)
		staff_tb[staff].empty = 0;
	sy = cursys;
	for (staff = 0; staff <= nstaff; staff++) {
		if (!sy->staff[staff].empty)
			empty[staff] = 0;
	}
	for (s = tsfirst; s; s = s->ts_next) {
		if (!(s->sflags & S_NEW_SY))
			continue;
		sy = sy->next;
		for (staff = 0; staff <= nstaff; staff++) {
			if (!sy->staff[staff].empty)
				empty[staff] = 0;
		}
	}

	/* output the scale of the voices
	 * and flag as non empty the staves with tablatures */
	{
		struct VOICE_S *p_voice;

		for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
			if (p_voice->scale != 1)
				a2b("/scvo%d{gsave %.2f dup scale}!\n",
				     (int) (p_voice - voice_tb),
				     p_voice->scale);
			if (p_voice->tblts[0])
				empty[p_voice->staff] = 0;
		}
	}

	/* set the vertical offset of the 1st staff */
	for (staff = 0; staff <= nstaff; staff++) {
		if (!empty[staff])
			break;
		staff_tb[staff].empty = 1;
	}
	y = 0;
	if (staff > nstaff)
		staff--;			/* one staff, empty */

	{
		for (i = 0; i < YSTEP; i++) {
			v = staff_tb[staff].top[i];
			if (y < v)
				y = v;
		}
	}

	/* draw the parts and tempo indications if any */
	y += draw_partempo(staff, y);

	y *= staff_tb[staff].staffscale;
	staffsep = cfmt.staffsep * 0.5 +
			staff_tb[staff].topbar * staff_tb[staff].staffscale;
	if (y < staffsep)
		y = staffsep;
	staff_tb[staff].y = -y;

	/* set the offset of the other staves */
	prev_staff = staff;
	for (staff++; staff <= nstaff; staff++) {
		if (empty[staff]) {
			staff_tb[staff].empty = 1;
			continue;
		}
		if (sy->staff[prev_staff].sep != 0)
			staffsep = sy->staff[prev_staff].sep;
		else
			staffsep = cfmt.sysstaffsep;
		if (sy->staff[prev_staff].maxsep != 0)
			maxsep = sy->staff[prev_staff].maxsep;
		else
			maxsep = cfmt.maxsysstaffsep;

		dy = 0;
		if (staff_tb[staff].staffscale
				== staff_tb[prev_staff].staffscale) {
			for (i = 0; i < YSTEP; i++) {
				v = staff_tb[staff].top[i]
				  - staff_tb[prev_staff].bot[i];
				if (dy < v)
					dy = v;
			}
			dy *= staff_tb[staff].staffscale;
		} else {
			for (i = 0; i < YSTEP; i++) {
				v = staff_tb[staff].top[i]
					* staff_tb[staff].staffscale
				  - staff_tb[prev_staff].bot[i]
					* staff_tb[prev_staff].staffscale;
				if (dy < v)
					dy = v;
			}
		}
		staffsep += staff_tb[staff].topbar
				* staff_tb[staff].staffscale;
		if (dy < staffsep)
			dy = staffsep;
		maxsep += staff_tb[staff].topbar
				* staff_tb[staff].staffscale;
		if (dy > maxsep)
			dy = maxsep;
		y += dy;
		staff_tb[staff].y = -y;

		prev_staff = staff;
	}
	mbot = 0;
	for (i = 0; i < YSTEP; i++) {
		v = staff_tb[prev_staff].bot[i];
		if (mbot > v)
			mbot = v;
	}
	mbot *= staff_tb[prev_staff].staffscale;

	/* output the staff offsets */
	for (staff = nstaff; staff >= 0; staff--) {
		dy = staff_tb[staff].y;
		if (staff_tb[staff].staffscale != 1
		 && staff_tb[staff].staffscale != 0) {
			a2b("/scst%d{gsave 0 %.2f T %.2f dup scale}!\n",
			     staff, dy, staff_tb[staff].staffscale);
			a2b("/y%d{}!\n", staff);
		} else {
			a2b("/y%d{%.1f add}!\n", staff, dy);
		}
		a2b("/yns%d{%.1f add}!\n", staff, dy);
	}

	if (mbot == 0) {
		for (staff = nstaff; staff >= 0; staff--) {
			if (!empty[staff])
				break;
		}
		if (staff < 0)		/* no symbol in this system ! */
			return y;
	}
	dy = -mbot;
	staffsep = cfmt.staffsep * 0.5;
	if (dy < staffsep)
		dy = staffsep;
	maxsep = cfmt.maxstaffsep * 0.5;
	if (dy > maxsep)
		dy = maxsep;

	/* return the whole staff system height */
	return y + dy;
}

/* -- set the bottom and height of the measure bars -- */
static void bar_set(float *bar_bot, float *bar_height, float *xstaff)
{
	int staff;
	float dy, staffscale, top, bot;

	dy = 0;
	for (staff = 0; staff <= cursys->nstaff; staff++) {
//		if (cursys->staff[staff].empty) {
		if (xstaff[staff] < 0) {
			bar_bot[staff] = bar_height[staff] = 0;
			continue;
		}
		staffscale = staff_tb[staff].staffscale;
		top = staff_tb[staff].topbar * staffscale;
		bot = staff_tb[staff].botbar * staffscale;
		if (dy == 0)
			dy = staff_tb[staff].y + top;
		bar_bot[staff] = staff_tb[staff].y + bot;
		bar_height[staff] = dy - bar_bot[staff];

		if (cursys->staff[staff].flags & STOP_BAR)
			dy = 0;
		else
			dy = bar_bot[staff];
	}
}

/* -- draw the staff systems and the measure bars -- */
float draw_systems(float indent)
{
	struct SYMBOL *s, *s2;
	int staff, bar_force;
	float xstaff[MAXSTAFF], bar_bot[MAXSTAFF], bar_height[MAXSTAFF];
	float staves_bar, x, x2, line_height;

	line_height = set_staff();
	draw_vname(indent);

	/* draw the staff, skipping the staff breaks */
	for (staff = 0; staff <= nstaff; staff++)
		xstaff[staff] = cursys->staff[staff].empty ? -1 : 0;
	bar_set(bar_bot, bar_height, xstaff);
	draw_lstaff(0);
	bar_force = 0;
	for (s = tsfirst; s; s = s->ts_next) {
		if (bar_force && s->time != bar_force) {
			bar_force = 0;
			for (staff = 0; staff <= nstaff; staff++) {
				if (cursys->staff[staff].empty)
					xstaff[staff] = -1;
			}
			bar_set(bar_bot, bar_height, xstaff);
		}
		if (s->sflags & S_NEW_SY) {
			staves_bar = 0;
			for (s2 = s; s2; s2 = s2->ts_next) {
				if (s2->time != s->time)
					break;
				if (s2->type == BAR) {
					staves_bar = s2->x;
					break;
				}
			}
			cursys = cursys->next;
			for (staff = 0; staff <= nstaff; staff++) {
				x = xstaff[staff];
				if (x < 0) {			// no staff yet
					if (!cursys->staff[staff].empty) {
						if (staves_bar != 0)
							xstaff[staff] = staves_bar;
						else
							xstaff[staff] = s->x - s->wl - 2;
					}
					continue;
				}
				if (!cursys->staff[staff].empty) // if not staff stop
					continue;
				if (staves_bar != 0) {
					x2 = staves_bar;
					bar_force = s->time;
				} else {
					x2 = s->x - s->wl - 2;
					xstaff[staff] = -1;
				}
				draw_staff(staff, x, x2);
			}
			bar_set(bar_bot, bar_height, xstaff);
		}
		staff = s->staff;
		switch (s->type) {
		case BAR:
			if ((s->sflags & S_SECOND)
//			 || cursys->staff[staff].empty)
			 || xstaff[staff] < 0)
				s->flags |= ABC_F_INVIS;
			if (s->flags & ABC_F_INVIS)
				break;
			draw_bar(s, bar_bot[staff], bar_height[staff]);
			if (annotate)
				anno_out(s, 'B');
			break;
		case STBRK:
			if (cursys->voice[s->voice].range == 0) {
				if (s->xmx > .5 CM) {
					int i, nvoice;

					/* draw the left system if stbrk in all voices */
					nvoice = 0;
					for (i = 0; i < MAXVOICE; i++) {
						if (cursys->voice[i].range > 0)
							nvoice++;
					}
					for (s2 = s->ts_next; s2; s2 = s2->ts_next) {
						if (s2->type != STBRK)
							break;
						nvoice--;
					}
					if (nvoice == 0)
						draw_lstaff(s->x);
				}
			}
			s2 = s->prev;
			if (!s2)
				break;
			if (s2->type == BAR)
				x2 = s2->x;
			else
				x2 = s->x - s->xmx;
			staff = s->staff;
			x = xstaff[staff];
			if (x >= 0) {
				if (x >= x2)
					continue;
				draw_staff(staff, x, x2);
				xstaff[staff] = s->x;
			}
			break;
		default:
//fixme:does not work for "%%staves K: M: $" */
//removed for K:/M: in empty staves
//			if (cursys->staff[staff].empty)
//				s->flags |= ABC_F_INVIS;
			break;
		}
	}

	// draw the end of the staves
	for (staff = 0; staff <= nstaff; staff++) {
		if (bar_force && cursys->staff[staff].empty)
			continue;
		if ((x = xstaff[staff]) < 0)
			continue;
		draw_staff(staff, x, realwidth);
	}
	set_sscale(-1);
	return line_height;
}

/* -- output PostScript sequences and set the staff and voice colors -- */
void output_ps(struct SYMBOL *s, int color)
{
	struct SYMBOL *g, *g2;

	g = s->extra;
	g2 = NULL;
	for ( ; g; g = g->next) {
		if (g->type == FMTCHG) {
			switch (g->aux) {
			case PSSEQ:
//			case SVGSEQ:
//				if (g->aux == SVGSEQ)
//					a2b("%%svg %s\n", g->text);
//				else
					a2b("%s\n", g->text);
				if (!g2)
					s->extra = g->next;
				else
					g2->next = g->next;
				continue;
			}
		}
		g2 = g;
	}
}

/* -- draw remaining symbols when the staves are defined -- */
static void draw_symbols(struct VOICE_S *p_voice)
{
	struct BEAM bm;
	struct SYMBOL *s;
	float x, y;
	int staff, first_note;

#if 0
	/* output the PostScript code at start of line */
	for (s = p_voice->sym; s; s = s->next) {
		if (s->extra)
			output_ps(s, 1);
		switch (s->type) {
		case CLEF:
		case KEYSIG:
		case TIMESIG:
		case BAR:
			continue;	/* skip the symbols added by init_music_line() */
		}
		break;
	}
#endif

	bm.s2 = NULL;
	first_note = 1;
	for (s = p_voice->sym; s; s = s->next) {
		if (s->extra)
			output_ps(s, 1);
		if (s->flags & ABC_F_INVIS) {
			switch (s->type) {
			case KEYSIG:
				memcpy(&p_voice->key, &s->u.key,
					sizeof p_voice->key);
			default:
				continue;
			case NOTEREST:
			case GRACE:
				break;
			}
		}
		set_color(s->color);
		x = s->x;
		switch (s->type) {
		case NOTEREST:
			set_scale(s);
			if (s->abc_type == ABC_T_NOTE) {
				if ((s->sflags & (S_BEAM_ST | S_BEAM_END)) == S_BEAM_ST
				 || (first_note && !(s->sflags & S_BEAM_ST))) {
					first_note = 0;
					if (calculate_beam(&bm, s)) {
						if (annotate)
							anno_out(s, 'b');
						draw_beams(&bm);
					}
				}
				draw_note(x, s, bm.s2 == NULL);
				if (annotate)
					anno_out(s, 'N');
				if (s == bm.s2)
					bm.s2 = NULL;
				if (annotate
				 && (s->sflags & (S_BEAM_ST | S_BEAM_END))
							== S_BEAM_END)
					anno_out(s, 'e');
				break;
			}
			draw_rest(s);
			if (annotate)
				anno_out(s, 'R');
			break;
		case BAR:
			break;			/* drawn in draw_systems */
		case CLEF:
			staff = s->staff;
			if (s->sflags & S_SECOND)
/*			 || p_voice->staff != staff)	*/
				break;		/* only one clef per staff */
			if ((s->flags & ABC_F_INVIS)
			 || staff_tb[staff].empty)
				break;
			set_color(0);
			set_sscale(staff);
			y = staff_tb[staff].y;
			x -= 10;		/* clef shift - see set_width() */
			putxy(x, y + s->y);
			if (s->u.clef.name)
				a2b("%s\n", s->u.clef.name);
			else
				a2b("%c%cclef\n",
				     s->aux ? 's' : ' ',
				     "tcbp"[(unsigned) s->u.clef.type]);
			if (s->u.clef.octave != 0) {
/*fixme:break the compatibility and avoid strange numbers*/
				if (s->u.clef.octave > 0)
					y += s->ymx - 9;
				else
					y += s->ymn;
				putxy(x - 2, y);
				a2b("oct\n");
			}
			if (annotate)
				anno_out(s, 'c');
			break;
		case TIMESIG:
//fixme: set staff color
			memcpy(&p_voice->meter, &s->u.meter,
					sizeof p_voice->meter);
			if ((s->sflags & S_SECOND)
			 || staff_tb[s->staff].empty)
				break;
			if (cfmt.alignbars && s->staff != 0)
				break;
			set_color(0);
			set_sscale(s->staff);
			draw_timesig(x, s);
			if (annotate)
				anno_out(s, 'M');
			break;
		case KEYSIG:
//fixme: set staff color
			memcpy(&p_voice->key, &s->u.key,
					sizeof p_voice->key);
			if ((s->sflags & S_SECOND)
			 || staff_tb[s->staff].empty)
				break;
			set_color(0);
			set_sscale(s->staff);
			draw_keysig(p_voice, x, s);
			if (annotate)
				anno_out(s, 'K');
			break;
		case MREST:
			set_scale(s);
			putxy(x, staff_tb[s->staff].y + 12);
			a2b("mrest(%d)/Times-Bold 15 selectfont ",
				s->u.bar.len);
			putxy(x, staff_tb[s->staff].y + 28);
			a2b("M showc\n");
			if (annotate)
				anno_out(s, 'Z');
			break;
		case GRACE:
			set_scale(s);
			draw_gracenotes(s);
			break;
		case SPACE:
		case STBRK:
		case FMTCHG:
			break;			/* nothing */
		case CUSTOS:
			set_scale(s);
			s->sflags |= ABC_F_STEMLESS;
			draw_note(x, s, 0);
			break;
		default:
			bug("Symbol not drawn", 1);
		}
	}
	set_scale(p_voice->sym);
	draw_all_ties(p_voice);
	set_sscale(-1);
	set_color(0);
}

/* -- draw all symbols -- */
void draw_all_symb(void)
{
	struct VOICE_S *p_voice;
	struct SYMBOL *s;

	for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
#if 1 /* pb about rest display when "%%staffnonote 0" fixed in draw_rest */
		if (p_voice->sym)
#else
		if (p_voice->sym
		 && !staff_tb[p_voice->staff].empty)
#endif
			draw_symbols(p_voice);
	}

	// update the clefs
	for (s = tsfirst; s; s = s->ts_next) {
		if (s->type == CLEF)
			staff_tb[s->staff].s_clef = s;
	}
}

/* -- output a floating value, and x and y according to the current scale -- */
void putf(float v)
{
	a2b("%.1f ", v);
}

void putx(float x)
{
	putf(x / cur_scale);
}

void puty(float y)
{
	putf(scale_voice ?
		y / cur_scale :		/* scaled voice */
		y - cur_trans);		/* scaled staff */
}

void putxy(float x, float y)
{
	if (scale_voice)
		a2b("%.1f %.1f ",
		     x / cur_scale, y / cur_scale);	/* scaled voice */
	else
		a2b("%.1f %.1f ",
		     x / cur_scale, y - cur_trans);	/* scaled staff */
}

/* -- set the voice or staff scale -- */
void set_scale(struct SYMBOL *s)
{
	float scale, trans;

	scale = voice_tb[s->voice].scale;
	if (scale == 1) {
		set_sscale(s->staff);
		return;
	}
/*fixme: KO when both staff and voice are scaled */
	trans = 0;
	scale_voice = 1;
	if (scale == cur_scale && trans == cur_trans)
		return;
	if (cur_scale != 1)
		a2b("grestore ");
	cur_scale = scale;
	cur_trans = trans;
	if (scale != 1)
		a2b("scvo%d ", s->voice);
}

/* -- set the staff scale (only) -- */
void set_sscale(int staff)
{
	float scale, trans;

	scale_voice = 0;
	if (staff != cur_staff && cur_scale != 1)
		cur_scale = 0;
	if (staff >= 0)
		scale = staff_tb[staff].staffscale;
	else
		scale = 1;
	if (staff >= 0 && scale != 1)
		trans = staff_tb[staff].y;
	else
		trans = 0;
	if (scale == cur_scale && trans == cur_trans)
		return;
	if (cur_scale != 1)
		a2b("grestore ");
	cur_scale = scale;
	cur_trans = trans;
	if (scale != 1) {
		a2b("scst%d ", staff);
		cur_staff = staff;
	}
}

/* -- set the tie directions for one voice -- */
static void set_tie_dir(struct SYMBOL *sym)
{
	struct SYMBOL *s;
	int i, ntie, dir, sec, pit, ti;

	for (s = sym; s; s = s->next) {
		if (!(s->sflags & S_TI1))
			continue;

		/* if other voice, set the ties in opposite direction */
		if (s->multi != 0) {
/*			struct SYMBOL *s2;

			s2 = s->ts_next;
			if (s2->time == s->time && s2->staff == s->staff) { */
				dir = s->multi > 0 ? SL_ABOVE : SL_BELOW;
				for (i = 0; i <= s->nhd; i++) {
					ti = s->u.note.notes[i].ti1;
					if (!((ti & 0x07) == SL_AUTO))
						continue;
					s->u.note.notes[i].ti1 = (ti & SL_DOTTED) | dir;
				}
				continue;
/*			} */
		}

		/* if one note, set the direction according to the stem */
		sec = ntie = 0;
		pit = 128;
		for (i = 0; i <= s->nhd; i++) {
			if (s->u.note.notes[i].ti1) {
				ntie++;
				if (pit < 128
				 && s->u.note.notes[i].pit <= pit + 1)
					sec++;
				pit = s->u.note.notes[i].pit;
			}
		}
		if (ntie <= 1) {
			dir = s->stem < 0 ? SL_ABOVE : SL_BELOW;
			for (i = 0; i <= s->nhd; i++) {
				ti = s->u.note.notes[i].ti1;
				if (ti != 0) {
					if ((ti & 0x07) == SL_AUTO)
						s->u.note.notes[i].ti1 =
							(ti & SL_DOTTED) | dir;
					break;
				}
			}
			continue;
		}
		if (sec == 0) {
			if (ntie & 1) {
/* in chords with an odd number of notes, the outer noteheads are paired off
 * center notes are tied according to their position in relation to the
 * center line */
				ntie /= 2;
				dir = SL_BELOW;
				for (i = 0; i <= s->nhd; i++) {
					ti = s->u.note.notes[i].ti1;
					if (ti == 0)
						continue;
					if (ntie == 0) {	/* central tie */
						if (s->pits[i] >= 22)
							dir = SL_ABOVE;
					}
					if ((ti & 0x07) == SL_AUTO)
						s->u.note.notes[i].ti1 =
							(ti & SL_DOTTED) | dir;
					if (ntie-- == 0)
						dir = SL_ABOVE;
				}
				continue;
			}
/* even number of notes, ties divided in opposite directions */
			ntie /= 2;
			dir = SL_BELOW;
			for (i = 0; i <= s->nhd; i++) {
				ti = s->u.note.notes[i].ti1;
				if (ti == 0)
					continue;
				if ((ti & 0x07) == SL_AUTO)
					s->u.note.notes[i].ti1 =
						(ti & SL_DOTTED) | dir;
				if (--ntie == 0)
					dir = SL_ABOVE;
			}
			continue;
		}
/*fixme: treat more than one second */
/*		if (nsec == 1) {	*/
/* When a chord contains the interval of a second, tie those two notes in
 * opposition; then fill in the remaining notes of the chord accordingly */
			pit = 128;
			for (i = 0; i <= s->nhd; i++) {
				if (s->u.note.notes[i].ti1) {
					if (pit < 128
					 && s->u.note.notes[i].pit <= pit + 1) {
						ntie = i;
						break;
					}
					pit = s->u.note.notes[i].pit;
				}
			}
			dir = SL_BELOW;
			for (i = 0; i <= s->nhd; i++) {
				ti = s->u.note.notes[i].ti1;
				if (ti == 0)
					continue;
				if (ntie == i)
					dir = SL_ABOVE;
				if ((ti & 0x07) == SL_AUTO)
					s->u.note.notes[i].ti1 =
							(ti & SL_DOTTED) | dir;
			}
/*fixme..
			continue;
		}
..*/
/* if a chord contains more than one pair of seconds, the pair farthest
 * from the center line receives the ties drawn in opposition */
	}
}

/* -- have room for the ties out of the staves -- */
static void set_tie_room(void)
{
	struct VOICE_S *p_voice;
	struct SYMBOL *s, *s2;

	for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
		s = p_voice->sym;
		if (!s)
			continue;
		s = s->next;
		if (!s)
			continue;
		set_tie_dir(s);
		for ( ; s; s = s->next) {
			float dx, y, dy;

			if (!(s->sflags & S_TI1))
				continue;
			if (s->pits[0] < 20
			 && (s->u.note.notes[0].ti1 & 0x07) == SL_BELOW)
				;
			else if (s->pits[s->nhd] > 24
			      && (s->u.note.notes[s->nhd].ti1 & 0x07) == SL_ABOVE)
				;
			else
				continue;
			s2 = s->next;
			while (s2 && s2->abc_type != ABC_T_NOTE)
				s2 = s2->next;
			if (s2) {
				if (s2->staff != s->staff)
					continue;
				dx = s2->x - s->x - 10;
			} else {
				dx = realwidth - s->x - 10;
			}
			if (dx < 100)
				dy = 9;
			else if (dx < 300)
				dy = 12;
			else
				dy = 16;
			if (s->pits[s->nhd] > 24) {
				y = 3 * (s->pits[s->nhd] - 18) + dy;
				if (s->ymx < y)
					s->ymx = y;
				if (s2 && s2->ymx < y)
					s2->ymx = y;
				y_set(s->staff, 1, s->x + 5, dx, y);
			}
			if (s->pits[0] < 20) {
				y = 3 * (s->pits[0] - 18) - dy;
				if (s->ymn > y)
					s->ymn = y;
				if (s2 && s2->ymn > y)
					s2->ymn = y;
				y_set(s->staff, 0, s->x + 5, dx, y);
			}
		}
	}
}
