Logo Search packages:      
Sourcecode: abcm2ps version File versions

music.c

/*
 * Music generator.
 *
 * This file is part of abcm2ps.
 *
 * Copyright (C) 1998-2004 Jean-François Moine
 * Adapted from abc2ps, Copyright (C) 1996,1997 Michael Methfessel
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

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

#include "abcparse.h"
#include "abc2ps.h"

struct SYMBOL *tsnext;        /* next line when cut */
float realwidth;        /* real staff width while generating */

static int insert_meter;      /* flag to insert time signature */
static float alfa_last, beta_last;  /* for last short short line.. */

#define AT_LEAST(a,b)  do { float tmp = b; if(a<tmp) a=tmp; } while (0)

/* width of notes indexed by log2(note_length) */
float space_tb[NFLAGS_SZ] = {
      7, 10, 14.15, 20, 28.3,
      40,                     /* crotchet */
      56.6, 80, 113, 150
};
float dot_space = 1.2;              /* space factor when dots */

/* upper and lower space needed by rests */
static struct {
      char u, l;
} rest_sp[NFLAGS_SZ] = {
      {16, 31},
      {16, 25},
      {16, 19},
      {10, 19},
      {10, 13},
      {10, 13},               /* crotchet */
      {7, 7},
      {10, 4},
      {10, 7},
      {10, 13}
};

/* -- decide whether to shift heads to other side of stem on chords -- */
/* also position accidentals to avoid too much overlap */
/* this routine is called only once per tune */
static void set_head_directions(struct SYMBOL *s)
{
      int i, n, sig, d, shift;
      int i1, i2, i3, m, nac;
      float dx, xmn, dx1, dx2;

      /* set default accidental shifts */
      dx = 9;
      switch (s->head) {
      case H_SQUARE:
            dx = 14;
            break;
      case H_OVAL:
            dx = 12;
            break;
      case H_EMPTY:
            dx = 10;
            break;
      }
      n = s->nhd;
      for (i = 0; i <= n; i++)
            s->shac[i] = dx;
      if (n == 0) {
            if (s->as.u.note.grace)
                  s->shac[0] *= 0.7;
            return;
      }

      /* set the head shifts */
      dx *= 0.78;
      i1 = 1;
      i2 = n + 1;
      sig = s->stem;
      if (sig < 0) {
            dx = -dx;
            i1 = n - 1;
            i2 = -1;
      }
      if (s->as.u.note.grace)
            dx *= 0.7;

      shift = 0;
      nac = 0;
      for (i = i1; i != i2; i += sig) {
            d = s->pits[i] - s->pits[i - sig];
            if (d < 0)
                  d = -d;
            if (d > 3 || (d >= 2 && s->head < H_SQUARE))
                  shift = 0;
            else {
                  shift = !shift;
                  if (shift) {
                        s->shhd[i] = dx;
                        nac++;
                  }
            }
      }
      if (nac != 0 && dx > 0)
            s->xmx = dx;            /* shift the dots */

      /* set the accidental shifts */
      i1 = i2 = i3 = 255;
      dx1 = dx2 = 0;
      m = n;
      for (i = n; i >= 0; i--) {
            if (s->as.u.note.accs[i] == 0)
                  continue;
            if (m >= 0) {           /* see if any head shift */
                  if (i3 - s->pits[i] >= 4) {
                        for (m--; m >= 0; m--) {
                              if (s->shhd[m] != 0) {
                                    i3 = s->pits[m];
                                    break;
                              }
                        }
                  }
            }
            if (i3 - s->pits[i] >= 4 || i3 - s->pits[i] <= -4 || m < 0
                || s->shhd[m] >= 0)
                  xmn = 0;
            else  xmn = -s->shhd[m];

            if (i1 - s->pits[i] >= 6)
                  dx = xmn;
            else if (i1 - s->pits[i] >= 4) {
                  if (xmn > dx1 - 4.5 && xmn < dx1 + 4.5)
                        dx = dx1 + 4.5;
                  else  dx = xmn;
            } else {
                  if (xmn > dx1 - 7 && xmn < dx1 + 7)
                        dx = dx1 + 7;
                  else if (i2 - s->pits[i] >= 6)
                        dx = xmn;
                  else if (i2 - s->pits[i] >= 4) {
                        if (xmn > dx2 - 4.5 && xmn < dx2 + 4.5)
                              dx = dx1 + 7;
                        else  dx = xmn;
                  } else {
                        if (xmn > dx2 - 7 && xmn < dx2 + 7)
                              dx = dx1 + 7;
                        else  dx = xmn;
                  }
            }
            i2 = i1;
            i1 = s->pits[i];
            dx2 = dx1;
            dx1 = dx;
            s->shac[i] += dx;
      }

      /* adjust for grace notes */
      if (s->as.u.note.grace) {
            for (i = n; i >= 0; i--)
                  s->shac[i] *= 0.7;
      }
}

/* -- insert a clef change (treble or bass) -- */
static void insert_clef(struct SYMBOL *s,
                  int clef_type)
{
      struct VOICE_S *p_voice;
      struct SYMBOL *s2, *new_s;
      int staff, time, seq;

      time = s->time;
      seq = s->seq;

      /* search the main voice of the staff */
      staff = s->staff;
      s2 = s;
      if (voice_tb[s->voice].second) {
            while (s2->ts_prev->time == time
                   && s2->ts_prev->seq == seq)
                  s2 = s2->ts_prev;
            for (; s2 != 0; s2 = s2->ts_next) {
                  if (s2->staff == staff && !voice_tb[s2->voice].second)
                        break;
            }
      }

      /* create the symbol */
      if (s2 != 0) {
            p_voice = &voice_tb[s2->voice];
            p_voice->last_symbol = s2->prev;
            new_s = add_sym(p_voice, CLEF);
            new_s->next = s2;
            s2->prev = new_s;
      } else {
            for (p_voice = first_voice; ; p_voice = p_voice->next)
                  if (p_voice->staff == staff && !p_voice->second)
                        break;
            for (s2 = p_voice->sym; s2->next != 0; s2 = s2->next)
                  ;
            p_voice->last_symbol = s2;
            new_s = add_sym(p_voice, CLEF);
            new_s->staff = staff;
      }

      new_s->as.u.clef.type = clef_type;
      new_s->as.u.clef.line = clef_type == TREBLE ? 2 : 4;
      new_s->u = 1;           /* small clef */

      /* link in time */
      do {
            s = s->ts_prev;
      } while (s->time == time
             && s->seq == seq);
      new_s->ts_next = s->ts_next;
      new_s->ts_next->ts_prev = new_s;
      s->ts_next = new_s;
      new_s->ts_prev = s;
      new_s->time = time;
}

/* -- define the clef for a staff -- */
/* this function is called only once for the whole tune */
static void set_clef(int staff)
{
      struct SYMBOL *s;
      int min, max;
      struct SYMBOL *last_chg;
      int clef_type;

      /* get the max and min pitches */
      min = max = 16;               /* 'C' */
      for (s = first_voice->sym; s != 0; s = s->ts_next) {
            if (s->staff != staff || s->type != NOTE)
                  continue;
            if (s->pits[0] < min)
                  min = s->pits[0];
            else if (s->pits[s->nhd] > max)
                  max = s->pits[s->nhd];
      }

      staff_tb[staff].clef.type = TREBLE;
      staff_tb[staff].clef.line = 2;
      if (min >= 13)                /* all upper than 'G,' --> treble clef */
            return;
      if (max <= 19) {        /* all lower than 'F' --> bass clef */
            staff_tb[staff].clef.type = BASS;
            staff_tb[staff].clef.line = 4;
            return;
      }

      /* set clef changes */
      clef_type = TREBLE;
      last_chg = 0;
      for (s = first_voice->sym; s != 0; s = s->ts_next) {
            struct SYMBOL *s2, *s3;

            if (s->staff != staff || s->type != NOTE)
                  continue;

            /* check if a clef change may occur */
            if (clef_type == TREBLE) {
                  if (s->pits[0] > 12           /* F, */
                      || s->pits[s->nhd] > 20)  /* G */
                        continue;
                  s2 = s->ts_prev;
                  if (s2->time == s->time
                      && s2->staff == staff
                      && s2->pits[0] >= 19)     /* F */
                        continue;
                  s2 = s->ts_next;
                  if (s2 != 0
                      && s2->staff == staff
                      && s2->time == s->time
                      && s2->pits[0] >= 19)     /* F */
                        continue;
            } else {
                  if (s->pits[0] < 12           /* F, */
                      || s->pits[s->nhd] < 20)  /* G */
                        continue;
                  s2 = s->ts_prev;
                  if (s2->time == s->time
                      && s2->staff == staff
                      && s2->pits[0] <= 13)     /* G, */
                        continue;
                  s2 = s->ts_next;
                  if (s2 != 0
                      && s2->staff == staff
                      && s2->time == s->time
                      && s2->pits[0] <= 13)     /* G, */
                        continue;
            }

            /* go backwards and search where to insert a clef change */
#if 1 /*fixme:test*/
            s3 = s;
#else
            if (!voice_tb[s->voice].second
                && voice_tb[s->voice].staff == staff)
                  s3 = s;
            else  s3 = 0;
#endif
            for (s2 = s->ts_prev; s2 != last_chg; s2 = s2->ts_prev) {
                  if (s2->staff != staff)
                        continue;
                  if (s2->type == BAR) {
#if 0 /*fixme:test*/
                        if (voice_tb[s2->voice].second
                            || voice_tb[s2->voice].staff != staff)
                              continue;
#endif
                        s3 = s2;
                        break;
                  }
                  if (s2->len == 0) /* neither note nor rest */
                        continue;

                  /* exit loop if a clef change cannot occur */
                  if (s2->type == NOTE) {
                        if (clef_type == TREBLE) {
                              if (s2->pits[0] >= 19)        /* F */
                                    break;
                        } else {
                              if (s2->pits[s2->nhd] <= 13)  /* G, */
                                    break;
                        }
                  }

#if 1 /*fixme:test*/
                  /* have a 2nd choice on beam start */
                  if ((s2->sflags & S_WORD_ST)
                      || (s3->sflags & S_WORD_ST) == 0)
                        s3 = s2;
#else
                  /* have a 2nd choice if word starts on the main voice */
                  if (!voice_tb[s2->voice].second
                      && voice_tb[s2->voice].staff == staff) {
                        if ((s2->sflags & S_WORD_ST)
                            || s3 == 0
                            || (s3->sflags & S_WORD_ST) == 0)
                              s3 = s2;
                  }
#endif
            }
            s2 = last_chg;
            last_chg = s;

            /* if first change, see if any note before */
            if (s2 == 0) {
                  struct SYMBOL *s4;

#if 1 /*fixme:test*/
                  s4 = s3;
#else
                  if ((s4 = s3) == 0)
                        s4 = s;
#endif
                  for (s4 = s4->ts_prev; s4 != 0; s4 = s4->ts_prev) {
                        if (s4->staff != staff)
                              continue;
                        if (s4->type == NOTE)
                              break;
                  }

                  /* if no note, change the clef of the staff */
                  if (s4 == 0) {
                        if (clef_type == TREBLE) {
                              clef_type = BASS;
                              staff_tb[staff].clef.line = 4;
                        } else {
                              clef_type = TREBLE;
                              staff_tb[staff].clef.line = 2;
                        }
                        staff_tb[staff].clef.type = clef_type;
                        continue;
                  }
            }

            /* no change possible if no insert point */
#if 1 /*fixme:test*/
            else if (s3->time == s2->time)
#else
            if (s3 == 0 || s3 == s2)
#endif
                  continue;

            /* insert a clef change */
            clef_type = clef_type == TREBLE ? BASS : TREBLE;
            insert_clef(s3, clef_type);
      }
}

/* -- sort the symbols by time -- */
/* this function is called only once for the whole tune */
static void def_tssym(void)
{
      struct SYMBOL *s, *t, *prev_sym;
      int time, bars;
      struct VOICE_S *p_voice;

      for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
            p_voice->s_anc = 0;
            p_voice->selected = 0;
      }

      /* sort the symbol by time */
      prev_sym = 0;
      s = 0;            /* compiler warning */
      for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
            s = p_voice->sym;
            s->ts_prev = prev_sym;
            if (prev_sym != 0)
                  prev_sym->ts_next = s;
            prev_sym = s;
            p_voice->s_anc = s->next;
      }
      bars = 0;               /* (for errors) */
      for (;;) {
            int seq;

            /* search the closest next time/sequence */
            time = (unsigned) ~0 >> 1;          /* max int */
            seq = -1;
            for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
                  if ((s = p_voice->s_anc) == 0
                      || s->time > time)
                        continue;
                  if (s->time < time
                      || s->seq < seq) {
                        time = s->time;
                        seq = s->seq;
                  }
            }
            if (seq < 0)
                  break;            /* echu (finished) */

            /* warn about incorrect number of notes / measures */
            t = 0;
            for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
                  if ((s = p_voice->s_anc) != 0
                      && s->time == time
                      && s->seq == seq) {
                        p_voice->selected = 1;
                        if (s->type == BAR
                            && s->as.u.bar.type != B_INVIS
                            && t == 0)
                              t = s;
                  } else      p_voice->selected = 0;
            }

            if (t != 0) {
                  int ko = 0;

                  bars++;
                  for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
                        if ((s = p_voice->s_anc) == 0)
                              continue;
                        if (s->type == BAR)
                              continue;
                        if (s->len > 0          /* note or rest */
                            || s->time != time) { /* or misplaced bar */
                              error(1, s,
                                    "Too many notes in measure %d for voice %s",
                                    bars, p_voice->name);
                              ko = 1;
                              break;
                        }
                        p_voice->selected = 1;
                  }
                  if (ko) {
                        int newtime;

                        for (p_voice = first_voice;
                             p_voice;
                             p_voice = p_voice->next) {
                              if ((t = p_voice->s_anc) == 0
                                  || t->type != BAR)
                                    continue;
                              newtime = s->time + s->len;
                              for (; t != 0; t = t->next) {
                                    t->time = newtime;
                                    newtime += t->len;
                              }
                        }
                        bars--;
                        continue;
                  }
            }

            /* set the time linkage */
            for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
                  if (!p_voice->selected)
                        continue;
                  s = p_voice->s_anc;
                  s->ts_prev = prev_sym;
                  prev_sym->ts_next = s;
                  prev_sym = s;
                  p_voice->s_anc = s->next;
            }
      }
}

/* -- set the staff of the floating voices -- */
/* this function is called only once per tune */
static void set_float(void)
{
      struct VOICE_S *p_voice;
      int staff, staff_chg;
      struct SYMBOL *s, *s1;

      for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
            if (!p_voice->floating)
                  continue;
            staff_chg = 0;
            staff = p_voice->staff;
            for (s = p_voice->sym; s != 0; s = s->next) {
                  signed char up, down;

                  if (s->type != NOTE) {
                        if (staff_chg)
                              s->staff++;
                        continue;
                  }
                  if (s->pits[0] >= 19) {       /* F */
                        staff_chg = 0;
                        continue;
                  }
                  if (s->pits[s->nhd] <= 13) {  /* G, */
                        staff_chg = 1;
                        s->staff++;
                        continue;
                  }
                  up = 127;
                  for (s1 = s->ts_prev; s1 != 0; s1 = s1->ts_prev) {
                        if (s1->staff != staff
                            || s1->voice == s->voice)
                              break;
/*fixme:test*/
                        if (/*s1->type == NOTE
                            &&*/ s1->pits[0] < up)
                              up = s1->pits[0];
                  }
                  if (up == 127) {
                        if (staff_chg)
                              s->staff++;
                        continue;
                  }
                  if (s->pits[s->nhd] > up - 3) {
                        staff_chg = 0;
                        continue;
                  }
                  down = -127;
                  for (s1 = s->ts_next; s1 != 0; s1 = s1->ts_next) {
                        if (s1->staff != staff + 1
                            || s1->voice == s->voice)
                              break;
/*fixme:test*/
                        if (/*s1->type == NOTE
                            &&*/ s1->pits[s1->nhd] > down)
                              down = s1->pits[s1->nhd];
                  }
                  if (down == -127) {
                        if (staff_chg)
                              s->staff++;
                        continue;
                  }
                  if (s->pits[0] < down + 3) {
                        staff_chg = 1;
                        s->staff++;
                        continue;
                  }
                  up -= s->pits[s->nhd];
                  down = s->pits[0] - down;
                  if (!staff_chg) {
                        if (up < down + 3)
                              continue;
                        staff_chg = 1;
                  } else {
                        if (up < down - 3) {
                              staff_chg = 0;
                              continue;
                        }
                  }
                  s->staff++;
            }
      }
}

/* -- set the y values of some symbols -- */
static void set_yval(struct SYMBOL *s)
{
      switch (s->type) {
      case CLEF:
            if (voice_tb[s->voice].second) {
                  s->dc_top = s->ymx
                        = s->dc_bot = s->ymn = 12;
                  break;
            }
            switch (s->as.u.clef.type) {
            default:                /* treble / perc */
                  s->y = -2 * 6;
                  s->ymx = 24 + 12;
                  s->ymn = -12;
                  break;
            case ALTO:
                  s->y = -3 * 6;
                  s->ymx = 24 + 5;
                  s->ymn = -4;
                  break;
            case BASS:
                  s->y = -4 * 6;
                  s->ymx = 24 + 5;
                  s->ymn = -3;
                  break;
            }
            s->y += s->as.u.clef.line * 6;
            if (s->y > 0)
                  s->ymx += s->y;
            if (s->as.u.clef.octave > 0)
                  s->ymx += 12;
            s->dc_top = s->ymx;
            if (s->y < 0)
                  s->ymn += s->y;
            if (s->as.u.clef.octave < 0)
                  s->ymn -= 12;
            s->dc_bot = s->ymn;
            break;
      default:
            s->dc_top = 24 + 2;
            s->ymx = 24 + 2;
            s->dc_bot = -2;
            s->ymn = -2;
            break;
      }
}

/* -- set the pitch of the notes according to the clefs -- */
/* also set the vertical offset of the symbols */
/* it supposes that the first symbol of each voice is the clef */
/* this function is called only once per tune */
static void set_pitch(void)
{
      struct SYMBOL *s;
      int staff;
      char staff_clef[MAXSTAFF];

      for (s = first_voice->sym; s != 0; s = s->ts_next) {
            struct SYMBOL *g;
            int delta, np, m, pav;

            staff = s->staff;
            switch (s->type) {
            case CLEF:
                  set_yval(s);
                  if (voice_tb[s->voice].second)
                        continue;
                  switch (s->as.u.clef.type) {
                  default:          /* treble / perc */
                        delta = 0 - 2 * 2;
                        break;
                  case ALTO:
                        delta = 6 - 3 * 2;
                        break;
                  case BASS:
                        delta = 12 - 4 * 2;
                        break;
                  }
                  staff_clef[staff] = delta + s->as.u.clef.line * 2;
                  continue;
            default:
                  set_yval(s);
                  if ((g = s->grace) == 0)
                        continue;
                  delta = staff_clef[staff];
                  for (; g != 0; g = g->next) {
                        if (delta != 0) {
                              for (m = g->nhd; m >= 0; m--)
                                    g->pits[m] += delta;
                        }
                        g->ymn = 3 * (g->pits[0] - 18);
                        g->ymx = 3 * (g->pits[g->nhd] - 18);
                  }
                  continue;
            case MREST:
                  if (voice_tb[s->voice].second)
                        continue;   /* not displayed */
                  s->dc_top = 24 + 15;
                  s->ymx = 24 + 15;
                  s->dc_bot = -2;
                  s->ymn = -2;
                  continue;
            case REST:
                  s->y = 12;
                  s->dc_top = 12 + 8;
                  s->ymx = 12 + 8;
                  s->dc_bot = 12 - 8;
                  s->ymn = 12 - 8;
                  continue;
            case NOTE:
                  break;
            }
            np = s->nhd;
            delta = staff_clef[staff];
            if (delta != 0) {
                  for (m = np; m >= 0; m--)
                        s->pits[m] += delta;
            }
            pav = 0;
            for (m = np; m >= 0; m--)
                  pav += s->pits[m];
            s->yav = 3 * pav / (np + 1) - 3 * 18;
            s->ymx = 3 * (s->pits[np] - 18) + 2;
            s->dc_top = s->ymx;
            s->ymn = 3 * (s->pits[0] - 18) - 2;
            s->dc_bot = s->ymn;
      }
}

/* -- set the stem direction when multi-voices -- */
/* and adjust the vertical offset of the rests */
/* this function is called only once per tune */
static void set_multi(void)
{
      struct SYMBOL *s;
      int i, staff, rvoice;
      struct {
            int nvoice;
            struct {
                  int voice;
                  short ymn;
                  short ymx;
            } st[4];          /* (no more than 4 voices per staff) */
      } stb[MAXSTAFF];
      struct {
            short range;
            signed char st1, st2;
      } vtb[MAXVOICE];
      struct VOICE_S *p_voice;

      for (p_voice = first_voice, rvoice = 0;
           p_voice != 0;
           p_voice = p_voice->next, rvoice++)
            vtb[p_voice - voice_tb].range = rvoice;

      s = first_voice->sym;
      while (s != 0) {
            struct SYMBOL *t;

            for (staff = nstaff; staff >= 0; staff--) {
                  stb[staff].nvoice = -1;
                  for (i = 4; --i >= 0; ) {
                        stb[staff].st[i].voice = -1;
                        stb[staff].st[i].ymx = 0;
                        stb[staff].st[i].ymn = 24;
                  }
            }
            for (i = 0; i < MAXVOICE; i++)
                  vtb[i].st1 = vtb[i].st2 = -1;

            /* go to the next bar and get the max/min offsets */
            for (t = s;
                 t != 0 && t->type != BAR;
                 t = t->ts_next) {
                  if (t->len == 0         /* not a note or a rest */
                      || t->as.u.note.invis)
                        continue;
                  staff = t->staff;
                  if (vtb[t->voice].st1 < 0)
                        vtb[t->voice].st1 = staff;
                  else if (vtb[t->voice].st1 == staff)
                        ;
                  else {
                        if (staff > vtb[t->voice].st1) {
                              if (staff > vtb[t->voice].st2)
                                    vtb[t->voice].st2 = staff;
                        } else {
                              if (vtb[t->voice].st1 > vtb[t->voice].st2)
                                    vtb[t->voice].st2 = vtb[t->voice].st1;
                              vtb[t->voice].st1 = staff;
                        }
                  }
                  rvoice = vtb[t->voice].range;
                  for (i = stb[staff].nvoice; i >= 0; i--) {
                        if (stb[staff].st[i].voice == rvoice)
                              break;
                  }
                  if (i < 0) {
                        if (++stb[staff].nvoice >= 4)
                              bug("Too many voices per staff", 1);
                        for (i = 0; i < stb[staff].nvoice; i++) {
                              if (rvoice < stb[staff].st[i].voice) {
                                    memmove(&stb[staff].st[i + 1],
                                          &stb[staff].st[i],
                                          sizeof stb[staff].st[i]
                                                * (stb[staff].nvoice - i));
                                    break;
                              }
                        }
                        stb[staff].st[i].voice = rvoice;
                  }
                  if (t->type != NOTE)
                        continue;
                  if (t->dc_top > stb[staff].st[i].ymx)
                        stb[staff].st[i].ymx = t->dc_top;
                  if (t->dc_bot < stb[staff].st[i].ymn)
                        stb[staff].st[i].ymn = t->dc_bot;
            }

            for ( ;
                 s != 0 && s->type != BAR;
                 s = s->ts_next) {
                  int us, ls;

                  staff = s->staff;
                  if (vtb[s->voice].st2 >= 0) {
                        if (staff == vtb[s->voice].st1)
                              s->multi = -1;
                        else if (staff == vtb[s->voice].st2)
                              s->multi = 1;
                  }
                  if (stb[staff].nvoice <= 0) { /* voice alone on the staff */
                        if (s->multi != 0)
                              continue;
/*fixme:could be done in set_float()*/
                        p_voice = &voice_tb[s->voice];
                        if (p_voice->floating) {
                              if (s->staff == p_voice->staff)
                                    s->multi = -1;
                              else  s->multi = 1;
                        }
                        continue;
                  }
                  rvoice = vtb[s->voice].range;
                  for (i = 0; i < 4; i++) {
                        if (stb[staff].st[i].voice == rvoice)
                              break;
                  }
                  if (i == 4)
                        continue;         /* voice ignored */
                  if (s->multi == 0) {
                        if (i == stb[staff].nvoice)
                              s->multi = -1;    /* last voice */
                        else {
                              s->multi = 1;     /* first voice(s) */

                              /* if 3 voices, and vertical space enough,
                               * have stems down for the middle voice */
                              if (i != 0
                                  && i + 1 == stb[staff].nvoice) {
                                    if (stb[staff].st[i].ymn - cfmt.stemheight
                                        > stb[staff].st[i + 1].ymx)
                                          s->multi = -1;

                                    /* special case for unisson */
                                    t = s->ts_next;
                                    if (s->pits[s->nhd] == s->ts_prev->pits[0]
                                        && (s->sflags & S_WORD_ST)
                                        && s->as.u.note.word_end
                                        && (t == 0
                                          || t->staff != s->staff
                                          || t->time != s->time))
                                          s->multi = -1;
                              }
                        }
                  }
                  if (s->type != REST)
                        continue;

                  /* set the rest vertical offset */
                  /* (if visible and invisible rests on the same staff,
                   *  set as if 1 rest only) */
                  us = rest_sp[C_XFLAGS - s->nflags].u;
                  ls = rest_sp[C_XFLAGS - s->nflags].l;

                  if (i == 0) {                 /* first voice */
                        t = s;
                        while (t->ts_next != 0
                               && t->ts_next->staff == staff
                               && t->ts_next->time == s->time) {
                              t = t->ts_next;
                              if (t->type != REST
                                   || !t->as.u.note.invis)
                                    break;
                        }
                        if (t == s
                            || t->staff != staff
                            || t->time != s->time
                            || t->type != REST
                            || !t->as.u.note.invis) {
                              s->y = (stb[staff].st[1].ymx + ls)
                                    / 6 * 6;
                              if (s->y < 12)
                                    s->y = 12;
                        }
                  } else if (i == stb[staff].nvoice) { /* last voice */
                        t = s;
                        while (t->ts_prev->staff == staff
                               && t->ts_prev->time == s->time) {
                              t = t->ts_prev;
                              if (t->type != REST
                                   || !t->as.u.note.invis)
                                    break;
                        }
                        if (t == s
                            || t->staff != staff
                            || t->time != s->time
                            || t->type != REST
                            || !t->as.u.note.invis) {
                              s->y = (stb[staff].st[i - 1].ymn - us + 48)
                                    / 6 * 6 - 48;
                              if (s->y > 12)
                                    s->y = 12;
                        }
                  } else {          /* middle voice */
/*fixme: may be too high*/
                        s->y = (stb[staff].st[i - 1].ymn
                              + stb[staff].st[i + 1].ymx)
                              / 12 * 6;
                        if (((t = s->ts_next) != 0
                             && t->staff == staff
                             && t->time == s->time
                             && t->len != 0
                             && !t->as.u.note.invis
                             && t->ymx > s->y - ls)
                            || (s->ts_prev->staff == staff
                              && s->ts_prev->time == s->time
                              && s->ts_prev->len != 0
                              && !s->ts_prev->as.u.note.invis
                              && s->ts_prev->ymn < s->y + us))
                              s->shhd[0] = 10;
                  }
                  s->ymx = s->y + us;
                  s->dc_top = s->ymx;
                  if (s->dc_top > stb[staff].st[i].ymx)
                        stb[staff].st[i].ymx = s->dc_top;
                  s->ymn = s->y - ls;
                  s->dc_bot = s->ymn;
                  if (s->dc_bot < stb[staff].st[i].ymn)
                        stb[staff].st[i].ymn = s->dc_bot;
            }

            while (s != 0 && s->type == BAR)
                  s = s->ts_next;
      }
}

/* -- set the staves and stems when multivoice -- */
/* this function is called only once per tune */
static void set_global(void)
{
      int staff;
      struct SYMBOL *s;
      struct VOICE_S *p_voice;

#ifndef CLEF_TRANSPOSE
      int old_behaviour, done;

      /* adjust the pitches if old abc2ps behaviour of clef definition */
      old_behaviour = done = 0;
      for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
            int max, min;

            if (!p_voice->forced_clef
                || p_voice->clef.type == PERC)
                  continue;

            /* search if any pitch is too high for the clef */
            max = 100;
            min = -100;
            for (s = p_voice->sym; s != 0; s = s->next) {
                  switch (s->type) {
                  case CLEF:
                        if (!s->as.u.clef.check_pitch) {
                              max = 100;
                              min = -100;
                              continue;
                        }
                        switch (s->as.u.clef.type) {
                        case TREBLE:
                        case PERC:
                              max = 100;
                              min = -100;
                              break;
                        case ALTO:
                              max = 25;   /* e */
                              min = 14;   /* G, */
                              break;
                        case BASS:
                              max = 21;   /* A */
                              min = 10;   /* C, */
                              break;
                        }
                        continue;
                  default:
                        continue;
                  case NOTE:
                        if (s->pits[0] < min) {
                              done = 1;
                              break;            /* new behaviour */
                        }
                        if (s->pits[s->nhd] <= max)
                              continue;
                        old_behaviour = 1;
                        done = 1;
                        break;
                  }
                  break;
            }
            if (done)
                  break;
      }
      if (old_behaviour) {
            for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
                  int delta;

                  if (!p_voice->forced_clef
                      || p_voice->clef.type == PERC)
                        continue;
                  delta = 0;
                  for (s = p_voice->sym; s != 0; s = s->next) {
                        struct SYMBOL *g;
                        int i;

                        switch (s->type) {
                        case CLEF:
                              if (!s->as.u.clef.check_pitch)
                                    delta = 0;
                              else switch (s->as.u.clef.type) {
                                    case TREBLE:
                                    case PERC: delta = 0; break;
                                    case ALTO: delta = -7; break;
                                    case BASS: delta = -14; break;
                              }
                        default:
                              continue;
                        case NOTE:
                        case GRACE:
                              if (delta == 0)
                                    continue;
                              break;
                        }
                        if (s->type == NOTE) {
                              for (i = s->nhd; i >= 0; i--)
                                    s->pits[i] += delta;
                        } else {
                              for (g = s->grace; g != 0; g = g->next) {
                                    for (i = g->nhd; i >= 0; i--)
                                          g->pits[i] += delta;
                              }
                        }
                  }
            }
      }
#endif

      /* set a pitch for all symbols,
       * the start/end of words and the sequence number */
      for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
            int pitch, start_flag, seq;
            struct SYMBOL *sym, *lastnote;

            sym = p_voice->sym;
            pitch = 22;                   /* 'B' - if no note! */
            for (s = sym; s != 0; s = s->next) {
                  if (s->type == NOTE) {
                        pitch = s->pits[0];
                        break;
                  }
            }
            start_flag = 1;
            lastnote = 0;
            seq = 0;
            for (s = sym; s != 0; s = s->next) {
                  switch (s->type) {
                  default:
                        if ((s->sflags & S_EOLN) == 0)
                              break;
                        /* fall thru */
                  case BAR:
                  case MREST:
                  case MREP:
                        if (lastnote != 0
                            && !(s->sflags & S_BEAM_ON)) {
                              lastnote->as.u.note.word_end = 1;
                              start_flag = 1;
                              lastnote = 0;
                        }
                        if (s->type == BAR
                            && s->next == 0
                            && s->prev->type == NOTE
                            && s->prev->len >= BREVE)
                              s->prev->head = H_SQUARE;
                        break;
                  case NOTE:
                  case REST:
                        if (s->nflags <= 0 && s->len > 0) {
                              if (lastnote != 0) {
                                    lastnote->as.u.note.word_end = 1;
                                    lastnote = 0;
                              }
                              s->as.u.note.word_end = start_flag = 1;
                              s->sflags |= S_WORD_ST;
                        } else if (s->type == NOTE) {
                              if (start_flag)
                                    s->sflags |= S_WORD_ST;
                              if (s->sflags & S_EOLN)
                                    s->as.u.note.word_end = 1;
                              start_flag = s->as.u.note.word_end;
                              lastnote = s;
                        } else if (s->as.u.note.word_end
                                 || (s->sflags & S_EOLN)) {
                              if (lastnote != 0) {
                                    lastnote->as.u.note.word_end = 1;
                                    lastnote = 0;
                              }
                              s->as.u.note.word_end = 0;
                              start_flag = 1;
                        }
                        break;
                  }
                  if (s->type == NOTE) {
                        pitch = s->pits[0];
                        if (s->prev->type != NOTE) {
                              s->prev->pits[0] = (s->prev->pits[0]
                                              + pitch) / 2;
                        }
                  } else      s->pits[0] = pitch;
                  switch (s->type) {
                  case NOTE:
                  case MREST:
                  case MREP:
                  case FMTCHG:
                  case TUPLET:
                        seq = 0;
                        break;
                  case REST:
                        if (s->len != 0) {      /* if not space */
                              seq = 0;
                              break;
                        }
                        s->seq = s->next == 0 ? 0 : (s->next->seq - 1);
                  default:
                        if (s->seq <= seq)
                              s->seq = seq + 1;
                        seq = s->seq;
                        break;
                  }
            }
            if (lastnote != 0)
                  lastnote->as.u.note.word_end = 1;
      }

      /* sort the symbols by time */
      def_tssym();

      /* set the staff of the floating voices */
      set_float();

      /* set the clefs */
      if (cfmt.autoclef) {
            for (p_voice = first_voice; p_voice; p_voice = p_voice->next)
                  if (p_voice->forced_clef)
                        staff_tb[p_voice->staff].forced_clef = 1;
            for (staff = 0; staff <= nstaff; staff++) {
                  if (!staff_tb[staff].forced_clef)
                        set_clef(staff);
            }
      }

      /* set the starting clefs and adjust the note pitches */
      for (p_voice = first_voice; p_voice; p_voice = p_voice->next)
            memcpy(&p_voice->sym->as.u.clef,
                   &staff_tb[p_voice->staff].clef,
                   sizeof p_voice->sym->as.u.clef);
      set_pitch();
}

/* -- return the left indentation of the staves -- */
static float set_indent(int first_line)
{
      int staff;
      float w, maxw;
      struct VOICE_S *p_voice;
      char *p, *q;

      maxw = 0;
      for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
            p = first_line ? p_voice->nm : p_voice->snm;
            if (p == 0)
                  continue;
            str_font(&cfmt.voicefont);
            for (;;) {
                  if ((q = strstr(p, "\\n")) != 0)
                        *q = '\0';
                  w = tex_str(p);
                  if (w > maxw)
                        maxw = w;
                  if (q == 0)
                        break;
                  *q = '\\';
                  p = q + 2;
            }
      }

      /* when no name, indent the first line if requested */
      if (maxw == 0)
            return first_line ? cfmt.indent : 0;

      maxw += 4 * cwid(' ') * cfmt.voicefont.swfac;
      for (staff = 0; staff <= nstaff; staff++) {
            if (staff_tb[staff].brace
                || staff_tb[staff].bracket) {
                  maxw += 10;
                  break;
            }
      }
      return maxw;
}

/* -- set the y offset of the staves and return the whole height -- */
/* !! this routine is tied to draw_vocals() !! */
static float set_staff(void)
{
      struct SYMBOL *s;
      struct VOICE_S *p_voice;
      int staff, any_part, any_tempo;
      float y, vocal_height, staffsep, dy, maxsep;
      struct {
            char avoc, bvoc;  /* number of vocals above and below the staff */
            short vocal;            /* some vocal below the staff */
            float x;
            float ctop, mtop;
            float cbot, mbot;
      } delta_tb[MAXSTAFF], *p_delta;

      memset(delta_tb, 0, sizeof delta_tb);
      for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
            if (p_voice->nvocal > 0) {
                  staff = p_voice->staff;
                  if (cfmt.vocalabove
                      || (p_voice->next != 0
                        && p_voice->next->staff == staff
                        && p_voice->next->nvocal > 0)) {
                        delta_tb[staff].avoc += p_voice->nvocal;
                        if (staff > 0)
                              delta_tb[staff - 1].vocal = 1;
                  } else {
                        delta_tb[staff].bvoc += p_voice->nvocal;
                        delta_tb[staff].vocal = 1;
                  }
            }
      }
      delta_tb[nstaff].vocal = 1;

      any_part = any_tempo = 0;
      for (s = first_voice->sym->next;    /* skip the clefs */
           s != 0; s = s->ts_next) {
/*fixme: bad! better have a flag in the voice*/
            if (s->voice == first_voice - voice_tb) {
                  switch (s->type) {
                  case PART:
                        if (cfmt.printparts)
                              any_part = 1;
                        break;
                  case TEMPO: 
                        any_tempo = 1;
                        break;
                  }
            }
            staff = s->staff;
            p_delta = &delta_tb[staff];
            if (s->x > p_delta->x) {
                  p_delta->x = s->x + 8;
                  p_delta->ctop = s->dc_top;
                  p_delta->cbot = s->dc_bot;
            } else {
                  if (s->dc_top > p_delta->ctop)
                        p_delta->ctop = s->dc_top;
                  if (s->dc_bot < p_delta->cbot)
                        p_delta->cbot = s->dc_bot;
            }

            /* adjust above the staff */
            if (staff == 0
                || p_delta[-1].vocal) {
                  if (p_delta->mtop < p_delta->ctop)
                        p_delta->mtop = p_delta->ctop;
            } else {
                  dy = p_delta[-1].cbot - p_delta->ctop;
                  if (p_delta[-1].mbot > dy)
                        p_delta[-1].mbot = dy;
            }

            /* adjust below the staff */
            if (p_delta->vocal) {
                  if (p_delta->mbot > p_delta->cbot)
                        p_delta->mbot = p_delta->cbot;
            } else {
                  dy = p_delta->cbot - p_delta[1].ctop;
                  if (p_delta->mbot > dy)
                        p_delta->mbot = dy;
            }
      }

      /* draw the parts and tempo indications if any */
      dy = 0;
      if (any_part || any_tempo)
            dy = draw_partempo(delta_tb[0].mtop,
                           any_part,
                           any_tempo,
                           delta_tb[0].avoc);

      /* set the staff offsets */
      staffsep = cfmt.staffsep * 0.5 + 24;
      if (cfmt.maxstaffsep >= cfmt.staffsep)
            maxsep = cfmt.maxstaffsep * 0.5 + 24;
      else  maxsep = 800;
      y = 0;
      vocal_height = 1.1 * cfmt.vocalfont.size;
      for (staff = 0, p_delta = delta_tb;
           staff <= nstaff;
           staff++, p_delta++) {
            p_delta->ctop = y + dy;
            dy += p_delta->mtop + vocal_height * p_delta->avoc + 2;
            if (dy < staffsep)
                  dy = staffsep;
            else if (dy > maxsep)
                  dy = maxsep;
            y += dy;
            staff_tb[staff].y = -y;
            PUT2("/y%d{%.1f add}def\n", staff, -y);
            if (staff == 0) {
                  staffsep = cfmt.sysstaffsep + 24;
                  if (cfmt.maxsysstaffsep >= cfmt.sysstaffsep)
                        maxsep = cfmt.maxsysstaffsep + 24;
                  else  maxsep = 800;
            }
            dy = -p_delta->mbot;
            p_delta->cbot = y + dy + 2;
            if (p_delta->bvoc > 0) {
                  dy += cfmt.vocalfont.size;
                  if (dy < cfmt.vocalspace)
                        dy = cfmt.vocalspace;
                  dy += vocal_height * (p_delta->bvoc - 1)
                        + cfmt.vocalfont.size * 0.4;
            }
      }
      staffsep = cfmt.staffsep * 0.5;
      if (p_delta->bvoc > 0)
            dy += staffsep * 0.5;
      else if (dy < staffsep)
            dy = staffsep;
      if (cfmt.maxstaffsep >= cfmt.staffsep) {
            maxsep = cfmt.maxstaffsep * 0.5 + 24;
            if (dy > maxsep)
                  dy = maxsep;
      }

      /* set the vocal offsets */
      for (p_voice= first_voice; p_voice; p_voice = p_voice->next) {
            if (p_voice->nvocal > 0) {
                  staff = p_voice->staff;
                  p_delta = &delta_tb[staff];
                  if (cfmt.vocalabove
                      || (p_voice->next != 0
                        && p_voice->next->staff == staff
                        && p_voice->next->nvocal > 0)) {
                        p_voice->yvocal = -p_delta->ctop
                               - cfmt.vocalfont.size;
                        p_delta->ctop += vocal_height * p_voice->nvocal;
                  } else {
                        p_voice->yvocal = -p_delta->cbot - cfmt.vocalfont.size
/*??*/                              + 2;
                        p_delta->cbot += vocal_height * p_voice->nvocal;
                  }
            }
      }
      y += dy;
      if (cfmt.maxstaffsep > cfmt.staffsep
          && y > cfmt.maxstaffsep)
            y = cfmt.maxstaffsep;
      return y;
}

/* -- decide on beams and on stem directions -- */
/* this routine is called only once per tune */
static void set_beams(struct SYMBOL *sym)
{
      struct SYMBOL *s, *t;
      int beam, laststem, stem, lasty;
      int do_break;

      /* set stem directions; near middle, use previous direction */
      beam = 0;
      stem = 0;                     /* (compilation warning) */
      laststem = 0;
      lasty = 0;
      do_break = 0;
      for (s = sym; s != 0; s = s->next) {
            if (s->type != NOTE) {
                  struct SYMBOL *g;

                  if ((g = s->grace) != 0) {
                        if (s->multi == 0)
                              s->multi = 1;
                        s->stem = s->multi;
                        for (; g != 0; g = g->next)
                              g->multi = g->stem = s->multi;
                  }
                  laststem = 0;
                  continue;
            }
            if ((s->stem = s->multi) == 0) {

                  /* not set by set_multi() (voice alone on the staff) */
                  s->stem = s->yav >= 12 ? -1 : 1;
                  if (!cfmt.bstemdown
                      && laststem != 0
                      && s->yav > 11
                      && s->yav < 13) {
                        int dy;

                        dy = s->yav - lasty;
                        if (dy > -7 && dy < 7)
                              s->stem = laststem;
                  }

                  /* notes in a beam have the same stem direction */
                  if ((s->sflags & S_WORD_ST)
                      && !s->as.u.note.word_end) { /* start of beam */
                        int avg, n;

                        avg = s->yav;
                        n = 12;
                        for (t = s->next; t != 0; t = t->next) {
                              if (t->type == NOTE) {
                                    avg += t->yav;
                                    n += 12;
                              }
                              if (t->as.u.note.word_end)
                                    break;
                        }
                        stem = 1;
                        if (avg >= n) {
                              stem = -1;
                              if (!cfmt.bstemdown
                                  && avg < n + n / 12
                                  && laststem != 0)
                                    stem = laststem;
                        }
                        beam = 1;
                  }
                  if (beam)
                        s->stem = stem;
            } else {                /* stem set by set_multi */
                  if ((s->sflags & S_WORD_ST)
                      && !s->as.u.note.word_end) { /* start of beam */
                        beam = 1;
                        stem = s->stem;
                  }
            }

            if (s->as.u.note.word_end)
                  beam = 0;
            if (voice_tb[s->voice].key.bagpipe)
                  s->stem = -1;
            laststem = (s->len >= MINIM || s->as.u.note.stemless)
                  ? 0 : s->stem;
            lasty = s->yav;
            if (s->len <= SEMIQUAVER / 2
                && s->prev->len <= SEMIQUAVER / 2)
                  do_break = 1;
      }

      /* set beam breaks on demi-semi-quaver sequences */
      if (do_break && cfmt.halfbeam) {
            int bartime;
            struct SYMBOL *s2;

            bartime = 0;
            for (s = sym; s != 0; s = s->next) {
                  if (s->type == BAR) {
                        bartime = s->time;
                        continue;
                  }
                  if (s->len == 0
                      || s->len > SEMIQUAVER / 2)
                        continue;
                  for (s2 = s; s2 != 0; s2 = s2->prev) {
                        if ((s2->time - bartime) % QUAVER == 0) {
                              do {
                                    s2 = s2->prev;
                              } while (s2 != 0 && s2->type != NOTE);
                              if (s2 != 0)
                                    s2->sflags |= S_BEAM_BREAK;
                              break;
                        }
                  }
                  s2 = s;
                  if ((s = s->next) == 0)
                        break;
                  while ((s->time - bartime) % QUAVER != 0) {
                        if (s->type == NOTE)
                              s2 = s;
                        if ((s = s->next) == 0)
                              break;
                  }
                  if (s == 0)
                        break;
                  s2->sflags |= S_BEAM_BREAK;
            }
      }
}

/* -- set the x offset of the grace notes -- */
static float set_graceoffs(struct SYMBOL *s)
{
      struct SYMBOL *g, *next;
      int m;
      float xx;

      xx = 0;
      g = s->grace;
      g->sflags |= S_WORD_ST;
      for ( ; ; g = g->next) {
            set_head_directions(g);
            for (m = g->nhd; m >= 0; m--) {
                  if (g->as.u.note.accs[m]) {
                        xx += 4;
                        break;
                  }
            }
            g->x = xx;

            if (g->nflags <= 0) {
                  g->sflags |= S_WORD_ST;
                  g->as.u.note.word_end = 1;
            }
            next = g->next;
            if (next == 0) {
                  g->as.u.note.word_end = 1;
                  break;
            }
            if (next->nflags <= 0)
                  g->as.u.note.word_end = 1;
            if (g->as.u.note.word_end) {
                  next->sflags |= S_WORD_ST;
                  xx += GSPACE / 4;
            }
            if (g->nflags <= 0)
                  xx += GSPACE / 4;
            if (g->y > next->y + 8)
                  xx -= 1.6;
            xx += GSPACE;
      }

      /* return the whole width */
      return xx;
}

/* -- shift the notes when voices overlap -- */
/* this routine is called only once per tune */
static void set_overlap(void)
{
      struct SYMBOL *s, *s1;

/*fixme: the accidentals are not fully treated.. */
/*fixme: problems when stems are inverted*/
      for (s = first_voice->sym; s != 0; s = s->ts_next) {
            struct SYMBOL *s2;
            int d, m, nhd2;
            float d1, d2, x1, x2, dy1, dy2, noteshift;

            if (s->type != NOTE
                || s->as.u.note.invis)
                  continue;

            if ((s2 = s->ts_next) == 0)
                  break;
            if (s->staff != s2->staff
                || s->time != s2->time)
                  continue;
            if (s2->type != NOTE) {
                  if (s2->type != REST)
                        continue;
                  s2 = s2->ts_next;
                  if (s2 == 0
                      || s2->type != NOTE
                      || s->staff != s2->staff
                      || s->time != s2->time)
                        continue;
            } else if (s->as.u.note.invis)
                  continue;

            nhd2 = s2->nhd;

            /* align the accidentals when bigger than SEMIBREVE */
            if (s->head >= H_OVAL) {
                  if (s2->head < H_OVAL)
                        for (m = nhd2; m >= 0; m--)
                              s2->shac[m] += 3.;
            } else {
                  if (s2->head >= H_OVAL)
                        for (m = s->nhd; m >= 0; m--)
                              s->shac[m] += 3.;
            }

            if (s2->multi < 0)            /* if lowest voice */
                  s2->doty = -3;          /* shift down the dots */
            d1 = d2 = x1 = x2 = dy1 = 0;
            d = s->pits[0] - s2->pits[nhd2];
            s1 = s;

            /* shift the accidentals */
/*fixme: not finished... */
            if (d != 0 && d >= -5 && d <= 5
                && s1->as.u.note.accs[0]
                && s2->as.u.note.accs[nhd2]) {
                  noteshift = (d <= -4 || d >= 4) ? 4.5 : 7;
                  if (s2->shac[nhd2] < s1->shac[0] + noteshift)
                        s2->shac[nhd2] = s1->shac[0] + noteshift;
            }

            /* if voices have a same stem direction, force a shift */
            if (s1->stem == s2->stem) {
                  if (s1->dc_bot > s2->dc_top
                      || s1->dc_top < s2->dc_bot)
                        continue;
                  if (d > 0) {
                        s1 = s2;
                        s2 = s;
                        nhd2 = s2->nhd;
                        d = -d;
                  }
#if 0
                  if ((d < -1
                       && s1->head == H_OVAL)
                      || (d < -3
                        && s1->head == H_SQUARE))
                        continue;
#endif
            }
            switch (d) {
            case 0: {               /* unisson */
                  int l1, l2;

                  if ((l1 = s1->len) >= SEMIBREVE
                      || (l2 = s2->len) >= SEMIBREVE)
                        break;
                  if (cfmt.shifthnote
                      && ((l1 = s1->len) >= MINIM
                        || (l2 = s2->len) >= MINIM))
                        break;
                  if (s1->stem == s2->stem)
                        break;
                  s2->sflags |= S_NO_HEAD;      /* same head */
                  s2->as.u.note.accs[nhd2] = 0;
/*                d2 = s1->shhd[0]; */
                  dy2 = 0;
                  if (l1 == l2)
                        goto do_shift;
                  if (l1 < l2) {
                        l1 = l2;
                        l2 = s1->len;
                  }
                  if (l1 == l2 * 2
                      || l1 == l2 * 4
                      || l1 == l2 * 8
                      || l1 == l2 * 16
                      || l1 == l2 * 3
                      || l1 * 2 == l2 * 3
                      || l1 * 3 == l2 * 4) {
                        if (l1 < MINIM) {
                              if (s1->stem < 0) {
                                    if (s1->dots > 0)
                                          dy1 = -3;
                              } else if (s2->dots > 0) {
                                    s2->sflags &= ~S_NO_HEAD;
                                    s1->sflags |= S_NO_HEAD;
                                    s2->as.u.note.accs[nhd2] =
                                          s1->as.u.note.accs[0];
                                    s1->as.u.note.accs[0] = 0;
                                    dy2 = -3;
                              }
                              goto do_shift;
                        }
                        if (l2 < CROTCHET) {    /* (l1 == MINIM) */
                              if (s2->len == MINIM) {
                                    s2->sflags &= ~S_NO_HEAD;
                                    s1->sflags |= S_NO_HEAD;
                                    s2->as.u.note.accs[nhd2] =
                                          s1->as.u.note.accs[0];
                                    s1->as.u.note.accs[0] = 0;
                              }
                              goto do_shift;
                        }
                  }
                  s2->sflags &= ~S_NO_HEAD;
                  break;
                }
            case 1:
                  break;
            default:
                  if ((s1->head == H_SQUARE
                       || s2->head == H_SQUARE)
                      && (d >= -3 && d <= 3))
                        break;
                  if (d > 0)
                        continue;
                  if (d < -1
                      && s1->head == H_OVAL
                      && s2->head == H_OVAL)
                        continue;
                  break;
            }
            if (s1->len >= BREVE
                || s2->len >= BREVE)
                  noteshift = 13;
            else if (s1->len >= SEMIBREVE
                   || s2->len >= SEMIBREVE)
                  noteshift = 10;
            else  noteshift = 7.8;
/*fixme: treat the accidentals*/
/*fixme: treat the chord shifts*/
/*fixme: treat the previous shifts (3 voices or more)*/
            /* the dot of the 2nd voice should be lower */
            dy2 = -3;

            /* if the 1st voice is below the 2nd one,
             * shift the 1st voice */
            if (d < 0) {
                  d1 = noteshift;
                  if (d > -7
                      && s2->len < CROTCHET
                      && (s2->sflags & S_WORD_ST)
                      && s2->as.u.note.word_end) {
/*fixme: have higher stem??*/
                        if (s2->stem >= 0)
                              d1 += 1; /* have space for the flag */
                  } else if (s1->pits[s1->nhd] < s2->pits[0] - 1)
                        d1 *= 0.5;
/*fixme: check if overlap in chord*/

                  /* and shift the dot of the 2nd voice if any */
                  if (s2->dots > 0)
                        x2 = d1;

                  /* the dot of the 1st voice must be lower */
                  dy1 = -3;
                  dy2 = 0;

            /* if the upper note is dotted but not the lower one,
             * shift the 1st voice */
            } else if (s1->dots > 0) {
                  if (s2->dots == 0)
                        d1 = noteshift;

                  /* both notes are dotted, shift the 2nd voice */
                  else {
                        d2 = noteshift;
                        x1 = d2;
                  }

            /* if the upper note is MINIM or higher, shift the 1st voice */
            } else if (s1->head != H_FULL
                     && s1->len > s2->len)
                        d1 = noteshift;

            /* else shift the 2nd voice */
            else  d2 = noteshift;

            /* do the shift, and update the width */
      do_shift:
            if (d1 > 0) {
                  if (s2->stem > 0)
                        d1 += s2->shhd[0];
                  else  d1 += s2->shhd[nhd2];
                  if (s1->stem > 0)
                        d1 -= s1->shhd[0];
                  else  d1 -= s1->shhd[nhd2];
                  if (d1 > 0) {
                        for (m = s1->nhd; m >= 0; m--)
                              s1->shhd[m] += d1;
                        s1->xmx += d1;
                  }
                  s2->xmx = x2;
            } else {
                  if (s1->stem > 0)
                        d2 += s1->shhd[0];
                  else  d2 += s1->shhd[nhd2];
                  if (s2->stem > 0)
                        d2 -= s2->shhd[0];
                  else  d2 -= s2->shhd[nhd2];
                  if (d2 > 0) {
                        for (m = nhd2; m >= 0; m--)
                              s2->shhd[m] += d2;
                        s2->xmx += d2;
                  }
                  s1->xmx = x1;
            }
            s1->doty = dy1;
            s2->doty = dy2;
      }
}

/* -- set the stem lengths -- */
/* this routine is called only once per tune */
static void set_stems(void)
{
      struct SYMBOL *s, *g;
      float slen, ymin, ymax;
      int ymn, ymx;

      for (s = first_voice->sym; s != 0; s = s->ts_next) {
            if (s->type != NOTE) {
                  if ((g = s->grace) == 0)
                        continue;
                  ymin = 0;
                  ymax = 24;
                  for (; g != 0; g = g->next) {
                        slen = GSTEM;
                        if (g->nflags > 1)
                              slen += 1.2 * (g->nflags - 1);
                        ymn = 3 * (g->pits[0] - 18);
                        ymx = 3 * (g->pits[g->nhd] - 18);
                        if (s->stem >= 0) {
                              g->y = ymn;
                              ymx += slen;
                              g->ys = ymx;
                        } else {
                              g->y = ymx;
                              ymn -= slen;
                              g->ys = ymn;
                        }
                        if (ymn < ymin)
                              ymin = ymn;
                        else if (ymx > ymax)
                              ymax = ymx;
                        g->dc_top = ymx;
                        g->ymx = (int) (ymx + 1);
                        g->dc_bot = ymn;
                        g->ymn = (int) (ymn - 1);
                  }
                  s->dc_top = ymax + 2;
                  s->ymx = (int) (s->dc_top + 0.5);
                  s->dc_bot = ymin - 2;
                  s->ymn = (int) (s->dc_bot - 0.5);
                  continue;
            }

            /* shift notes in chords (need stem direction to do this) */
            set_head_directions(s);

            /* set height of stem end, without considering beaming for now */
            slen = cfmt.stemheight;
            switch (s->nflags) {
            case 2: slen += 2; break;
            case 3:     slen += 5; break;
            case 4:     slen += 10; break;
            case 5:     slen += 16; break;
            }
            if (s->nhd > 0)
                  slen -= 2;
            ymn = 3 * (s->pits[0] - 18);
            ymx = 3 * (s->pits[s->nhd] - 18);
            if (s->as.u.note.stemless) {
                  if (s->stem >= 0) {
                        s->y = ymn;
                        s->ys = ymx;
                  } else {
                        s->ys = ymn;
                        s->y = ymx;
                  }
                  ymin = (float) (ymn - 4);
                  ymax = (float) (ymx + 4);
            } else if (s->stem >= 0) {
                  if (s->nflags >= 2)
                        slen -= 1;
                  if (s->pits[s->nhd] > 26
                      && (s->nflags <= 0
                        || !((s->sflags & S_WORD_ST)
                             && s->as.u.note.word_end))) {
                        slen -= 2;
                        if (s->pits[s->nhd] > 28)
                              slen -= 2;
                  }
                  s->y = ymn;
                  ymin = (float) (ymn - 4);
                  s->ys = ymx + slen;
                  if (s->ys < 12)
                        s->ys = 12;
                  ymax = s->ys + 2;
                  if (s->as.u.note.ti1[0] != 0)
/*fixme
 *                    || s->as.u.note.ti2[0] != 0) */
                        ymin -= 3;
            } else {
                  if (s->pits[0] < 18
                      && (s->nflags <= 0
                        || !((s->sflags & S_WORD_ST)
                             && s->as.u.note.word_end))) {
                        slen -= 2;
                        if (s->pits[0] < 16)
                              slen -= 2;
                  }
                  s->ys = ymn - slen;
                  if (s->ys > 12)
                        s->ys = 12;
                  ymin = s->ys - 2.;
                  s->y = ymx;
                  ymax = (float) (ymx + 4);
                  if (s->as.u.note.ti1[s->nhd] != 0)
/*fixme
 *                    || s->as.u.note.ti2[s->nhd] != 0)*/
                        ymax += 3.;
            }
                   
            s->dc_top = ymax;
            s->ymx = (int) (ymax + 0.5);
            s->dc_bot = ymin;
            s->ymn = (int) (ymin - 0.5);
      }
}

/* -- set width and space of a symbol -- */
/* This routine sets the minimal left and right widths wl,wr
 * so that successive symbols are still separated when
 * no extra glue is put between them. It also sets the prefered
 * spacings pl,pr for good output. */
static void set_width(struct SYMBOL *s)
{
      int i, m;
      struct SYMBOL *s2, *k;
      float xx, w, wlnote, wlw;

      switch (s->type) {
      case NOTE:
      case REST:

            /* set the note widths */
            switch (s->head) {
            case H_SQUARE:
                  wlnote = 8;
                  break;
            case H_OVAL:
                  wlnote = 6;
                  break;
            case H_EMPTY:
                  wlnote = 5;
                  break;
            default:
                  wlnote = 4.5;
                  break;
            }
            s->wr = wlnote;

            /* room for shifted heads and accidental signs */
            if (s->xmx > 0)
                  s->wr += s->xmx + 4.;
            s2 = s->prev;
            while (s2->type == TUPLET)
                  s2 = s2->prev;
            if (s2->type == BAR)
                  wlnote += 3;
            for (m = 0; m <= s->nhd; m++) {
                  xx = s->shhd[m];
                  if (xx < 0)
                        AT_LEAST(wlnote, -xx + 5.);
                  if (s->as.u.note.accs[m])
                        AT_LEAST(wlnote, s->shac[m] + 4.5);
            }
            if (s2->type == BAR)
                  wlnote -= 3;

            /* room for the decorations */
            if (s->as.u.note.dc.n > 0)
                  wlnote += deco_width(s);

            /* space for flag if stem goes up on standalone note */
            if ((s->sflags & S_WORD_ST)
                && s->as.u.note.word_end
                && s->stem > 0 && s->nflags > 0)
                  AT_LEAST(s->wr, s->xmx + 12.);

            /* leave room for dots and set their offset */
            if (s->dots > 0) {

                  /* standalone with up-stem and flags */
                  if (s->nflags > 0 && s->stem > 0
                      && s->xmx == 0 && s->doty == 0
                      && (s->sflags & S_WORD_ST)
                      && s->as.u.note.word_end
                      && !(s->y % 6))
                        s->xmx = DOTSHIFT;
                  switch (s->head) {
                  case H_SQUARE:
                  case H_OVAL:
                        s->xmx += 2;
                        break;
                  case H_EMPTY:
                        s->xmx += 1;
                        break;
                  }
                  AT_LEAST(s->wr, s->xmx + 12.);
                  if (s->dots >= 2)
                        s->wr += 3.5 * (s->dots - 1);
            }

            wlw = wlnote;

            /* extra space when up stem - down stem */
            if (s2->type == NOTE) {
                  if (s2->stem > 0 && s->stem < 0)
                        AT_LEAST(wlw, 7);

                  /* make sure helper lines don't overlap */
                  if ((s->y > 27 && s2->y > 27)
                      || (s->y < -3 && s2->y < -3))
                        AT_LEAST(wlw, 6);
            }

            /* leave room for guitar chord */
            /* !! this sequence is tied to draw_gchord() !! */
            if (s->as.text != 0) {
                  float lspc, rspc;
                  char *p, *q, sep, antype;

                  str_font(&cfmt.font_tb[s->gcf]);
                  lspc = rspc = cwid(' ') * cfmt.font_tb[s->gcf].swfac;
                  p = s->as.text;
                  antype = '\0';
                  sep = '\n';
                  for (;;) {
                        if (sep == '\n') {
                              if (*p != '\0' && strchr("^_<>@", *p) != 0)
                                    antype = *p++;
                              else  antype = '\0';
                        }
                        for (q = p; ; q++) {
                              if (*q == '\0' || *q == ';' || *q == '\n') {
                                    sep = *q;
                                    *q = '\0';
                                    break;
                              }
                        }
                        w = tex_str(p);
                        switch (antype) {
                        case '^':         /* above */
                        case '_':         /* below */
                        default: {        /* default = above */
                              float wl;

                              wl = w * GCHPRE;
                              if (wl > 8)
                                    wl = 8;
                              if (wl > lspc)
                                    lspc = wl;
                              w -= wl;
                              if (w > rspc)
                                    rspc = w;
                              break;
                            }
                        case '<':         /* left */
                              w += wlnote;
                              if (w > lspc)
                                    lspc = w;
                              break;
                        case '>':         /* right */
                              w += s->wr;
                              if (w > rspc)
                                    rspc = w;
                              break;
                        case '@':         /* absolute */
                              break;
                        }
                        if (sep == '\0')
                              break;
                        *q = sep;
                        p = q + 1;
                  }
/*fixme: pb when '<' only*/
                  if (s2->as.text != 0)
                        AT_LEAST(wlw, lspc);
/*fixme: pb when '>' only*/
                  for (k = s->next; k != 0; k = k->next) {
                        switch (k->type) {
                        default:
                              continue;
                        case NOTE:
                        case REST:
                        case BAR:
                              if (k->as.text != 0)
                                    AT_LEAST(s->wr, rspc);
                              break;
                        }
                        break;
                  }
            }

            /* leave room for vocals under note */
            /* related to draw_vocals() */
            if (s->ly) {
                  struct lyrics *ly = s->ly;
                  struct lyl *lyl;
                  float align = 0;

                  for (i = 0; i < MAXLY; i++) {
                        float swfac, shift;
                        unsigned char *p;

                        if ((lyl = ly->lyl[i]) == 0)
                              continue;
                        p = lyl->t;
                        w = lyl->w;
                        swfac = lyl->f->swfac;
                        xx = w + 2 * cwid(' ') * swfac;
                        if (isdigit(*p) || p[1] == ':'
                            || p[1] == '(' || p[1] == ')') {
                              float sz;

                              if (p[1] == '(')
                                    sz = cwid(p[1]);
                              else {
                                    sz = 0;
                                    while (*p != '\0') {
/*fixme: KO when '\ooo'*/
                                          if (*p == '\\') {
                                                p++;
                                                continue;
                                          }
                                          sz += cwid(*p);
                                          if (*p == ' ')
                                                break;
                                          p++;
                                    }
                              }
                              sz *= swfac;
                              shift = (w - sz + 2 * cwid(' ') * swfac)
                                    * VOCPRE;
                              if (shift > 20.)
                                    shift = 20.;
                              shift += sz;
                              if (isdigit(lyl->t[0])) {
                                    if (shift > align)
                                          align = shift;
                              }
                        } else if (*p == '\x02' || *p == '\x03')
                              shift = 0;
                        else {
                              shift = xx * VOCPRE;
                              if (shift > 20.)
                                    shift = 20.;
                        }
                        lyl->s = shift;
                        AT_LEAST(wlw, shift);
                        xx -= shift;
                        shift = 2 * cwid(' ') * swfac;
                        for (k = s->next; k != 0; k = k -> next) {
                              switch (k->type) {
                              case NOTE:
                              case REST:
                                    if (k->ly == 0
                                        || k->ly->lyl[i] == 0)
                                          xx -= 9;
                                    else if (k->ly->lyl[i]->t[0] == '\x02'
                                           || k->ly->lyl[i]->t[0] == '\x03')
                                          xx -= shift;
                                    else  break;
                                    if (xx <= 0)
                                          break;
                                    continue;
                              case CLEF:
                              case TIMESIG:
                              case KEYSIG:
                                    xx -= 10;
                                    continue;
                              case TEMPO:
                              case PART:
                              case GRACE:
                              case TUPLET:
                                    continue;
                              default:
                                    break;
                              }
                              break;
                        }
                        if (xx > s->wr)
                              s->wr = xx;
                  }
                  if (align > 0) {
                        for (i = 0; i < MAXLY; i++) {

                              if ((lyl = ly->lyl[i]) == 0)
                                    continue;
                              if (isdigit(lyl->t[0]))
                              lyl->s = align;
                        }
                  }
            }

            /* set the natural space */
            if (s->len != 0) {
                  xx = space_tb[C_XFLAGS - s->nflags];
                  if (s->dots)
                        xx *= dot_space;
                  s->pl = (1. - bnnp) * xx;
                  if (s->xmx > 0)
                        xx *= 1.2;
                  s->pr = bnnp * xx;
            } else {                /* space ('y') */
                  if (s->as.u.note.lens[1] < 0)
                        xx = 10;
                  else  xx = (float) (s->as.u.note.lens[1] / 2);
                  s->wl = s->wr = xx;
                  s->pl = s->pr = xx * 1.4;
            }

            /* reduce right space when not followed by a note */
            for (k = s->next; k != 0; k = k->next) {
                  switch (k->type) {
                  case PART:
                  case TEMPO:
                        continue;
                  default:
                        s->pr *= 0.8;
                        break;
                  case NOTE:
                  case REST:
                  case TUPLET:
                        break;
                  }
                  break;
            }

            /* squeeze notes a bit if big jump in pitch */
            if (s->type == NOTE
                && s2->type == NOTE) {
                  int dy;
                  float fac;

                  dy = s->y - s2->y;
                  if (dy < 0)
                        dy =- dy;
                  fac = 1. - 0.01 * dy;
                  if (fac < 0.9)
                        fac = 0.9;
                  s2->pr *= fac;

                  /* stretch / shrink when opposite stem directions */
                  if (s2->stem > 0 && s->stem < 0)
                        s2->pr *= 1.1;
                  else if (s2->stem < 0 && s->stem > 0)
                        s2->pr *= 0.9;
            }

            /* if preceeded by a grace note sequence, adjust */
            if (s2->type == GRACE) {
                  s->wl = wlnote - 4.5;
                  s->pl = 0;
                  wlw -= s->wl;
                  if (s2->wl < wlw - s2->wr)
                        s2->wl = wlw - s2->wr;
            } else      s->wl = wlw;
            break;
      case BAR:
            {
                  int bar_type;

                  w = 5;
                  bar_type = s->as.u.bar.type;
                  switch (bar_type) {
                  case B_OBRA:
                  case (B_OBRA << 4) + B_CBRA:
                        w = 0;            /* invisible */
                        break;
                  case (B_BAR << 4) + B_COL:
                  case (B_COL << 4) + B_BAR:
                        w += 3 + 3 + 5;
                        break;
                  case (B_COL << 4) + B_COL:
                        w += 5 + 3 + 3 + 3 + 5;
                        break;
                  default:
                        for (;;) {
                              switch (bar_type & 0x0f) {
                              case B_OBRA:
                              case B_CBRA:
                                    w += 3;
                                    break;
                              case B_COL:
                                    w += 2;
                              }
                              bar_type >>= 4;
                              if (bar_type == 0)
                                    break;
                              w += 3;
                        }
                        break;
                  }
            }
            if (w != 0) {
                  s->wl = w;
                  s->pl = w + 5;
                  if (s->next != 0
                      && s->next->type != TIMESIG
                      && s->next->type != KEYSIG) {
                        s->wr = 6;
                        s->pr = 8;
                  } else {
                        s->wr = 5;
                        s->pr = 5.5;
                  }
                  s->shhd[0] = (w - 5) * -0.5;
            }
            if (s->as.u.bar.dc.n > 0)
                  s->wl += deco_width(s);

            /* have room for the repeat numbers / guitar chord */
            if (s->as.text == 0)
                  break;
            str_font(&cfmt.font_tb[s->gcf]);
            xx = tex_str(s->as.text)
                  + cwid(' ') * cfmt.font_tb[s->gcf].swfac * 1.5;
            if (!s->as.u.bar.repeat_bar) {
                  if (s->prev->as.text != 0) {
                        float spc;

                        spc = xx * GCHPRE;
                        if (spc > 8)
                              spc = 8;
                        AT_LEAST(s->wl, spc);
                        s->pl = s->wl;
                        xx -= spc;
                  }
            }
/*fixme: may have gchord a bit further..*/
            for (s2 = s->next; s2 != 0; s2 = s2->next) {
                  switch (s2->type) {
                  case PART:
                  case TEMPO:
                  case TUPLET:
                        continue;
                  case NOTE:
                  case REST:
                  case BAR:
                        if (s2->as.text != 0) {
                              AT_LEAST(s->wr, xx);
                              s->pr = s->wr;
                        }
                        break;
                  default:
                        break;
                  }
                  break;
            }
            if (s->next != 0
                && s->next->as.text != 0
                && s->next->len > 0) {    /*fixme: what if 2 bars?*/
                  AT_LEAST(s->wr, xx);
                  s->pr = s->wr;
            }
            break;
      case CLEF:
            if (!s->as.u.clef.invis) {
                  s->wl = 12;
                  s->wr = s->u ? 10 : 16;
            } else if (!s->u) {
                  s->wl = 6;
                  s->wr = 6;
            }
            break;
      case KEYSIG: {
            int n1, n2;
            int esp = 4;

            if (s->as.u.key.nacc == 0) {
                  n1 = s->as.u.key.sf;    /* new key sig */
                  n2 = s->u;        /* old key */
                  if (n1 * n2 > 0) {
                        if (n1 < 0 /*|| n2 < 0*/) {
                              n1 = -n1;
                              n2 = -n2;
                        }
                        if (n2 > n1)
                              n1 = n2;
                  } else {
                        n1 -= n2;
                        if (n1 < 0)
                              n1 = -n1;
                        esp += 3;   /* see extra space in draw_keysig() */
                  }
            } else {
                  int last_acc;

                  n1 = s->as.u.key.nacc;
                  last_acc = s->as.u.key.accs[0];
                  for (i = 1; i < n1; i++) {
                        if (s->as.u.key.accs[i] != last_acc) {
                              last_acc = s->as.u.key.accs[i];
                              esp += 3;
                        }
                  }
            }
            if (n1 > 0) {
                  s->wr = (float) (5 * n1 + esp);
                  s->wl = 2;
            } else if (s->next != 0 && s->next->type != TIMESIG)
                  s->wl = 2;
            break;
      }
      case TIMESIG:
            /* !!tied to draw_timesig()!! */
            w = 2;
            for (i = 0; i < s->as.u.meter.nmeter; i++) {
                  int l;

                  if (s->as.u.meter.meter[i].top[0] == ' ') {
                        w += 3;
                        continue;
                  }
                  l = strlen(s->as.u.meter.meter[i].top);
                  if (l > sizeof s->as.u.meter.meter[i].top)
                        l = sizeof s->as.u.meter.meter[i].top;
                  if (s->as.u.meter.meter[i].bot[0] != '\0') {
                        int l2;

                        l2 = strlen(s->as.u.meter.meter[i].bot);
                        if (l2 > sizeof s->as.u.meter.meter[i].bot)
                              l2 = sizeof s->as.u.meter.meter[i].bot;
                        if (l2 > l)
                              l = l2;
                  }
                  w += 3.5 * l;
            }
            s->wl = w;
            s->wr = w + 7;
            break;
      case MREST:
            if (voice_tb[s->voice].second)
                  break;                  /* not displayed */
            s->wl = 40 / 2 + 16;
            s->wr = 40 / 2 + 16;
            s->pl = s->wl + 16;
            s->pr = s->wr + 16;
            break;
      case MREP:
            if (s->as.u.bar.len == 1) {
                  s->wr = s->wl = 16 / 2 + 8;
                  s->pr = s->pl = s->wr + 8;
            } else      {
                  s2 = s->prev->prev;     /* invisible rest (see parse) */
                  s->wl = s2->wl;
                  s->wr = s2->wr;
                  s->pl = s2->pl;
                  s->pr = s2->pr;
            }
            break;
      case GRACE:
            s->wl = set_graceoffs(s) + GSPACE * 0.8;
            if (s->prev != 0)
                  s->prev->pr -= s->wl - 10.;
            w = GSPACE0;
            if ((s2 = s->next) != 0
                && s2->type == NOTE) {
                  struct SYMBOL *g;

                  g = s->grace;
                  while (g->next != 0)
                        g = g->next;
                  if (g->y >= (float) (3 * (s2->pits[s2->nhd] - 18)))
                        w -= 1.;    /* above, a bit closer */
                  else if ((g->sflags & S_WORD_ST)
                         && g->y < (float) (3 * (s2->pits[0] - 18) - 7))
                        w += 2.;    /* below with flag, a bit further */
            }
            s->wr = w;
            break;
      case FMTCHG:
            if (s->u != STBRK)
                  break;
            s->wl = s->xmx;
/*fixme: if the last symbol, remove it ??*/
            if (s->next == 0 || s->next->type != CLEF)
                  s->wr = 8;
            else {
                  s->wr = 2;
                  s->next->u = 0;   /* big clef */
            }
            s->pl = s->wl + 4;
            s->pr = s->wr + 4;
            break;
      case TEMPO:
      case PART:
      case TUPLET:            /* no space */
            break;
      default:
            bug("Cannot set width for symbol", 1);
      }
}

/* -- split up unsuitable bars at end of staff -- */
static void check_bar(struct SYMBOL *s)
{
      struct VOICE_S *p_voice;
      int bar_type;
      int i;

      p_voice = &voice_tb[s->voice];

      if (s->type == KEYSIG && s->prev != 0 && s->prev->type == BAR)
            s = s->prev;
      if (s->type != BAR)
            return;
      if (s->as.u.bar.repeat_bar) {
            p_voice->bar_start = B_INVIS;
            p_voice->bar_text = s->as.text;
            p_voice->bar_repeat = 1;
            s->as.text = 0;
            s->as.u.bar.repeat_bar = 0;
      }
      bar_type = s->as.u.bar.type;
      if (bar_type == B_COL)              /* ':' */
            return;
      if ((bar_type & 0x0f) != B_COL)           /* if not left repeat bar */
            return;
      if (!(s->sflags & S_RRBAR)) {       /* 'xx:' (not ':xx:') */
            p_voice->bar_start = bar_type;
            if (s->prev != 0 && s->prev->type == BAR) {
                  s->prev->next = 0;
                  if (s->ts_prev != 0)
                        s->ts_prev->ts_next = s->ts_next;
                  if (s->ts_next != 0)
                        s->ts_next->ts_prev = s->ts_prev;
            } else      s->as.u.bar.type = B_BAR;
            return;
      }
      if (bar_type == B_DREP) {           /* '::' */
            s->as.u.bar.type = B_RREP;
            p_voice->bar_start = B_LREP;
            return;
      }
      for (i = 0; bar_type != 0; i++)
            bar_type >>= 4;
      bar_type = s->as.u.bar.type;
      s->as.u.bar.type = bar_type >> ((i / 2) * 4);
      i = ((i + 1) / 2 * 4);
      p_voice->bar_start = bar_type & ((1 << i) - 1);
}

/* -- set the end of a piece of tune -- */
/* tsnext is the beginning of the next line */
static void set_piece(struct SYMBOL *s)
{
      struct VOICE_S *p_voice;

      /* if last line, do nothing */
      if ((tsnext = s->ts_next) == 0)
            return;

      /* if the key signature changes on the next line,
       * put it at the end of the current line */
      if (tsnext->type == KEYSIG) {
            for (s = tsnext; s->ts_next != 0; s = s->ts_next)
                  if (s->ts_next->type != KEYSIG)
                        break;
            if ((tsnext = s->ts_next) == 0)
                  return;
      }
      s->ts_next = 0;

      /* set end of voices */
      for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
            int voice;

            voice = p_voice - voice_tb;
            for (s = tsnext->ts_prev; s != 0; s = s->ts_prev) {
                  struct SYMBOL *s2;

                  if (s->voice != voice)
                        continue;

                  /* set the word end / start */
                  for (s2 = s; s2 != 0; s2 = s2->prev) {
                        if (s2->type == NOTE) {
                              s2->as.u.note.word_end = 1;
                              break;
                        }
                        if (s2->type == BAR)
                              break;
                  }
                  for (s2 = s->next; s2 != 0; s2 = s2->next) {
                        if (s2->type == NOTE) {
                              s2->sflags |= S_WORD_ST;
                              break;
                        }
                        if (s2->type == BAR)
                              break;
                  }
                  s->next = 0;
                  check_bar(s);
                  break;
            }
      }
}

/* -- set the horizontal offsets -- */
static void set_xoffset(struct SYMBOL *s,
                  struct SYMBOL *last)
{
      struct SYMBOL *prev, *s3;
      struct VOICE_S *p_voice;
      float shrink, space, stretch;

      set_width(s);

      prev = s->prev;
      shrink = prev->wr + s->wl;
      space = prev->pr + s->pl;
      stretch = 0;

      switch (s->type) {
      case CLEF:
            if (prev->len == 0)
                  break;
            space = prev->pl - 5;
            break;
      case NOTE:
      case REST:
            if (prev->type == GRACE)
                  break;
            if (prev->type == BAR)
                  space *= 1.5;
            if ((s->sflags & S_WORD_ST) == 0) /* reduce spacing within a beam */
                  space *= fnnp;
            if (s->sflags & S_IN_TUPLET)  /* reduce spacing in n-plet */
                  space *= gnnp;
            /* fall thru */
      case GRACE:
            stretch = s->pl;
            break;
      case BAR:
      case MREST:
      case MREP:
            stretch = s->pl * 0.4;
            break;
      }
      s->shrink = prev->shrink + shrink;
      s->x = prev->x + space;

      switch (prev->type) {
      case NOTE:
      case GRACE:
      case REST:
            stretch += prev->pr;
            break;
      case BAR:
      case MREST:
      case MREP:
            stretch += prev->pr * 0.4;
            break;
      }
      s->stretch = stretch;

      /* if the duration of the previous symbol is greater than
         the duration of the last symbol, adjust */
      if (prev->time != last->time) {
            if (s->len != 0 || s->type == TUPLET) {
                  int len;

                  if ((len = prev->len) == 0)   /* (start of line) */
                        len = s->time - prev->time;
                  s->x = last->x
                        + (space - 5) * (s->time - last->time) / len
                        + 5;
#if 1
                  s->shrink = last->shrink + 5;
#else
                  s->shrink = last->shrink + s->wl;
#endif
            }
      }

      /* shift if clash with a previous symbol */
      for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
            s3 = p_voice->s_anc;
            if (s3->staff != s->staff || s3->voice == s->voice)
                  continue;
            if (s->shrink < s3->shrink + s3->wr + s->wl
                && (s3->type != NOTE
                  || !s3->as.u.note.invis)) {
                  shrink = s3->shrink;
                  if (s3->ly != 0
                      || (s->dc_bot <= s3->dc_top
                        && s->dc_top >= s3->dc_bot))
                        shrink += s3->wr + s->wl;
                  else if (s3->wr > s->wl)
                        shrink += s3->wr;
                  else  shrink += s->wl;
                  if (s->shrink < shrink)
                        s->shrink = shrink;
            }
      }
}

/* -- Set the characteristics of the glue between symbols, then
 *    position the symbols along the staff. If staff is overfull,
 *    only does symbols which fit in. -- */
static void set_sym_glue(float width)
{
      float alfa0, beta0;
      float alfa, beta;
      int voice, time, seq, some_grace;
      float space, shrink, stretch;
      float new_shrink, new_space, new_stretch;
      float w;
      struct SYMBOL *s;

      alfa0 = ALFA_X;               /* max shrink and stretch */
      beta0 = BETA_X;
      if (cfmt.continueall) {
            alfa0 = cfmt.maxshrink;
            beta0 = BETA_C;
      }

      /* set the offsets of the first symbols (clefs - one for each voice) */
      s = first_voice->sym;
      time = s->time;
      seq = s->seq;
      while (s != 0
             && s->time == time
             && s->seq == seq) {
            set_width(s);
            s->x = s->shrink = s->stretch = s->wl;
            voice_tb[s->voice].s_anc = s;
            s = s->ts_next;
      }
      space = shrink = stretch
            = new_space = new_shrink
            = s->ts_prev->x;

      /* then loop over the symbols */
      for (;;) {
            struct SYMBOL *s2, *s3, *s4;
            struct VOICE_S *p_voice;

            /* get the notes at this time, set spacing
             * and get the min shrinking */
            time = s->time;
            seq = s->seq;
            s4 = s;
            new_stretch = 0;
            for (s2 = s; s2 != 0; s2 = s2->ts_next) {
                  if (s2->time != time
                      || s2->seq != seq)
                        break;

                  /* set the x offsets of the symbol */
                  set_xoffset(s2, s->ts_prev);

                  /* keep the symbol with larger space */
                  if (s2->shrink > new_shrink)
                        new_shrink = s2->shrink;
                  if (s2->x > new_space)
                        new_space = s2->x;
                  if (s2->stretch > new_stretch)
                        new_stretch = s2->stretch;
            }

            /* make sure that space >= shrink */
            if (new_space < space + new_shrink - shrink)
                  new_space = space + new_shrink - shrink;

            /* set the horizontal offsets */
            stretch += new_space - space + new_stretch;
            shrink = new_shrink;
            space = new_space;

            /* adjust spacing and advance */
            s4 = s;                       /* (for overfull) */
            for ( ; s != s2; s = s->ts_next) {
                  s->x = space;
                  s->shrink = shrink;
                  s->stretch = stretch;
                  voice_tb[s->voice].s_anc = s;

                  if (s->next != 0) {

                        /* remove some double bars */
                        if (s->next->type == BAR
                            && s->next->as.text == 0
                            && s->next->next != 0
                            && s->next->next->type == BAR
                            && s->next->next->as.text == 0) {
                              s3 = 0;
                              if ((s->next->as.u.bar.type == B_SINGLE
                                   || s->next->as.u.bar.type == B_DOUBLE)
                                  && (s->next->next->as.u.bar.type & 0xf0))
                                    s3 = s->next;
#if 0
                              if ((s->next->as.u.bar.type & 0xf0)
                                  && (s->next->next->as.u.bar.type == B_SINGLE
                                    || s->next->next->as.u.bar.type == B_DOUBLE))
                                    s3 = s->next->next;
#endif
                              if (s3 != 0) {
                                    s3->prev->next = s3->next;
                                    if (s3->next != 0)
                                          s3->next->prev = s3->prev;
                                    s3->ts_prev->ts_next = s3->ts_next;
                                    if (s3->ts_next != 0)
                                          s3->ts_next->ts_prev = s3->ts_prev;
                                    if (s3 == s2)
                                          s2 = s3->ts_next;
                              }
                        }
                  }
            }

            if (s == 0)
                  break;

            /* check the total width */
            if (cfmt.continueall) {
                  if (space <= width)
                        continue;
                  if ((space - width) / (space - shrink) < alfa0)
                        continue;
            } else if (shrink < width)
                  continue;

            /* may have a key sig change at end of line */
/*fixme: may also have a meter change*/
            if (s->type == KEYSIG)
                  continue;
            s = s4->ts_prev;
            if (!cfmt.continueall)
                  error(0, s, "Line overfull");

            /* go back to the previous bar, if any */
            for (s2 = s; s2 != 0; s2 = s2->ts_prev) {
                  if ((s2->type == BAR
                       && s2->as.u.bar.type != B_INVIS)
                      || s2->type == KEYSIG)
                        break;
            }

            /* (should have some note) */
            if (s2 != 0
                && s2->time > first_voice->sym->time)
                  s = s2;
            else if (s->time <= first_voice->sym->time) {
                  error(1, s, "No symbol in the music line");
                  break;
            }

            /* don't cut in a grace note sequence if followed by note */
            if (s->type == GRACE
                && s->next != 0
                && s->next->type == NOTE)
                  s = s->prev;

            /* restore the linkages */
            if (tsnext != 0)
                  tsnext->ts_prev->ts_next = tsnext;
            for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
                  voice = p_voice - voice_tb;
                  for (s2 = tsnext; s2 != 0; s2 = s2->ts_next) {
                        if (s2->voice == voice) {
                              if (s2->prev != 0)
                                    s2->prev->next = s2;
                              break;
                        }
                  }
            }
            set_piece(s);
            break;
      }

      some_grace = 0;
      for (s = first_voice->sym; s->ts_next != 0; s = s->ts_next)
            if (s->type == GRACE)
                  some_grace = 1;

      /* get the total space from the last effective symbol */
      while (s->type == TEMPO
             || s->type == STAVES)
            s = s->ts_prev;
      space = s->x;
      stretch = s->stretch;
      shrink = s->shrink;

      /* if the last symbol is not a bar, add some extra space */
      if (s->type != BAR) {
            if (s->pr < s->wr)
                  s->pr = s->wr;
            shrink += s->wr + 3.;
            space += s->pr + 5.;
            stretch += (s->pr + 5.) * 1.4;
      }

      /* set the glue, calculate final symbol positions */
      alfa = beta = 0;
      if (space > width) {
            alfa = (space - width) / (space - shrink);
#if 0
            if (alfa > alfa0)
                  alfa = alfa0;
#endif
      } else {
            beta = (width - space) / (stretch - space);
            if (beta > beta0) {
                  if (!cfmt.continueall) {
                        error(0, s,
                              "Line underfull (%.0fpt of %.0fpt)",
                              beta0 * stretch + (1 - beta0) * space,
                              width);
                  }
                  if (!cfmt.stretchstaff)
                        beta = 0;
                  if (!cfmt.stretchlast
                      && tsnext == 0      /* if last line of tune */
                      && beta >= beta_last) {
                        alfa = alfa_last; /* shrink underfull last line same as previous */
                        beta = beta_last;
                  }
            }
      }
      alfa_last = alfa;
      beta_last = beta;
      w = alfa * shrink + beta * stretch + (1 - alfa - beta) * space;
      realwidth = w;
      if (alfa != 0) {
            for (s = first_voice->sym; s != 0; s = s->ts_next)
                  s->x = alfa * s->shrink + (1. - alfa) * s->x;
      } else {
            for (s = first_voice->sym; s != 0; s = s->ts_next)
                  s->x = beta * s->stretch + (1. - beta) * s->x;
      }
      if (some_grace) {
            for (s = first_voice->sym; s != 0; s = s->ts_next) {
                  struct SYMBOL *g;

                  if ((g = s->grace) == 0)
                        continue;
                  for ( ; g->next != 0; g = g->next)
                        ;
                  space = s->x - g->x;
                  for (g = s->grace; g != 0; g = g->next)
                        g->x += space;
            }
      }
}

/* -- check if any "real" symbol in the piece -- */
/* and adjust the scale and left and right margins */
static int any_symbol(void)
{
      struct SYMBOL *s;

      for (s = first_voice->sym; s != 0; s = s->ts_next) {
            switch (s->type) {
            case NOTE:
            case REST:
            case MREST:
            case BAR:
            case MREP:
                  return 1;
            }
      }
      return 0;
}

/* -- find one line to output -- */
static void find_piece(void)
{
      struct SYMBOL *s;
      int number, time, seq, i, voice;
      /* !! see /tw_head !! */
      static unsigned char pitw[12] =
            {28, 54, 28, 54, 28, 28, 54, 28, 54, 28, 54, 28};

      if (!cfmt.continueall) {
            voice = first_voice - voice_tb;
            if ((number = cfmt.barsperstaff) == 0) {

                  /* find the first end-of-line */
                  for (s = first_voice->sym; /*s != 0*/; s = s->ts_next) {
                        if (s->sflags & S_EOLN && s->voice == voice)
                              break;
                        if (s->ts_next == 0) {
                              /* when '\' at end of line and 'P:' */
/*                            bug("no eoln in piece", 0); */
                              break;
                        }
                  }
            } else {

                  /* count the measures */
                  for (s = first_voice->sym; s->ts_next != 0; s = s->ts_next)
                        if (s->len > 0)         /* if note or rest */
                              break;
                  for ( ; s->ts_next != 0; s = s->ts_next) {
                        if (s->type != BAR
                            || s->as.u.bar.type == B_INVIS)
                              continue;
                        if (s->prev->type == BAR)
                              continue;
                        if (s->voice == voice
                            && --number <= 0)
                              break;
                  }
            }

            /* cut at the last symbol of the sequence */
            time = s->time + s->len;
            seq = s->seq;
            if (s->len > 0) {

                  /* note or rest: cut on end time */
                  for (; s->ts_next != 0; s = s->ts_next)
                        if (s->ts_next->time >= time)
                              break;
            } else {

                  /* other symbol: cut at end of sequence */
                  for (; s->ts_next != 0; s = s->ts_next)
                        if (s->ts_next->seq != seq)
                              break;
            }
            set_piece(s);
      } else      tsnext = 0;

      for (i = nstaff; i >= 0; i--) {
            staff_tb[i].nvocal = 0;
            staff_tb[i].y = 0;
      }

      /* shift the first note if any tin whistle */
      for (i = 0; i < nwhistle; i++) {
            struct VOICE_S *p_voice;
            float w;

            p_voice = &voice_tb[whistle_tb[i].voice];
            if (p_voice != first_voice
                && p_voice->prev == 0)
                  continue;
            w = pitw[whistle_tb[i].pitch % 12];
            for (s = p_voice->sym; s != 0; s = s->next) {
                  if (s->type == NOTE)
                        break;
                  w -= s->wl + s->wr;
            }
            if (s == 0 || w < 0)
                  continue;
            if (w > s->wl)
                  s->wl = w;
      }
}

/* -- init symbol list with clef, meter, key -- */
static void init_music_line(struct VOICE_S *p_voice)
{
      struct SYMBOL *s, *sym;

      sym = p_voice->sym;
      p_voice->sym = 0;

      /* output the first postscript sequences */
      if (sym != 0) {
            while (sym->type == FMTCHG
                   && sym->u == PSSEQ) {
                  PUT1("%s\n", sym->as.text);
                  if (sym->next != 0)
                        sym->next->prev = 0;
                  if (sym->ts_next != 0)
                        sym->ts_next->ts_prev = sym->ts_prev;
                  if (sym->ts_prev != 0)
                        sym->ts_prev->ts_next = sym->ts_next;
                  if (tsnext == sym)
                        tsnext = sym->ts_next;
                  if ((sym = sym->next) == 0)
                        break;
            }
      }

      /* add clef */
      s = add_sym(p_voice, CLEF);
      s->staff = p_voice->staff;
      memcpy(&p_voice->clef, &staff_tb[p_voice->staff].clef,
             sizeof p_voice->clef);
      memcpy(&s->as.u.clef, &p_voice->clef, sizeof s->as.u.clef);

      if (!p_voice->second) {

            /* add keysig */
            if (p_voice->key.sf != 0 || p_voice->key.nacc != 0) {
                  s = add_sym(p_voice, KEYSIG);
                  s->staff = p_voice->staff;
                  s->seq++;
                  memcpy(&s->as.u.key, &p_voice->key, sizeof s->as.u.key);
                  if (s->as.u.key.bagpipe && s->as.u.key.sf == 2) /* K:Hp */
                        s->u = 3;               /* --> G natural */
            }

            /* add time signature if needed (from first voice) */
            if (insert_meter
                && first_voice->meter.nmeter != 0) {  /* != M:none */
                  s = add_sym(p_voice, TIMESIG);
                  s->staff = p_voice->staff;
                  s->seq += 2;
                  memcpy(&s->as.u.meter, &first_voice->meter,
                         sizeof s->as.u.meter);
            }

            /* add tempo if any */
            if (info.tempo) {
                  s = info.tempo;
                  memset((&s->as) + 1, 0,
                         sizeof (struct SYMBOL) - sizeof (struct abcsym));
                  p_voice->last_symbol->next = s;
                  s->prev = p_voice->last_symbol;
                  p_voice->last_symbol = s;
                  s->voice = p_voice - voice_tb;
                  s->staff = p_voice->staff;
                  s->type = TEMPO;
                  s->seq = s->prev->seq + 1;    /*??*/
                  info.tempo = 0;
            }

            /* add bar if needed */
            if (p_voice->bar_start != 0) {
                  int i;

                  i = 4;
                  if (p_voice->bar_text == 0    /* if repeat continuation */
                      && p_voice->bar_start == B_OBRA) {
                        for (s = sym; s != 0; s = s->next) {      /* search the end of repeat */
                              if (s->type == BAR) {
                                    if ((s->as.u.bar.type & 0xf0) /* if complex bar */
                                        || s->as.u.bar.type == B_CBRA
                                        || s->as.u.bar.repeat_bar)
                                          break;
                                    if (--i < 0)
                                          break;
                              }
                        }
                        if (s == 0 || sym == 0)
                              i = -1;
                        if (i >= 0 && sym->time == s->time)
                              i = -1;           /* no note */
                  }
                  if (i >= 0) {
                        s = add_sym(p_voice, BAR);
                        s->staff = p_voice->staff;
                        s->seq += 3;
                        s->as.u.bar.type = p_voice->bar_start;
                        s->as.text = p_voice->bar_text;
                        s->as.u.bar.repeat_bar = p_voice->bar_repeat;
                  }
                  p_voice->bar_start = 0;
                  p_voice->bar_repeat = 0;
                  p_voice->bar_text = 0;
            }
      }
      if ((p_voice->last_symbol->next = sym) != 0)
            sym->prev = p_voice->last_symbol;
      p_voice->nvocal = 0;
}

/* -- initialize a new line -- */
static void cut_symbols(void)
{
      struct VOICE_S *p_voice;
      struct SYMBOL *s, *s1;
      int j, t;

      clrarena(3);

      /* set start of voices */
      s1 = tsnext;
      for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
            int voice;

            p_voice->sym = 0;       /* may have no symbol */
            voice = p_voice - voice_tb;
            for (s = s1; s != 0; s = s->ts_next) {
                  if (s->voice == voice) {
                        p_voice->sym = s;
                        s->prev = 0;
                        break;
                  }
            }
      }

      /* add the first symbols of the line */
      for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
            init_music_line(p_voice);

      /* insert the new symbols into the time sorted list */
            p_voice->s_anc = p_voice->sym;
      }

      s = 0;
      t = s1->time;
      for (j = 1; ; j++) {
            int done;

            done = 1;
            for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
                  s1 = p_voice->s_anc;
                  if (s1 == 0
                      || s1->ts_prev != 0)
                        continue;
                  done = 0;         /* new symbol */
                  if (s == 0)
                        first_voice->sym = s1;
                  else  s->ts_next = s1;
                  s1->ts_prev = s;
                  s = s1;
                  s->seq = j;
                  s->time = t;
                  set_yval(s);
                  p_voice->s_anc = s->next;
            }
            if (done)
                  break;
      }
      s->ts_next = tsnext;
      if (tsnext != 0)
            tsnext->ts_prev = s;
}

/* -- output for parsed symbol list -- */
void output_music(void)
{
      struct VOICE_S *p_voice;
      int voice, first_line;
      float lwidth;

      for (p_voice = first_voice; p_voice; p_voice = p_voice->next)
            if (p_voice->sym != 0)
                  break;
      if (p_voice == 0)
            return;           /* no symbol at all */

      lvlarena(2);

      voice_dup();      /* duplicate the voices appearing in many staves */

      for (p_voice = first_voice; p_voice; p_voice = p_voice->next)
            init_music_line(p_voice);
      first_line = insert_meter;
      insert_meter = 0;

      alfa_last = 0.1;
      beta_last = 0;

      check_buffer();   /* dump buffer if not enough space for a music line */

      set_global();                 /* set global characteristics */
      if (first_voice->next != 0)   /* when multi-voices */
            set_multi();            /* set the stems direction in 'multi' */
      for (p_voice = first_voice; p_voice; p_voice = p_voice->next)
            set_beams(p_voice->sym);      /* decide on beams */
      set_stems();                  /* set the stem lengths */
      if (first_voice->next != 0)   /* when multi-voices */
            set_overlap();          /* shift the notes on voice overlap */

      clrarena(3);
      lvlarena(3);
      lwidth = ((cfmt.landscape ? cfmt.pageheight : cfmt.pagewidth)
            - cfmt.leftmargin - cfmt.rightmargin)
                  / cfmt.scale;
      if (lwidth < 1*CM) {
            error(1, 0, "Bad page width %.1f", lwidth);
            lwidth = 1*CM;
      }
      for (;;) {        /* loop over pieces of line for output */
            find_piece();

            if (any_symbol()) {
                  float indent, line_height;

                  indent = set_indent(first_line);
                  set_sym_glue(lwidth - indent);
                  if (indent != 0)
                        PUT1("%.2f 0 T\n", indent); /* do indentation */
                  PUT0("/dlsym{\n");
                  draw_sym_near();
                  PUT0("}def\n");
                  line_height = set_staff();
                  PUT0("dlsym\n");
                  draw_staff(first_line, indent);
                  for (p_voice = first_voice; p_voice; p_voice = p_voice->next)
                        draw_symbols(p_voice);
                  draw_all_deco();
                  bskip(line_height);
                  if (nwhistle != 0)
                        draw_whistle();
                  if (indent != 0)
                        PUT1("%.2f 0 T\n", -indent);
                  buffer_eob();
                  first_line = 0;
            }
            if (tsnext == 0)
                  break;
            cut_symbols();
      }
      lvlarena(2);

      /* reset the parser */
      for (voice = MAXVOICE; --voice >= 0; ) {
            voice_tb[voice].sym = 0;
            voice_tb[voice].time = 0;
      }
}

/* -- reset the generator -- */
void reset_gen(void)
{
      insert_meter = 1;
}

Generated by  Doxygen 1.6.0   Back to index