Logo Search packages:      
Sourcecode: abcm2ps version File versions

draw.c

/*
 * Drawing functions.
 *
 * 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 <stdlib.h>
#include <string.h>
#include <time.h>
#include <ctype.h>

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

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

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

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

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

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

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

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

/* -- calculate a beam -- */
/* (the staves may be defined or not) */
static int calculate_beam(struct BEAM *bm,
                    struct SYMBOL *s1)
{
      struct SYMBOL *s, *s2;
      int notes, nflags, staff, voice;
      float x, y, ys, a, b, max_stem_err;
      float sx, sy, sxx, sxy, syy, a0, stem_xoff;
      int two_staves;
      static float min_tb[2][6] = {
            {STEM_MIN, STEM_MIN,
                  STEM_MIN2, STEM_MIN3, STEM_MIN4, STEM_MIN4},
            {STEM_CH_MIN, STEM_CH_MIN,
                  STEM_CH_MIN2, STEM_CH_MIN3, STEM_CH_MIN4, STEM_CH_MIN4},
      };
  
      /* find first and last note in beam */
      notes = nflags = 0;     /* set x positions, count notes and flags */
      two_staves = 0;
      staff = s1->staff;
      stem_xoff = s1->as.u.note.grace ? GSTEM_XOFF : STEM_XOFF;
      for (s = s1; ; s = s->next) {
            if (s->type != NOTE)
                  continue;
            s->xs = s->x;
            if (s->stem > 0)
                  s->xs +=  stem_xoff + s->shhd[0];
            else  s->xs += -stem_xoff + s->shhd[s->nhd];
            if (s->nflags > nflags)
                  nflags = s->nflags;
            notes++;
            if (s->staff != staff)
                  two_staves = 1;
            if (s->as.u.note.word_end)
                  break;
      }

      if ((s2 = s) == 0) {
            error(1, s1, "No beam end!");
            return 0;
      }

      bm->s2 = s2;                  /* (don't display the flags) */
      if (bm->staff >= 0) {         /* staves not defined */
            if (two_staves)
                  return 0;
            bm->staff = staff;
      } else {                /* staves defined */
            if (!two_staves && !s1->as.u.note.grace)
                  return 0;
      }

      /* make the beam to follow the note pitches */
      if (s1->stem * s2->stem > 0) {
            a = s1->y - s2->y;
            b = s1->ys - s2->ys;
            if (b != a) {
                  if ((s1->stem > 0 && b < a)
                      || (s1->stem < 0 && b > a))
                        s1->ys = s2->ys + a;
                  else  s2->ys = s1->ys - a;
            }
      }

      sx = sy = sxx = sxy = syy = 0;      /* linear fit through stem ends */
      for (s = s1; ; s = s->next) {
            if (s->type != NOTE)
                  continue;
            x = s->xs;
            y = s->ys + staff_tb[s->staff].y;
            sx += x; sy += y;
            sxx += x * x; sxy += x * y; syy += y * y;
            if (s == s2)
                  break;
      }

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

      /* the next few lines modify the slope of the beam */
      if (!s1->as.u.note.grace) {
            if (notes >= 3) {
                  float hh;

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

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

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

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

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

      /* have flat beams when asked */
      if (cfmt.flatbeams
          && voice_tb[s1->voice].key.bagpipe) {
            if (!s1->as.u.note.grace)
                  b = -11 + staff_tb[s1->staff].y;
            else  b = 35 + staff_tb[s1->staff].y;
            a = 0;
      }

/*fixme: have a look again*/
      /* have room for the symbols in the staff */
      max_stem_err = 0;       /* check stem lengths */
      voice = s1->voice;
      s = s1;
      if (two_staves)
            /*fixme: to do*/
            ;
      else if (!s1->as.u.note.grace) {
            while (s->ts_prev->type == NOTE
                   && s->ts_prev->time == s->time)
                  s = s->ts_prev;
            for (; s != 0 && s->time <= s2->time; s = s->ts_next) {
                  float stem_err;

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

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

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

      /* have room for the gracenotes, bars and clefs */
      for (s = s1->next; ; s = s->next) {
            struct SYMBOL *g;

            if ((g = s->grace) == 0) {
                  if (s->type == CLEF || s->type == BAR) {
                        y = a * s->x + b;
                        if (s->next->stem > 0) {
                              y = s->ymx - y
                                    + BEAM_DEPTH + BEAM_SHIFT * (nflags - 1)
                                    + 2;
                              if (y > 0)
                                    b += y;
                        } else {
                              y = s->ymn - y
                                    - BEAM_DEPTH - BEAM_SHIFT * (nflags - 1)
                                    - 7;
                              if (y < 0)
                                    b += y;
                        }
                  }
                  if (s == s2)
                        break;
                  continue;
            }
            for ( ; g != 0; g = g->next) {
                  y = a * g->x + b;
                  if (s->next->stem > 0) {
                        y = g->ys - y
                              + BEAM_DEPTH + BEAM_SHIFT * (nflags - 1)
                              + 2;
                        if (y > 0)
                              b += y;
                  } else {
                        y = g->y - y
                              - BEAM_DEPTH - BEAM_SHIFT * (nflags - 1)
                              - 7;
                        if (y < 0)
                              b += y;
                  }
            }
            if (s == s2)
                  break;
      }

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

      /* adjust final stems and rests under beam */
      for (s = s1; ; s = s->next) {
            switch (s->type) {
            case NOTE:
                  s->ys = a * s->xs + b - staff_tb[s->staff].y;
                  if (s->stem > 0) {
                        s->ymx = s->ys + 2.5;
                        if (s->dc_top < s->ys + 2)
                              s->dc_top = s->ys + 2;
                  } else {
                        s->ymn = s->ys - 2.5;
                        if (s->dc_bot > s->ys - 2)
                              s->dc_bot = s->ys - 2;
                  }
                  break;
            case REST:
                  y = a * s->x + b - staff_tb[s->staff].y;
                  if (s1->stem > 0) {
                        y -= BEAM_DEPTH + BEAM_SHIFT * (nflags - 1);
                        y -= s->head != H_FULL ? 4 : 9;
                        if (s1->multi == 0 && y > 12)
                              y = 12;
                        if (s->y <= y)
                              break;
                  } else {
                        y += BEAM_DEPTH + BEAM_SHIFT * (nflags - 1);
                        y += s->head != H_FULL ? 4 : 11;
                        if (s1->multi == 0 && y < 12)
                              y = 12;
                        if (s->y >= y)
                              break;
                  }
                  if (s->head != H_FULL) {
                        int iy;

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

      /* save beam parameters */
      bm->s1 = s1;
      bm->a = a;
      bm->b = b;
      if (!s1->as.u.note.grace)
            bm->t = s1->stem * BEAM_DEPTH;
      else  bm->t = s1->stem * 1.6;
      bm->nflags = nflags;
      return 1;
}

/* -- draw a single beam -- */
/* (the staves may be defined or not) */
static void draw_beam(float x1,
                  float x2,
                  float dy,
                  struct BEAM *bm)
{
      float y1, dy2;

      y1 = bm->a * x1 + bm->b - dy;
      dy2 = bm->a * (x2 - x1);
      PUT5("%.1f %.1f %.1f %.1f %.1f ", bm->t, x2 - x1, dy2, x1, y1);
      if (bm->staff >= 0)
            PUT1("y%d ", bm->staff);
      PUT0("bm\n");
}

/* -- draw the beams for one word -- */
/* (the staves may be defined or not) */
static void draw_beams(struct BEAM *bm)
{
      struct SYMBOL *s, *s1, *s2;
      int i, two_staves;
      float shift, bshift, bstub;

      s1 = bm->s1;
      s2 = bm->s2;
      if (!s1->as.u.note.grace) {
            bshift = BEAM_SHIFT;
            bstub = BEAM_STUB;
            shift = 0.34;           /* (half width of the stem) */
      } else {
            bshift = 3;
            bstub = 3.2;
            shift = 0.29;
      }

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

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

                  if (s->type != NOTE || s->nflags < i) {
                        if (s == s2)
                              break;
                        continue;
                  }
                  k1 = s;
                  for (;;) {
                        if (s == s2
                            || (s->next->len > 0 && s->next->nflags < i)
                            || (s->sflags & S_BEAM_BREAK))
                              break;
                        s = s->next;
                  }
                  k2 = s;
                  while (k2->type != NOTE)
                        k2 = k2->prev;
                  x1 = k1->xs;
                  if (k1 == k2) {
                        if (k1 == s1)
                              x1 += bstub;
                        else if (k1 == s2 || (k1->sflags & S_BEAM_BREAK)) {
                              x1 -= bstub;
                        } else {
                              struct SYMBOL *k;

                              k = k1->prev;
                              while (k->type != NOTE)
                                    k = k->prev;
                              if ((k->sflags & S_BEAM_BREAK)
                                  || k->nflags < k1->next->nflags
                                  || (k->nflags == k1->next->nflags
                                   && k->dots < k1->next->dots))
                                    x1 += bstub;
                              else  x1 -= bstub;
                        }
                  }
                  draw_beam(x1, k2->xs,
#if 1
                          shift * s1->stem,
#else
                          shift * k1->stem,     /*fixme: more complicated */
#endif
                          bm);

                  /* if on 2 staves, update the stem lengths */
                  if (two_staves) {
                        two_staves = k1->stem;
                        for (;;) {
                              if (k1->type == NOTE
                                  && k1->nflags == i
                                  && k1->stem != s1->stem) {
                                    k1->ys = bm->a * k1->xs + bm->b
                                        - staff_tb[k1->staff].y
                                        - bm->t;
#if 1
                                    k1->ys -= shift * s1->stem;
#else
                                    if (k1->stem != two_staves)
                                          k1->ys -= shift * two_staves;
#endif
                              }
                              if (k1 == k2)
                                    break;
                              k1 = k1->next;
                        }
                  }
                  if (s == s2)
                        break;
            }
      }
}

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

      memset(staff_d, 0, sizeof staff_d);
      n = 0;
      for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
            staff = p_voice->staff;
            if (staff_tb[staff].brace_end)
                  staff--;
            staff_p = &staff_d[staff];
            if (first_line) {
                  if ((p = p_voice->nm) == 0)
                        continue;
            } else {
                  if ((p = p_voice->snm) == 0)
                        continue;
            }
            for (;;) {
                  staff_p->v[staff_p->nl++] = p;
                  if ((p = strstr(p, "\\n")) == 0
                      || staff_p->nl >= MAXSTAFF)
                        break;
                  p += 2;
            }
            n++;
      }
      if (n == 0)
            return;
      str_font(&cfmt.voicefont);
      indent = -indent * 0.5;
      for (staff = nstaff; staff >= 0; staff--) {
            staff_p = &staff_d[staff];
            if (staff_p->nl == 0)
                  continue;
            y = staff_tb[staff].y + 12. + 9. * (staff_p->nl - 1)
                  - cfmt.voicefont.size * 0.3;
            if (staff_tb[staff].brace)
                  y -= (staff_tb[staff].y - staff_tb[staff + 1].y) * 0.5;
            for (n = 0; n < staff_p->nl; n++) {
                  p = staff_p->v[n];
                  if ((q = strstr(p, "\\n")) != 0)
                        *q = '\0';
                  PUT2("%.1f %.1f M ", indent, y);
                  put_str(p, A_CENTER);
                  y -= 18.;
                  if (q != 0)
                        *q = '\\';
            }
      }
}

/* -- draw the left side of the staves -- */
static void draw_lstaff(float x)
{
      int i;
      float xb, y, yb, ybk;

      PUT3("%.1f %.1f %.1f bar\n",
           staff_tb[0].y - staff_tb[nstaff].y + 24.,
           x, staff_tb[nstaff].y);
      xb = yb = ybk = 0;
      for (i = 0; i <= nstaff; i++) {
            y = staff_tb[i].y;
            if (staff_tb[i].brace)
                  yb = y + 24;
            if (staff_tb[i].bracket) {
                  ybk = y + 24;
                  xb = -6;
            }
            if (staff_tb[i].brace_end)
                  PUT3("%.1f %.1f %.1f brace\n",
                       yb - y, x + xb, yb);
            if (staff_tb[i].bracket_end) {
                  PUT3("%.1f %.1f %.1f bracket\n",
                       ybk - y, x, ybk);
                  xb = 0;
            }
      }
}

/* -- draw the staves and the left side -- */
void draw_staff(int first_line,
            float indent)
{
      int i;

      if (indent != 0)
            draw_vname(first_line, indent);     /* draw the voices name/subnames */

      /* draw the staves */
      for (i = nstaff; i >= 0; i--) {
            PUT2("%.1f 0 %.1f staff\n",
                 realwidth, staff_tb[i].y);
      }

      if (nstaff != 0)
            draw_lstaff(0);
}

/* -- draw the time signature -- */
static void draw_timesig(float x,
                   struct SYMBOL *s)
{
      int i, j;

      if (s->as.u.meter.wmeasure == 0)
            return;
      x = x - s->wl + 2;
      for (i = 0; i < s->as.u.meter.nmeter; i++) {
            char *f, meter[64];
            int l;
            float dx;

            if (s->as.u.meter.meter[i].top[0] == ' ') {
                  x += 6.;                /* half-space */
                  continue;
            }
            l = strlen(s->as.u.meter.meter[i].top);
            if (s->as.u.meter.meter[i].bot[0] != '\0') {
                  int l2;

                  sprintf(meter, "(%.8s)(%.2s)",
                        s->as.u.meter.meter[i].top,
                        s->as.u.meter.meter[i].bot);
                  f = "tsig";
                  l2 = strlen(s->as.u.meter.meter[i].bot);
                  if (l2 > l)
                        l = l2;
            } else {
                  if (s->as.u.meter.meter[i].top[0] == 'C') {
                        if (s->as.u.meter.meter[i].top[1] != '|')
                              f = "csig";
                        else  f = "ctsig";
                        meter[0] = '\0';
                  } else if (s->as.u.meter.meter[i].top[0] == '('
                           || s->as.u.meter.meter[i].top[0] == ')') {
                        sprintf(meter, "(\\%s)",
                              s->as.u.meter.meter[i].top);
                        f = "stsig";
                  } else {
                        sprintf(meter, "(%.8s)",
                              s->as.u.meter.meter[i].top);
                        f = "stsig";
                  }
            }
            dx = 7 * l;
            for (j = nstaff; j >= 0; j--)
                  PUT4("%s%.1f %.1f %s\n",
                       meter, x + dx * 0.5, staff_tb[j].y, f);
            x += dx;
      }
}

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

      static char sharp_cl[7] = {24, 9, 15, 21, 6, 12, 18};
      static char flat_cl[7] = {12, 18, 24, 9, 15, 21, 6};
      static signed char sharp1[6] = {-9, 12, -9, -9, 12, -9};
      static signed char sharp2[6] = {12, -9, 12, -9, 12, -9};
      static signed char flat1[6] = {9, -12, 9, -12, 9, -12};
      static signed char flat2[6] = {-12, 9, -12, 9, -12, 9};

      /* memorize the current keysig of the voice */
      memcpy(&p_voice->key, &s->as.u.key, sizeof p_voice->key);

      if (p_voice->second)
            return;

      clef_ix = 0 - 2;  /* treble */
      switch (staff_tb[staff].clef.type) {
      case ALTO:
            clef_ix = 3 - 3;
            break;
      case BASS:
            clef_ix = 6 - 4;
            break;
      }
      clef_ix += staff_tb[staff].clef.line;
      if (clef_ix < 0)
            clef_ix += 7;
      else if (clef_ix >= 7)
            clef_ix -= 7;

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

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

                  /* old sharps */
                  shift = sharp_cl[clef_ix];
                  p_seq = shift > 9 ? sharp1 : sharp2;
                  for (i = 0; i < old_sf; i++) {
                        PUT2("%.1f %.1f nt0 ", x, staffb + shift);
                        shift += *p_seq++;
                        x += 5;
                  }

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

            /* if less sharps or flats, put neutrals */
            /* sharps */
            } else if (s->as.u.key.sf > 0) {
                  if (s->as.u.key.sf < old_sf) {
                        shift = sharp_cl[clef_ix];
                        p_seq = shift > 9 ? sharp1 : sharp2;
                        for (i = 0; i < s->as.u.key.sf; i++)
                              shift += *p_seq++;
                        for (; i < old_sf; i++) {
                              PUT2("%.1f %.1f nt0 ", x, staffb + shift);
                              shift += *p_seq++;
                              x += 5;
                        }
                        x += 3;           /* extra space */
                  }
            /* flats */
            } else /*if (s->as.u.key.sf < 0)*/ {
                  if (s->as.u.key.sf > old_sf) {
                        shift = flat_cl[clef_ix];
                        p_seq = shift < 18 ? flat1 : flat2;
                        for (i = 0; i > s->as.u.key.sf; i--)
                              shift += *p_seq++;
                        for (; i > old_sf; i--) {
                              PUT2("%.1f %.1f nt0 ", x, staffb + shift);
                              shift += *p_seq++;
                              x += 5;
                        }
                        x += 3;           /* extra space */
                  }
            }

            /* new sharps */
            shift = sharp_cl[clef_ix];
            p_seq = shift > 9 ? sharp1 : sharp2;
            for (i = 0; i < s->as.u.key.sf; i++) {
                  PUT2("%.1f %.1f sh0 ", x, staffb + shift);
                  shift += *p_seq++;
                  x += 5;
            }

            /* new flats */
            shift = flat_cl[clef_ix];
            p_seq = shift < 18 ? flat1 : flat2;
            for (i = 0; i > s->as.u.key.sf; i--) {
                  PUT2("%.1f %.1f ft0 ", x, staffb + shift);
                  shift += *p_seq++;
                  x += 5;
            }
      } else {
            int last_acc, last_shift;

            /* explicit accidentals */
            last_acc = s->as.u.key.accs[0];
            last_shift = 100;
            for (i = 0; i < s->as.u.key.nacc; i++) {
                  char *p;

                  if (s->as.u.key.accs[i] != last_acc) {
                        last_acc = s->as.u.key.accs[i];
                        x += 3;
                  }
                  switch (last_acc & 0x0f) {
                  default:
                        p = "sh";
                        break;
                  case A_FT:
                  case A_DF:
                        p = "ft";
                        break;
                  case A_NT:
                        p = "nt";
                        break;
                  }
                  shift = clef_ix * 6
                        + 3 * (s->as.u.key.pits[i] - 18);
                  while (shift < -3)
                        shift += 21;
                  while (shift >= 24 + 3)
                        shift -= 21;
                  if (shift == last_shift + 21
                      || shift == last_shift - 21)
                        x -= 5;           /* same note */
                  last_shift = shift;
                  PUT4("%.1f %.1f %s%d ", x, staffb + shift,
                       p, micro_tb[last_acc >> 4]);
                  x += 5;
            }
      }
      if (old_sf != 0 || s->as.u.key.sf != 0 || s->as.u.key.nacc != 0)
            PUT0("\n");
}

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

      dotted = s->as.u.bar.dotted;
      bar_type = s->as.u.bar.type;
      switch (bar_type) {
      case B_OBRA:
      case B_CBRA:
      case (B_OBRA << 4) + B_CBRA:
            return;
      case B_COL:
            dotted = 1;
            bar_type = B_BAR;
            break;
      case (B_CBRA << 4) + B_BAR:
            bar_type = B_BAR;
            break;
      case (B_BAR << 4) + B_COL:
            bar_type |= (B_OBRA << 8);
            break;
      case (B_BAR << 8) + (B_COL << 4) + B_COL:
            bar_type |= (B_OBRA << 12);
            break;
      case (B_BAR << 12) + (B_COL << 8) + (B_COL << 4) + B_COL:
            bar_type |= (B_OBRA << 16);
            break;
      case (B_COL << 4) + B_BAR:
      case (B_COL << 8) + (B_COL << 4) + B_BAR:
      case (B_COL << 12) + (B_COL << 8) + (B_COL << 4) + B_BAR:
            bar_type <<= 4;
            bar_type |= B_CBRA;
            break;
      case (B_COL << 4) + B_COL:
            bar_type = (B_COL << 12) + (B_CBRA << 8) + (B_OBRA << 4) + B_COL;
            break;
      }
      for (;;) {
            psf = "bar";
            switch (bar_type & 0x0f) {
            case B_BAR:
                  if (dotted)
                        psf = "dotbar";
                  break;
            case B_OBRA:
            case B_CBRA:
                  psf = "thbar";
                  x -= 3;
                  break;
            case B_COL:
                  x -= 2;
                  break;
            }
            switch (bar_type & 0x0f) {
            default:
                  stafft = staff_tb[0].y + 24.; /* top of upper staff */
                  for (staff = 0; ; staff++) {
                        if (staff_tb[staff].stop_bar
                            || staff == nstaff) {
                              y = staff_tb[staff].y;
                              PUT4("%.1f %.1f %.1f %s ",
                                   stafft - y, x, y, psf);
                              if (staff == nstaff)
                                    break;
                              stafft = staff_tb[staff + 1].y + 24.;
                        }
                  }
                  break;
            case B_COL:
                  for (staff = nstaff; staff >= 0; staff--)
                        PUT2("%.1f %.1f rdots ",
                             x + 1, staff_tb[staff].y);
                  break;
            }
            bar_type >>= 4;
            if (bar_type == 0)
                  break;
            x -= 3;
      }
      PUT0("\n");
}

/* -- draw a rest -- */
/* (the staves are defined) */
static void draw_rest(struct SYMBOL *s)
{
      int i, y;
      float x, dotx, staffb;

static char *rest_tb[NFLAGS_SZ] = {
      "r128", "r64", "r32", "r16", "r8",
      "r4",
      "r2", "r1", "r0", "r00"
};

      x = s->x + s->shhd[0];
      y = s->y;

      /* if rest alone in the measure, center */
      if (s->len == voice_tb[s->voice].meter.wmeasure) {
            struct SYMBOL *prev;

            if (s->next != 0)
                  x = s->next->x;
            else  x = realwidth;
            prev = s->prev;
            while (prev->type == TEMPO || prev->type == PART)
                  prev = prev->prev;
            if (prev->type != BAR && voice_tb[s->voice].second) {
                  for (prev = prev->ts_next; ; prev = prev->ts_next) {
                        switch (prev->type) {
                        case CLEF:
                        case KEYSIG:
                        case TIMESIG:
                              continue;
                        default:
                              break;
                        }
                        prev = prev->ts_prev;
                        break;
                  }
            }
            x = (x + prev->x) * 0.5;

            /* center the associated decorations */
            if (s->as.u.note.dc.n > 0)
                  deco_update(s, x - s->x);
      }

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

      if (s->sflags & S_OTHER_HEAD) {
            draw_all_deco_head(s, x, y + staffb);
            return;
      }

      i = C_XFLAGS - s->nflags;           /* rest_tb index */
      PUT3("%.1f %.1f %s", x, y + staffb, rest_tb[i]);

      /* add helper line(s) */
      switch (i) {
      case 9:                             /* breve / longa */
      case 8:
            if (y >= 24)
                  PUT1(" %.1f hl", y + 6 + staffb);
            if (i == 8) {
                  if (y <= -6)
                        PUT1(" %.1f hl", y + staffb);
            } else {
                  if (y <= 0)
                        PUT1(" %.1f hl", y - 6 + staffb);
            }
            break;
      case 7:                             /* semibreve */
            if (y < -6
                || y >= 24)
                  PUT1(" %.1f hl", y + 6 + staffb);
            break;
      case 6:                             /* minim */
            if (y <= -6
                || y >= 30)
                  PUT1(" %.1f hl", y + staffb);
            break;
      }

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

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

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

      /* draw the notes */
      bm.s2 = 0;
      bm.staff = -1;                      /* staves defined */
      for (g = s->grace; ; g = g->next) {
            if (s->grace->next != 0) {    /* many notes */
                  if ((g->sflags & S_WORD_ST)
                      && !g->as.u.note.word_end) {
                        if (calculate_beam(&bm, g))
                              draw_beams(&bm);
                  }
            }
            draw_note(g->x, g, bm.s2 == 0);
            if (g == bm.s2)
                  bm.s2 = 0;

            if (g->as.u.note.sappo) {     /* (on 1st note only) */
                  if (g->next == 0) {     /* if one note */
                        x1 = 9;
                        y1 = g->stem > 0 ? 5 : -5;
                  } else {          /* many notes */
                        x1 = (g->next->x - g->x) * 0.5 + 4;
                        y1 = (g->ys + g->next->ys) * 0.5 - g->y;
                        if (g->stem > 0)
                              y1 -= 1;
                        else  y1 += 1;
                  }
                  PUT3("%.1f %.1f g%ca\n",
                       x1, y1, g->stem > 0 ? 'u' : 'd');
            }
            if (g->next == 0)
                  break;
      }

      /* slur */
      if (voice_tb[s->voice].key.bagpipe  /* no slur when bagpipe */
          || !cfmt.graceslurs
          || s->as.u.note.slur_st         /* explicit slur */
          || s->next == 0
          || s->next->type != NOTE)
            return;
      last = g;
      yy = g->y;
      for (g = s->grace; g != 0; g = g->next) {
            if (g->y < yy) {
                  yy = g->y;
                  last = g;
            }
      }
      x0 = last->x;
      y0 = (last->stem >= 0 ? last->y : last->ys) - 5;
      if (s->grace != last) {
            x0 -= 4;
            y0 += 1;
      }
      s = s->next;
      x3 = s->x - 1;
      if (s->stem < 0)
            x3 -= 4;
      y3 = 3 * (s->pits[0] - 18) - 5;
      dy1 = (x3 - x0) * 0.4;
      if (dy1 > 3)
            dy1 = 3;
      dy2 = dy1;
      bet1 = 0.2;
      bet2 = 0.8;
      if (y0 > y3 + 7) {
            x0 = last->x - 1;
            y0 += 0.5;
            y3 += 6.5;
            x3 = s->x - 5.5;
            dy2 = (y0 - y3) * 0.2;
            dy1 = (y0 - y3) * 0.8;
            bet1 = 0;
      } else if (y3 > y0 + 4) {
            y3 = y0 + 4;
            x0 = last->x + 2;
            y0 = (last->stem >= 0 ? last->y : last->ys) - 4;
      }

      x1 = bet1 * x3 + (1 - bet1) * x0;
      y1 = bet1 * y3 + (1 - bet1) * y0 - dy1;
      x2 = bet2 * x3 + (1 - bet2) * x0;
      y2 = bet2 * y3 + (1 - bet2) * y0 - dy2;

      PUT4("%.1f %.1f %.1f %.1f",
           x1, y1 + staffb, x2, y2 + staffb);
      PUT4(" %.1f %.1f %.1f %.1f gsl\n",
           x3, y3 + staffb, x0, y0 + staffb);
}

/* -- draw m-th head with accidentals and dots -- */
/* (the staves are defined) */
static void draw_basic_note(float x,
                      struct SYMBOL *s,
                      int m)
{
      int i, y, no_head;
      float staffb;
      char *p;
      int head, dots, nflags;
static char *acc_tb[] = { "", "sh", "nt", "ft", "dsh", "dft" };

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

      /* draw the note decorations */
      no_head = (s->sflags & S_OTHER_HEAD);
      if (no_head)
            draw_all_deco_head(s, x + s->shhd[m], y + staffb);
      if (s->as.u.note.decs[m] != 0) {
            int n;

            i = s->as.u.note.decs[m] >> 3;            /* index */
            n = i + (s->as.u.note.decs[m] & 0x07);    /* # deco */
            for ( ; i < n; i++)
                  no_head |= draw_deco_head(s->as.u.note.dc.t[i],
                                      x + s->shhd[m],
                                      y + staffb);
      }

      /* special case when no head */
      if (s->sflags & S_NO_HEAD) {
            if ((m == 0 && s->stem > 0)
                || (m == s->nhd && s->stem < 0)) {
                  PUT2("/x %.1f def/y %.1f def",      /* set x y */
                       x + s->shhd[m], y + staffb);
                  return;
            }
      }

      identify_note(s, s->as.u.note.lens[m],
                  &head, &dots, &nflags);

      /* draw the head */
      PUT2("%.1f %.1f ", x + s->shhd[m], y + staffb);
      if (no_head)
            p = "/y exch def/x exch def";
      else if (s->as.u.note.grace)
            p = "ghd";
      else if (s->as.u.note.accs[m]
             && voice_tb[s->voice].clef.type == PERC) {
            if (s->as.u.note.accs[m] == A_FT)
                  p = "pflhd";
            else  p = "pshhd";
      } else {
            switch (head) {
            case H_SQUARE:
                  if (s->as.u.note.lens[m] < BREVE * 2)
                        p = "breve";
                  else  p = "longa";
                  break;
            case H_OVAL:
                  if (s->as.u.note.lens[m] < BREVE)
                        p = "HD";
                  else  p = "HDD";
                  break;
            case H_EMPTY:
                  p = "Hd"; break;
            default:
                  p = "hd"; break;
            }
      }
      PUT0(p);

      /* add a helper line if horizontal shift */
      if (s->shhd[m]) {
            int yy;

            yy = 0;
            if (y >= 30) {
                  yy = y;
                  if (yy % 6)
                        yy -= 3;
            } else if (y <= -6) {
                  yy = y;
                  if (yy % 6)
                        yy += 3;
            }
            if (yy)
                  PUT1(" %.1f hl", yy + staffb);
      }

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

            dotx = 8. + s->xmx;
            if (y % 6)
                  doty = 0;
            else  {
                  if ((doty = s->doty) == 0) {  /* defined when lowest voice */
                        if (m < s->nhd && s->pits[m] == s->pits[m + 1] - 1)
                              doty = -3;
                        else  doty = 3;
                  }
            }
            for (i = 0; i < dots; i++) {
                  PUT2(" %.1f %d dt", x + dotx, doty);
                  dotx += 3.5;
            }
      }

      /* draw the accidental */
      if (s->as.u.note.accs[m]
          && voice_tb[s->voice].clef.type != PERC) {
            i = s->as.u.note.accs[m];
            p = acc_tb[i & 0x0f];
            i = micro_tb[i >> 4];
            x -= s->shac[m];
            if (!s->as.u.note.grace)
                  PUT3(" %.1f y %s%d", x, p, i);
            else  PUT3(" %.1f gsc %s%d grestore", x, p, i);
      }
}

/* -- draw a note or a chord -- */
/* (the staves are defined) */
static void draw_note(float x,
                  struct SYMBOL *s,
                  int fl)
{
      int i, m, ma, y;
      float staffb;
      char *hltype;

      staffb = staff_tb[s->staff].y;

      /* draw the master note - can be only the first or the last note */
      if (s->stem >= 0)
            ma = 0;
      else  ma = s->nhd;
      draw_basic_note(x, s, ma);          /* draw the note head */
      if (!s->as.u.note.stemless) {       /* add stem and flags */
            char  c, c2;
            float slen;

            c = s->stem >= 0 ? 'u' : 'd';
            slen = s->ys - s->y;
            if (!fl || s->nflags <= 0) {        /* stem only */
                  c2 = s->as.u.note.grace ? 'g' : 's';
                  if (s->nflags > 0) {    /* (fix for PS low resolution) */
                        if (s->stem >= 0)
                              slen -= 1;
                        else  slen += 1;
                  }
                  PUT3(" %.1f %c%c", slen, c2, c);
            } else {                      /* stem and flags */
                  if (voice_tb[(int) s->voice].key.bagpipe
                      && cfmt.straightflags)
                        c = 's';          /* straight flag */
                  c2 = s->as.u.note.grace ? 'g' : 'f';
                  PUT4(" %d %.1f s%c%c", s->nflags, slen, c2, c);
            }
      }

      if (s->as.u.note.grace)
            hltype = "ghl";
      else {
            switch (s->head) {
            default:
                  hltype = "hl";
                  break;
            case H_OVAL:
                  hltype = "hl1";
                  break;
            case H_SQUARE:
                  hltype = "hl2";
                  break;
            }
      }
      y = 3 * (s->pits[0] - 18);          /* lower helper lines */
      for (i = -6; i >= y; i -= 6)
            PUT2(" %.1f %s", i + staffb, hltype);
      y = 3 * (s->pits[s->nhd] - 18);           /* upper helper lines */
      for (i = 30; i <= y; i += 6)
            PUT2(" %.1f %s", i + staffb, hltype);

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

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

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

static struct SYMBOL *prev_scut(struct SYMBOL *s)
{
      struct SYMBOL *sym;
      int voice;

      voice = s->voice;
      for (s = s->prev ; s != 0; s = s->prev) {
            if (s->type == BAR
                && ((s->sflags & S_RRBAR)
                  || s->as.u.bar.type == B_THIN_THICK
                  || s->as.u.bar.type == B_THICK_THIN
                  || (s->as.u.bar.repeat_bar
                      && s->as.text != 0
                      && s->as.text[0] != '1')))
                  return s;
      }

      /* return sym before first note/rest/bar */
      sym = voice_tb[voice].sym;
      for (s = sym->next; s != 0; s = s->next) {
            switch (s->type) {
            case NOTE:
            case REST:
            case BAR:
                  return s->prev;
            }
      }
      return sym;
}

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

      some_upstem = low = 0;
      for (s = k1; ; s = s->next) {
            if (s->type == NOTE) {
                  if (!s->as.u.note.stemless) {
                        if (s->stem < 0)
                              return 1;
                        some_upstem = 1;
                  }
                  if (s->pits[0] < 22)    /* if under middle staff */
                        low = 1;
            }
            if (s == k2)
                  break;
      }
      if (!some_upstem && !low)
            return 1;
      return -1;
}

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

      alfa = 0.3;
      beta = 0.45;

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

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

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

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

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

      dx = 0.03 * (x2 - x1);
      if (dx > 10.)
            dx = 10.;
      dy = s;
      dz = 0.2 + 0.001 * (x2 - x1);
      if (dz > 0.6)
            dz = 0.6;
      dz *= s;

      if (staff < 0) {
            PUT4("%.1f %.1f %.1f %.1f ", 
                 xx2 - dx, yy2 + dy, xx1 + dx, yy1 + dy);
            PUT3("%.1f %.1f 0 %.1f ", x1, y1 + dz, dz);
            PUT4("%.1f %.1f %.1f %.1f ", xx1, yy1, xx2, yy2);
            PUT4("%.1f %.1f %.1f %.1f", x2, y2, x1, y1);
      } else {
            PUT3("%.1f %.1f y%d ",
                 xx2 - dx, yy2 + dy, staff);
            PUT3("%.1f %.1f y%d ",
                 xx1 + dx, yy1 + dy, staff);
            PUT4("%.1f %.1f y%d 0 %.1f ",
                  x1, y1 + dz, staff, dz);
            PUT3("%.1f %.1f y%d ",
                 xx1, yy1, staff);
            PUT3("%.1f %.1f y%d ",
                 xx2, yy2, staff);
            PUT3("%.1f %.1f y%d ",
                 x2, y2, staff);
            PUT3("%.1f %.1f y%d",
                 x1, y1, staff);
      }
      if (!dotted)
            PUT0(" SL\n");
      else  PUT0(" dSL\n");
}

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

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

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

      nn = 1;
      upstaff = k1->staff;
      two_staves = 0;
      if (k1 != k2)
          for (k = k1->next; k != 0; k = k->next) {
            if (k->len > 0) { /* note or rest */
                  nn++;
                  if (k->staff != upstaff) {
                        two_staves = 1;
                        if (k->staff < upstaff)
                              upstaff = k->staff;
                  }
            }
            if (k == k2)
                  break;
      }
/*fixme: KO when two staves*/
if (two_staves) fprintf(stderr, "*** multi-staves slurs not treated\n");

      /* fix endpoints */
      x1 = k1->x + k1->xmx;         /* take the max right side */
      if (k1 != k2)
            x2 = k2->x;
      else  x2 = realwidth;         /* (the slur starts on last note of the line) */
      y1 = s > 0 ? k1->dc_top + 2 : k1->dc_bot - 2;
      y2 = s > 0 ? k2->dc_top + 2 : k2->dc_bot - 2;

      if (k1->type == NOTE) {
            if (s > 0) {
                  if (k1->stem > 0) {
                        x1 += 5;
                        if (k1->as.u.note.word_end
                            && k1->nflags >= -1) {
                              if (k1->nflags > 0) {
                                    x1 += 2;
                                    y1 = k1->ys - 3;
                              } else      y1 = k1->ys - 6;
                        }
                  }
            } else {
                  if (k1->stem < 0) {
                        x1 -= 2;
                        if (k1->as.u.note.word_end
                            && k1->nflags >= -1) {
                              if (k1->nflags > 0) {
                                    x1 += 2;
                                    y1 = k1->ys + 3;
                              } else      y1 = k1->ys + 6;
                        }
                  }
            }
      }

      if (k2->type == NOTE) {
            if (s > 0) {
                  if (k2->stem > 0) {
                        x2 += 2;
                        if ((k2->sflags & S_WORD_ST)
                            && k2->nflags >= -1)
                              y2 = k2->ys - 6;
                  }
            } else {
                  if (k2->stem < 0) {
                        x2 -= 5;
                        if ((k2->sflags & S_WORD_ST)
                            && k2->nflags >= -1)
                              y2 = k2->ys + 6;
                  }
            }
      }

      if (k1->type != NOTE) {
            y1 = y2 + 1.2 * s;
            x1 = k1->x + k1->wr * 0.5;
            if (x1 > x2 - 12)
                  x1 = x2 - 12;
      }

      if (k2->type != NOTE) {
            y2 = y1 + 1.2 * s;
            if (k1 != k2)
                  x2 = k2->x - k2->wl * 0.3;
      }

      if (nn >= 3) {
            if (s > 0) {
                  if (y1 < k1->next->dc_top - 2
                      && k1->next->x < x1 + 48)
                        y1 = k1->next->dc_top - 2;
                  if (y2 < k2->prev->dc_top - 2
                      && k2->prev->x > x2 - 48)
                        y2 = k2->prev->dc_top - 2;
            } else {
                  if (y1 > k1->next->dc_bot + 2
                      && k1->next->x < x1 + 48)
                        y1 = k1->next->dc_bot + 2;
                  if (y2 > k2->prev->dc_bot + 2
                      && k2->prev->x > x2 - 48)
                        y2 = k2->prev->dc_bot + 2;
            }
      }

      /* shift endpoints */
      addx = 0.04 * (x2 - x1);
      if (addx > 3.0)
            addx = 3.0;
      addy = 0.01 * (x2 - x1);
      if (addy > 3.0)
            addy = 3.0;
      x1 += addx;
      x2 -= addx;

/*fixme: to simplify*/
      if (k1->staff == upstaff)
            y1 += s * addy;
      else  y1 = -6.;
      if (k2->staff == upstaff)
            y2 += s * addy;
      else  y2 = -6.;

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

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

      /* special case for grace notes */
      if (k1->as.u.note.grace)
            x1 = k1->x - GSTEM_XOFF * 0.5;
      if (k2->as.u.note.grace)
            x2 = k2->x + GSTEM_XOFF * 1.5;

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

                  for (g = k->grace; g != 0; g = g->next) {
                        y = g->y - a * k->x - addy;
                        if (s > 0) {
                              y += GSTEM + 2;
                              if (y > h)
                                    h = y;
                        } else {
                              y -= 2;
                              if (y < h)
                                    h = y;
                        }
                  }
                  break;
                }
            }
      }

      y1 += 0.45 * h;
      y2 += 0.45 * h;
      h *= 0.65;

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

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

/*fixme: ugly!*/
      if (m1 >= 0)
            y1 = 3 * (k1->pits[m1] - 18) + 5 * s;
      if (m2 >= 0)
            y2 = 3 * (k2->pits[m2] - 18) + 5 * s;

      slur_out(x1, y1, x2, y2, s,
             height, dotted, upstaff);

      /* have room for other symbols */
      a = (y2 - y1) / (x2 - x1);
/*---fixme: it seems to work with 0.4, but why?*/
      addy = y1 - a * x1 + 0.4 * height;
      for (k = k1; ; k = k->next) {
            if (k == k2
                && k->as.u.note.slur_st)
                  break;      /* the next slur will set the top/bottom */
            if (k->staff == upstaff) {
                  y = a * k->x + addy;
                  if (k->dc_top < y)
                        k->dc_top = y;
                  else if (k->dc_bot > y)
                        k->dc_bot = y;
            }
            if (k == k2)
                  break;
      }
}

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

      for (;;) {
            again = 0;
            gr1 = gr2 = 0;
            s = first;
            for (;;) {
                  if (s == 0 || s == last) {
                        if (gr1 == 0
                            || (s = gr1->next) == 0
                            || s == last)
                              break;
                        gr1 = 0;
                  }
                  if (s->type == GRACE) {
                        gr1 = s;
                        s = s->grace;
                        continue;
                  }
                  if ((s->type != NOTE && s->type != REST)
                      || (s->as.u.note.slur_st == 0
                        && !(s->sflags & S_SL1))) {
                        s = s->next;
                        continue;
                  }
                  k = 0;                  /* find matching slur end */
                  s1 = s->next;
                  gr1_out = 0;
                  for (;;) {
                        if (s1 == 0) {
                              if (gr2 != 0) {
                                    s1 = gr2->next;
                                    gr2 = 0;
                                    continue;
                              }
                              if (gr1 == 0 || gr1_out)
                                    break;
                              s1 = gr1->next;
                              gr1_out = 1;
                              continue;
                        }
                        if (s1->type == GRACE) {
                              gr2 = s1;
                              s1 = s1->grace;
                              continue;
                        }
                        if (s1->type == BAR
                            && ((s1->sflags & S_RRBAR)
                              || s1->as.u.bar.type == B_THIN_THICK
                              || s1->as.u.bar.type == B_THICK_THIN
                              || (s1->as.u.bar.repeat_bar
                                  && s1->as.text != 0
                                  && s1->as.text[0] != '1'))) {
                              k = s1;
                              break;
                        }
                        if (s1->type != NOTE && s1->type != REST) {
                              s1 = s1->next;
                              continue;
                        }
                        if (s1->as.u.note.slur_end
                            || (s1->sflags & S_SL2)) {
                              k = s1;
                              break;
                        }
                        if (s1->as.u.note.slur_st
                            || (s1->sflags & S_SL1)) {
                              again++;
                              break;
                        }
                        if (s1 == last)
                              break;
                        s1 = s1->next;
                  }
                  if (s1 == 0)
                        k = next_scut(s);
                  else if (k == 0) {
                        s = s1;
                        if (s == last)
                              break;
                        continue;
                  }

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

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

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

/* -- draw a tuplet -- */
/* (the staves are not yet defined) */
/* See 'tuplets' in format.txt about the value of 'u' */
static struct SYMBOL *draw_tuplet(struct SYMBOL *t)
{
      struct SYMBOL *s1, *s2, *sy, *next;
      int r, upstaff, nb_only, some_slur;
      float x1, x2, y1, y2, xm, ym, s, s0, yy, yx, dy;

      next = t;
      if ((t->u & 0x0f00) == 0x100)       /* if 'when' == never */
            return next;

      /* search the first and last notes/rests of the tuplet */
      r = t->as.u.tuplet.r_plet;
      s1 = 0;
      some_slur = 0;
      upstaff = t->staff;
      for (s2 = t->next; s2 != 0; s2 = s2->next) {
            if (s2->type != NOTE && s2->type != REST) {
                  if (s2->type == TUPLET) {
                        sy = draw_tuplet(s2);   /* nested tuplet */
                        if (sy->time > next->time)
                              next = sy;
                  } else if (s2->type == GRACE) {
                        for (sy = s2->grace; sy != 0; sy = sy->next) {
                              if (sy->as.u.note.slur_st
                                  || (sy->sflags & S_SL1))
                                    some_slur = 1;
                        }
                  }
                  continue;
            }
            if (s2->as.u.note.slur_st
                || (s2->sflags & S_SL1))
                  some_slur = 1;
            if (s2->staff < upstaff)
                  upstaff = s2->staff;
            if (s1 == 0)
                  s1 = s2;
            if (--r <= 0)
                  break;
      }
      if (s2 == 0)
            return next;                  /* no solution... */
      if (s2->time > next->time)
            next = s2;

      /* draw the slurs when inside the tuplet */
      if (some_slur)
            draw_slurs(s1, s2);
      if ((t->u & 0x0f0) == 0x10) { /* 'what' == slur */
            nb_only = 1;
            draw_slur(s1, s2, -1, -1, 
                    s1->stem > 0 ? SL_ABOVE : SL_BELOW, 0);
      } else {

            /* search if a bracket is needed */
            if ((t->u & 0x0f00) == 0x200) /* if 'when' == always */
                  nb_only = 0;
            else {
                  nb_only = s1->type == NOTE && s2->type == NOTE;
                  if (!((s1->sflags & S_WORD_ST) || (s1->prev->sflags & S_BEAM_BREAK)
                         || s1->prev->nflags < s1->nflags)
/*                       || s1->prev->as.u.note.word_end) */
                      || !(s2->as.u.note.word_end || (s2->sflags & S_BEAM_BREAK)))
                        nb_only = 0;
            }
            for (sy = s1; ; sy = sy->next) {
                  if (sy->len == 0) {     /* not a note or a rest */
                        if (sy->type != GRACE)
                              nb_only = 0;
                        if (sy == s2)
                              break;
                        continue;
                  }
                  if (sy == s2)
                        break;
                  if (sy->as.u.note.word_end || (sy->sflags & S_BEAM_BREAK))
                        nb_only = 0;
            }
      }

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

            if ((t->u & 0x0f) == 1)       /* if 'value' == none */
                  return next;
            a = (s2->ys - s1->ys) / (s2->x - s1->x);
            b = s1->ys - a * s1->x;
            if (s1->stem > 0) {
/*                if (s1->multi) { */
                        for (sy = s1; ; sy = sy->next) {
                              yy = a * sy->x + b;
                              if (sy->dc_top > yy)
                                    b += sy->dc_top - yy;
                              if (sy == s2)
                                    break;
                        }
/*                } */
                  b += 4.;
            } else {
/*                if (s1->multi) { */
                        for (sy = s1; ; sy = sy->next) {
                              yy = a * sy->x + b;
                              if (sy->dc_bot < yy)
                                    b += sy->dc_bot - yy;
                              if (sy == s2)
                                    break;
                        }
/*                } */
                  b -= 12.;
            }
            xm = (s2->x + s1->x) * 0.5;
            if (s1->stem * s2->stem > 0) {
                  if (s1->stem > 0)
                        xm += GSTEM_XOFF;
                  else  xm -= GSTEM_XOFF;
            }
            ym = a * xm + b;
            if ((t->u & 0x0f) == 0)       /* if 'value' == number */
                  PUT4("(%d)%.1f %.1f y%d bnum\n",
                       t->as.u.tuplet.p_plet, xm, ym, s1->staff);
            else  PUT5("(%d:%d)%.1f %.1f y%d bnum\n",
                       t->as.u.tuplet.p_plet, t->as.u.tuplet.q_plet,
                       xm, ym, s1->staff);

            if (s1->stem > 0) {
                  b += 8.;
                  for (sy = s1; ; sy = sy->next) {
                        yy = a * sy->x + b;
                        if (sy->dc_top < yy)
                              sy->dc_top = yy;
                        if (sy == s2)
                              break;
                  }
            } else {
                  for (sy = s1; ; sy = sy->next) {
                        yy = a * sy->x + b;
                        if (sy->dc_bot > yy)
                              sy->dc_bot = yy;
                        if (sy == s2)
                              break;
                  }
            }
            return next;
      }

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

/*fixme: two staves not treated*/
/*fixme: to optimize*/
    if (s1->multi >= 0) {

      /* sole or upper voice: the bracket is above the staff */
      x1 = s1->x - 4.;
      x2 = s2->x + 4.;
      y1 = 24;
      if (s1->staff == upstaff) {
            sy = s1;
            if (sy->type != NOTE) {
                  for (sy = sy->next; sy != s2; sy = sy->next)
                        if (sy->type == NOTE)
                              break;
            }
            if (sy->dc_top > 24)
                  y1 = sy->dc_top;
            if (s1->stem > 0)
                  x1 += 3.;
      }
      y2 = 24;
      if (s2->staff == upstaff) {
            sy = s2;
            if (sy->type != NOTE) {
                  for (sy = sy->prev; sy != s1; sy = sy->prev)
                        if (sy->type == NOTE)
                              break;
            }
            if (sy->dc_top > 24)
                  y2 = sy->dc_top;
            if (s2->stem > 0)
                  x2 += 3.;
      }

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

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

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

      ym += dy + 4.;
      y1 = ym + s * (x1 - xm);
      y2 = ym + s * (x2 - xm);
      PUT5("%.1f %.1f %.1f %.1f y%d tubr",
           x2 - x1, y2 - y1, x1, y1 + 4., upstaff);

      /* shift the slurs / decorations */
      ym += 8.;
      for (sy = s1; ; sy = sy->next) {
            if (sy->staff == upstaff) {
                  yy = ym + (sy->x - xm) * s;
                  if (sy->dc_top < yy)
                        sy->dc_top = yy;
            }
            if (sy == s2)
                  break;
      }

    } else {      /* lower voice of the staff: the bracket is below the staff */
/*fixme: think to all that again..*/
      x1 = s1->x - 8.;
/*fixme: the note may be shifted to the right*/
      x2 = s2->x;
      if (s1->staff == upstaff) {
            sy = s1;
            if (sy->type != NOTE) {
                  for (sy = sy->next; sy != s2; sy = sy->next)
                        if (sy->type == NOTE)
                              break;
            }
            y1 = sy->dc_bot;
      } else      y1 = 0;
      if (s2->staff == upstaff) {
            sy = s2;
            if (sy->type != NOTE) {
                  for (sy = sy->prev; sy != s1; sy = sy->prev)
                        if (sy->type == NOTE)
                              break;
            }
            y2 = sy->dc_bot;
      } else      y2 = 0;

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

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

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

      ym += dy - 12.;
      y1 = ym + s * (x1 - xm);
      y2 = ym + s * (x2 - xm);
      PUT5("%.1f %.1f %.1f %.1f y%d tubrl",
           x2 - x1, y2 - y1, x1, y1 + 4., upstaff);

      /* shift the slurs / decorations */
      for (sy = s1; ; sy = sy->next) {
            if (sy->staff == upstaff) {
                  yy = ym + (sy->x - xm) * s;
                  if (sy->dc_bot > yy)
                        sy->dc_bot = yy;
            }
            if (sy == s2)
                  break;
      }
    } /* lower voice */

      if ((t->u & 0x0f) == 1) {     /* if 'value' == none */
            PUT0("\n");
            return next;
      }
      yy = 0.5 * (y1 + y2);
      if ((t->u & 0x0f) == 0)       /* if 'value' == number */
            PUT4("(%d)%.1f %.1f y%d bnumb\n",
                 t->as.u.tuplet.p_plet, xm, yy, upstaff);
      else  PUT5("(%d:%d)%.1f %.1f y%d bnumb\n",
                 t->as.u.tuplet.p_plet, t->as.u.tuplet.q_plet,
                 xm, yy, upstaff);
      return next;
}

/* -- draw the ties between two notes/chords -- */
static void draw_note_ties(struct SYMBOL *k1,
                     struct SYMBOL *k2,
                     int ntie,
                     int *mhead1,
                     int *mhead2,
                     int dotted,
                     int job)
{
      int i, s0, s1, s;
      float x1, x2, h;

      s1 = 0;
      if ((s0 = k1->multi) == 0
          && (s0 = k2->multi) == 0)
            s1 = slur_direction(k1, k2);
      for (i = 0; i < ntie; i++) {
            int m1, m2, p1, p2, y1, y2;

            m1 = mhead1[i];
            p1 = k1->pits[m1];
            m2 = mhead2[i];
            p2 = k2->pits[m2];
            if (k1->as.u.note.ti1[m1] == SL_ABOVE)
                  s = 1;
            else if (k1->as.u.note.ti1[m1] == SL_BELOW)
                  s = -1;
            else if ((s = s0) == 0) {

                  /* try to have the same tie direction as the next one */
                  if (job != 2
                      && k2->nhd != 0
                      && m2 == k2->nhd
                      && k2->as.u.note.ti1[m2])
                        s = 1;
                  else if (job == 1 && k2->nhd == 0)
                        s = k2->stem >= 0 ? -1 : 1;
                  else if (k1->nhd == 0)
                        s = s1;
                  else if (m1 == 0) /* if bottom */
                        s = -1;
                  else if (m1 == k1->nhd) /* if top */
                        s = 1;
                  else  s = s1;
            }

            x1 = k1->x;
            h = k1->shhd[m1];       /* head shift */
            if (s > 0) {
                  if (m1 < k1->nhd && k1->pits[m1] + 1 == k1->pits[m1 + 1])
                        if (k1->shhd[m1 + 1] > h)
                              h = k1->shhd[m1 + 1];
            } else {
                  if (m1 > 0 && k1->pits[m1] == k1->pits[m1 - 1] + 1)
                        if (k1->shhd[m1 - 1] > h)
                              h = k1->shhd[m1 - 1];
            }
            if (h > 0)
                  x1 += h;
            x2 = k2->x;
            h = k2->shhd[m2];
            if (s > 0) {
                  if (m2 < k2->nhd && k2->pits[m2] + 1 == k2->pits[m2 + 1])
                        if (k2->shhd[m2 + 1] < h)
                              h = k2->shhd[m2 + 1];
            } else {
                  if (m2 > 0 && k2->pits[m2] == k2->pits[m2 - 1] + 1)
                        if (k2->shhd[m2 - 1] < h)
                              h = k2->shhd[m2 - 1];
            }
            if (h < 0)
                  x2 += h;
            if (job == 2) {         /* half tie from last note in line */
                  x2 -= k2->wl;
                  if (k1 == k2)
                        x2 = realwidth;
                  if (x2 < x1 + 16)
                        x2 = x1 + 16;
            } else if (job == 1) {  /* half tie to first note in line */
                  x1 = k1->x;
                  if (x1 > x2 - 20)
                        x1 = x2 - 20;
            }
            if (x2 - x1 > 20) {
                  x1 += 2;
                  x2 -= 2;
            }
            y1 = 3 * (p1 - 18) + 2 * s;
            y2 = 3 * (p2 - 18) + 2 * s;
            if (job != 1) {
                  if (k1->nhd != 0)
                        x1 += 4.5;
                  else  y1 += ((p1 % 2) ? 3 : 2) * s;
                  if (s > 0) {
                        if (k1->nflags > -2 && k1->stem > 0
                            && k1->nhd == 0)
                              x1 += 4.5;
                        if (!(p1 % 2) && k1->dots > 0)
                              y1 = 3 * (p1 - 18) + 6;
                  }
            }
            if (job != 2) {
                  if (k2->nhd != 0)
                        x2 -= 4.5;
                  else  y2 += ((p2 % 2) ? 3 : 2) * s;
                  if (s < 0) {
                        if (k2->nflags > -2 && k2->stem < 0
                            && k2->nhd == 0)
                              x2 -= 4.5;
                  }
                  if (job != 0)
                        y1 = y2;
            } else      y2 = y1;

            /* tie between 2 staves */
/*fixme: should also do that when clef change*/
            if (k1->staff != k2->staff) {
                  s = k1->staff - k2->staff;
                  y1 = 3 * (p1 - 18) + 3 * s;
                  y2 = 3 * (p2 - 18) - 3 * s;
                  x1 += 4;
                  x2 -= 4;
                  PUT4("%.1f %.1f M %.1f %.1f lineto stroke\n",
                       x1, staff_tb[k1->staff].y + y1,
                       x2, staff_tb[k2->staff].y + y2);
                  continue;
            }

            h = (0.04 * (x2 - x1) + 8) * s;
            slur_out(x1, staff_tb[k1->staff].y + y1,
                   x2, staff_tb[k1->staff].y + y2,
                   s, h, dotted, -1);
      }
}

/* -- draw ties between neighboring notes/chords -- */
static void draw_ties(struct SYMBOL *k1,
                  struct SYMBOL *k2,
                  int job)
{
      int i, m1, nh1, nh2;
      int mhead1[MAXHD], mhead2[MAXHD], ntie;

      ntie = 0;
      nh1 = k1->nhd;

      if (job == 2) {         /* half ties from last note in line */
            for (i = 0; i <= nh1; i++) {
                  if (k1->as.u.note.ti1[i])
                        mhead1[ntie++] = i;
            }
            draw_note_ties(k1, k2,
                        ntie, mhead1, mhead1,
                        k1->as.u.note.dotted_tie, job);
            return;
      }

      /* 2-note case: set up list of ties to draw */
      nh2 = k2->nhd;
      for (i = 0; i <= nh1; i++) {
            if (k1->as.u.note.ti1[i] != 0) {
                  int pit;

                  pit = k1->as.u.note.pits[i];
                  for (m1 = k2->nhd; m1 >= 0; m1--) {
                        if (k2->as.u.note.pits[m1] == pit) {
                              mhead1[ntie] = i;
                              mhead2[ntie++] = m1;
                              break;
                        }
                  }
                  if (m1 < 0)
                        error(1, k1, "Bad tie");
            }
      }
      draw_note_ties(k1, k2,
                  ntie, mhead1, mhead2,
                  k1->as.u.note.dotted_tie, job);
}

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

      for (s1 = p_voice->sym->next; s1 != 0; s1 = s1->next)
            if (s1->type != KEYSIG && s1->type != TIMESIG)
                  break;
      rtie = p_voice->rtie;               /* tie from 1st repeat bar */
      for (s2 = s1; s2 != 0; s2 = s2->next) {
            if (s2->type == NOTE)
                  break;
            if (s2->type != BAR
                || !s2->as.u.bar.repeat_bar
                || s2->as.text == 0)
                  continue;
            if (s2->as.text[0] == '1')    /* 1st repeat bar */
                  rtie = p_voice->tie;
            else  p_voice->tie = rtie;
      }
      if (s2 == 0)
            return;
      if (p_voice->tie != 0) {            /* tie from previous line */
            p_voice->tie->x = s1->x + s1->wr;
            s1 = p_voice->tie;
            p_voice->tie = 0;
            s1->next = s2;                /* (needed for slur_direction()) */
            s1->multi = s2->multi;
            s1->stem = s2->stem;
            s1->staff = s2->staff;
            draw_ties(s1, s2, 1);         /* tie to 1st note */
      }

      for (;;) {
            for (s1 = s2; s1 != 0; s1 = s1->next) {
                  if (s1->sflags & S_TI1)
                        break;
                  if (s1->type != BAR
                      || !s1->as.u.bar.repeat_bar
                      || s1->as.text == 0)
                        continue;
                  if (s1->as.text[0] == '1')    /* 1st repeat bar */
                        rtie = 0;
                  if (rtie == 0)
                        continue;
                  for (s2 = s1->next; s2 != 0; s2 = s2->next)
                        if (s2->type == NOTE)
                              break;
                  if (s2 == 0) {
                        s1 = 0;
                        break;
                  }
                  memcpy(&tie, rtie, sizeof tie);
                  tie.x = s1->x +s1->wr;
                  tie.next = s2;
                  tie.multi = s2->multi;
                  tie.stem = s2->stem;
                  tie.staff = s2->staff;
                  draw_ties(&tie, s2, 1);
            }
            if (s1 == 0) {
                  p_voice->rtie = rtie;
                  return;
            }
            for (s2 = s1->next; s2 != 0; s2 = s2->next) {
                  if (s2->type == NOTE)
                        break;
                  if (s2->type == BAR) {
                        if ((s2->sflags & S_RRBAR)
                            || s2->as.u.bar.type == B_THIN_THICK
                            || s2->as.u.bar.type == B_THICK_THIN)
                              break;
                        if (!s2->as.u.bar.repeat_bar
                            || s2->as.text == 0)
                              continue;
                        if (s2->as.text[0] != '1')
                              break;
                        rtie = s1;        /* 1st repeat bar */
                  }
            }
            if (s2 == 0)
                  break;
            draw_ties(s1, s2, s2->type == NOTE ? 0 : 2);
      }
      draw_ties(s1, s1, 2);
      p_voice->tie = s1;
      p_voice->rtie = rtie;
}

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

      slur_st = p_voice->slur_st;
      p_voice->slur_st = 0;

      /* draw the slurs inside the music line */
      draw_slurs(p_voice->sym->next, 0);

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

/* -- draw the lyrics under notes -- */
/* !! this routine is tied to set_width() and set_staff() !! */
static void draw_vocals(struct VOICE_S *p_voice)
{
      int hyflag, l, j, lflag, nwl;
      float lastx, w, lskip, y;
      struct FONTSPEC *f;

      if ((nwl = p_voice->nvocal) == 0)
            return;
      y = p_voice->yvocal;
      f = 0;                              /* (force new font) */
      lskip = 0;                    /* (compiler warning) */
      for (j = 0; j < nwl; j++) {
            struct SYMBOL *s;
            float x0, shift;

            hyflag = lflag = 0;
            if (p_voice->hy_st & (1 << j)) {
                  hyflag = 1;
                  p_voice->hy_st &= ~(1 << j);
            }
            for (s = p_voice->sym->next; /*s != 0*/; s = s->next)
                  if (s->type != KEYSIG && s->type != TIMESIG)
                        break;
            lastx = s->prev->x;
            x0 = 0;
            for ( ; s != 0; s = s->next) {
                  struct lyrics *ly;
                  struct lyl *lyl;
                  unsigned char *p;

                  if ((ly = s->ly) == 0
                      || (lyl = ly->lyl[j]) == 0) {
                        switch (s->type) {
                        case REST:
                        case MREST:
                        case MREP:
                              if (lflag) {
                                    PUT3("%.1f %.1f %.1f wln ",
                                         x0 - lastx, lastx + 3, y);
                                    lflag = 0;
                                    lastx = s->x + s->wr;
                              }
                        }
                        continue;
                  }
                  if (lyl->f != f) {            /* font change */
                        f = lyl->f;
                        PUT2("%.1f F%d ", f->size, f->fnum);
                        lskip = f->size * 1.1;
                  }
                  p = lyl->t;
                  w = lyl->w;
                  shift = lyl->s;
                  if (hyflag) {
                        if (*p == '\x03')       /* '_' */
                              *p = '\x02';
                        else if (*p != '\x02') {      /* not '-' */
                              PUT3("%.1f %.1f %.1f hyph ",
                                   s->x - shift - lastx, lastx, y);
                              hyflag = 0;
                              lastx = s->x + s->wr;
                        }
                  }
                  if (lflag
                      && *p != '\x03') {        /* not '_' */
                        PUT3("%.1f %.1f %.1f wln ",
                             x0 - lastx + 3, lastx + 3, y);
                        lflag = 0;
                        lastx = s->x + s->wr;
                  }
                  if (*p == '\x02'        /* '-' */
                      || *p == '\x03') {        /* '_' */
                        if (x0 == 0 && lastx > s->x - 18)
                              lastx = s->x - 18;
                        if (*p == '\x02')
                              hyflag = 1;
                        else  lflag = 1;
                        x0 = s->x - shift;
                        continue;
                  }
                  x0 = s->x - shift;
                  l = strlen(p) - 1;
                  if (p[l] == '\x02') {         /* '-' at end */
                        p[l] = '\0';
                        hyflag = 1;
                  }
                  PUT3("%.1f %.1f M(%s)lyshow ", x0, y, p);
                  lastx = x0 + w;
            }
            if (hyflag) {
                  PUT3("%.1f %.1f %.1f hyph ",
                       realwidth - 10 - lastx, lastx, y);
                  if (cfmt.hyphencont)
                        p_voice->hy_st |= (1 << j);
            }

            /* see if any underscore in the next line */
            for (s = tsnext; s != 0; s = s->ts_next)
                  if (s->voice == p_voice - voice_tb)
                        break;
            for ( ; s != 0; s = s->next) {
                  if (s->type == NOTE) {
                        if (s->ly != 0 && s->ly->lyl[j] != 0
                            && s->ly->lyl[j]->t[0] == '\x03') {
                              lflag = 1;
                              x0 = realwidth - 15;
                              if (x0 < lastx + 12)
                                    x0 = lastx + 12;
                        }
                        break;
                  }
            }
            if (lflag)
                  PUT3("%.1f %.1f %.1f wln",
                       x0 - lastx + 3, lastx + 3, y);
            PUT0("\n");
            y -= lskip;
      }
}

/* -- draw the symbols near the notes -- */
/* (the staves are not yet defined) */
/* order:
 * - beams
 * - decorations near the notes
 * - measure bar numbers
 * - n-plets
 * - slurs
 * - decorations tied to the notes
 * - guitar chords
 * - then remaining decorations
 */
void draw_sym_near(void)
{
      struct VOICE_S *p_voice;
      struct SYMBOL *s;

      for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
            struct BEAM bm;

            bm.staff = 0;                 /* staves not defined */
            for (s = p_voice->sym->next; s != 0; s = s->next) {
                  if (s->type == NOTE
                      && (s->sflags & S_WORD_ST)
                      && !s->as.u.note.word_end
                      && !s->as.u.note.invis) {
                        if (calculate_beam(&bm, s))
                              draw_beams(&bm);
                  }
            }
      }

      draw_deco_near();

      /* have room for the accidentals and adjust the grace notes x offsets */
      for (s = first_voice->sym->next; s != 0; s = s->ts_next) {
            int nhd, y;

            if (s->type != NOTE)
                  continue;
            nhd = s->nhd;
            if (s->as.u.note.accs[nhd]) {
                  y = s->y + 8;
                  if (s->dc_top < y)
                        s->dc_top = y;
            }
            if (s->as.u.note.accs[0]) {
                  y = s->y;
                  if ((s->as.u.note.accs[0] & 0x0f) == A_SH
                      || s->as.u.note.accs[0] == A_NT)
                        y -= 7;
                  else  y -= 5;
                  if (s->dc_bot > y)
                        s->dc_bot = y;
            }
      }

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

      for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
            for (s = p_voice->sym->next; s != 0; s = s->next) {
                  if (s->type == TUPLET)
                        s = draw_tuplet(s);
            }
            draw_all_slurs(p_voice);
      }

      draw_deco_note();

      draw_deco_staff();
}

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

      /* draw the symbols */
      bm.s2 = 0;
      bm.staff = -1;                      /* staves defined */
      for (s = p_voice->sym; s != 0; s = s->next) {
            x = s->x;
            switch (s->type) {
            case NOTE:
                  if (s->as.u.note.invis)
                        break;
                  if ((s->sflags & S_WORD_ST)
                      && !s->as.u.note.word_end) {
                        if (calculate_beam(&bm, s))
                              draw_beams(&bm);
                  }
                  draw_note(x, s, bm.s2 == 0);
                  if (s == bm.s2)
                        bm.s2 = 0;
                  break;
            case REST:
                  if (s->as.u.note.invis && !(s->sflags & S_OTHER_HEAD))
                        break;
                  draw_rest(s);
                  break;
            case BAR:
                  if (p_voice != first_voice)
                        break;
                  draw_bar(x, s);
                  break;
            case CLEF: {
                  int staff;

                  staff = s->staff;
                  if (p_voice->second || p_voice->staff != staff)
                        break;            /* only one clef per staff */
                  memcpy(&staff_tb[staff].clef, &s->as.u.clef, /* (for next lines) */
                        sizeof s->as.u.clef);
                  if (s->as.u.clef.invis)
                        break;
                  y = staff_tb[staff].y;
                  PUT4("%.1f %.1f %c%cclef\n", x, y + s->y,
                       s->u ? 's' : ' ',
                       "tcbp"[(unsigned) s->as.u.clef.type]);
                  if (s->as.u.clef.octave == 0)
                        break;
/*fixme: break the compatibility and avoid strange numbers*/
                  if (s->as.u.clef.octave > 0)
                        y += s->ymx - 36 - 12;
                  else  y += s->ymn + 19 + 5;
                  PUT3("%.1f %.1f oct%c\n", x, y,
                       s->as.u.clef.octave > 0 ? 'u' : 'l');
                  break;
            }
            case TIMESIG:
                  memcpy(&p_voice->meter, &s->as.u.meter,
                         sizeof p_voice->meter);
                  if (p_voice != first_voice)
                        break;
                  draw_timesig(x, s);
                  break;
            case KEYSIG:
                  draw_keysig(p_voice, x, s);
                  break;
            case MREST:
                  if (p_voice->second)
                        break;
                  PUT3("(%d)%.1f %.1f mrest\n",
                       s->as.u.bar.len, x, staff_tb[s->staff].y);
                  break;
            case MREP:
                  if (p_voice->second)
                        break;
                  if (s->as.u.bar.len == 1) {
                        x = (s->prev->x + s->next->x) * 0.5;
                        PUT2("%.1f %.1f mrep\n",
                             x, staff_tb[s->staff].y);
                        break;
                  }
                  PUT2("%.1f %.1f mrep2\n",
                       s->prev->x, staff_tb[s->staff].y);
                  if (s->voice != first_voice - voice_tb)
                        break;
                  set_font(&cfmt.font_tb[s->gcf]);
                  PUT3("%.1f %.1f M(%d)showc\n",
                       s->prev->x, staff_tb[s->staff].y + 24 + 4,
                       s->as.u.bar.len);
                  break;
            case GRACE:
                  draw_gracenotes(s);
                  break;
            case TEMPO:
            case STAVES:
            case PART:
            case TUPLET:
                  break;                  /* nothing */
            case FMTCHG:
                  if (s->u == STBRK) {
                        float dx, dy;

                        if (p_voice != first_voice)
                              break;
                        dx = s->ts_prev->x - x;
                        if (s->ts_prev->type == BAR)
                              dx += 1;
                        else  dx += s->ts_prev->wr;
                        y = staff_tb[nstaff].y - 1.;
                        dy = staff_tb[0].y + 24. + 2. - y;
                        PUT4("currentgray 1.0 setgray"
                             " %.1f %.1f %.1f %.1f rectfill"
                             " setgray\n",
                             x, y, dx, dy);
                        if (nstaff != 0 && s->xmx > 0.5 * CM)
                              draw_lstaff(x);
                        break;
                  }
                  if (s->u == PSSEQ) {
                        PUT1("%s\n", s->as.text);
                        break;
                  }
#if 1
/*fixme: should remove the other format changes */
                  break;
#else
                  /* fall thru */
#endif
            default:
                  bug("Symbol not drawn", 1);
            }
      }

      draw_all_ties(p_voice);

      draw_vocals(p_voice);
}

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

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

/* -- draw the tin whistle tablature -- */
void draw_whistle(void)
{
      struct VOICE_S *p_voice;
      struct SYMBOL *s;
      int i, j, pitch, w_pitch, w_octave;
      int sf;
      unsigned char workmap[70];    /* sharps/flats - base: lowest 'C' */
      unsigned char basemap[7];
      static char pitnam[12 * 2] = "C\0C#D\0EbE\0F\0F#G\0AbA\0BbB\0";
      static int w_tb[12] = {
            0x222222, 0x122222, 0x022222, 0x012222, 0x002222, 0x000222,
            0x000122, 0x000022, 0x000012, 0x000002, 0x000220, 0x000000
      };
      static int scale[7] = {0, 2, 4, 5, 7, 9, 11};   /* index = natural note */
      static int acc_pitch[6] = {0, 1, 0, -1, 2, -2}; /* index = enum accidentals */

      sf = 0;
      for (i = 0; i < nwhistle; i++) {
            p_voice = &voice_tb[whistle_tb[i].voice];
            if (p_voice != first_voice && p_voice->prev == 0)
                  continue;         /* voice not displayed */
            w_pitch = whistle_tb[i].pitch;
            PUT1("(%.2s)tw_head\n", &pitnam[(w_pitch % 12) * 2]);
            for (s = p_voice->sym->next; s != 0; s = s->next) {
                  switch (s->type) {
                  case NOTE:
                        break;
                  case KEYSIG:
                        sf = s->as.u.key.sf;
                        setmap(sf, basemap);
                        for (j = 0; j < 10; j++)
                              memcpy(&workmap[7 * j],
                                     basemap, 7);
                        continue;
                  case BAR:
                        if (s->as.u.bar.type == B_INVIS)
                              continue;
                        for (j = 0; j < 10; j++)
                              memcpy(&workmap[7 * j],
                                     basemap, 7);
                        continue;
                  default:
                        continue;
                  }
#if 0
/*fixme*/
                  if (s->as.u.note.ti2[0] != 0)  /* tied note */
                        continue;
#endif
                  pitch = s->as.u.note.pits[0] + 19;
                  if (s->as.u.note.accs[0] != 0) {
                        workmap[pitch] = s->as.u.note.accs[0] == A_NT
                              ? A_NULL
                              : s->as.u.note.accs[0];
                  }
                  pitch = scale[pitch % 7]
                        + acc_pitch[workmap[pitch]]
                        + 12 * (pitch / 7);
                  w_octave = 0;
                  pitch -= w_pitch;
                  while (pitch < 0) {
                        pitch += 12;
                        w_octave--;
                  }
                  while (pitch >= 36) {
                        pitch -= 12;
                        w_octave++;
                  }
                  PUT1("%.2f 0 ", s->x);
                  if (w_octave > 0)
                        PUT0("tw_over ");
                  else if (w_octave < 0)
                        PUT0("tw_under ");
                  w_octave = pitch / 12;
                  if (pitch == 12)
                        pitch = 0x222220; /* only special case (?) */
                  else  pitch = w_tb[pitch % 12];
                  for (j = 1; j < 7; j++) {
                        PUT1("tw_%d ", pitch & 0x0f);
                        pitch >>= 4;
                  }
                  if (w_octave == 0)
                        PUT0("pop pop");
                  else if (w_octave == 1)
                        PUT0("tw_p");
                  else  PUT0("tw_pp");
                  PUT0("\n");
            }
            bskip(63.);
      }
}

Generated by  Doxygen 1.6.0   Back to index