chiark / gitweb /
Import upstream version 5.3.
[mup] / mup / mup / prnttab.c
1 /* Copyright (c) 1997, 1998, 1999, 2001, 2003, 2004 by Arkkra Enterprises */
2 /* All rights reserved. */
3
4 /* functions to support printing of tab staffs */
5
6 #include "defines.h"
7 #include "structs.h"
8 #include "globals.h"
9
10 /* position of the last bend arrow for each staff, or 0.0 if no
11  * arrow on previous group */
12 static double Last_x_arrow[MAXSTAFFS + 1], Last_y_arrow[MAXSTAFFS + 1];
13
14 static double pr_tab_note P((int note_index, struct GRPSYL *gs_p,
15                 double y_adjust, struct MAINLL *mll_p));
16 static double pr_bstring P((struct GRPSYL *gs_p, struct NOTE *note_p,
17                 int note_index, double y_adjust, struct MAINLL *mll_p));
18 static void pr_b_arrow P((struct GRPSYL *gs_p, struct MAINLL *mll_p,
19                 double y_adjust));
20 static void pr_b_curve P((float *xlist, float *ylist, struct GRPSYL *gs_p,
21                 double y_adjust));
22 static void pr_arrowhead P((struct GRPSYL *gs_p, double y_adjust, int headchar));
23 static int bends_up P((struct GRPSYL *gs_p, int n, struct GRPSYL *pgs_p,
24                 int carried_in));
25 static int is_carried_in_bend P((struct MAINLL *mll_p,
26                 struct MAINLL *prev_mll_p));
27 \f
28
29
30 /* given a GRPSYL list for a tab staff, print all the notes in the list */
31
32 void
33 pr_tab_groups(gs_p, mll_p)
34
35 struct GRPSYL *gs_p;
36 struct MAINLL *mll_p;
37
38 {
39         int n;
40         double y_adjust;        /* to account for bend string space */
41         struct GRPSYL *ngs_p;   /* next group */
42         struct MAINLL *m_p;
43
44
45         /* go through the list of groups */
46         for (  ; gs_p != (struct GRPSYL *) 0; gs_p = gs_p->next) {
47
48                 /* if inhibitprint flag is set (because this is a tied-to
49                  * group), skip this group */
50                 if (gs_p->inhibitprint == YES) {
51                         continue;
52                 }
53
54                 /* measure repeats are special */
55                 if (is_mrpt(gs_p) == YES) {
56                         pr_mrpt(gs_p, mll_p->u.staff_p);
57                         continue;
58                 }
59
60                 /* multirests are special. Print on tab staff only if its
61                  * corresponding tabnote staff is not printed */
62                 if (gs_p->basictime < -1) {
63                         if (svpath(gs_p->staffno - 1, VISIBLE)->visible == NO) {
64                                 pr_multirest(gs_p, mll_p->u.staff_p);
65                         }
66                         continue;
67                 }
68
69                 /* print the fret number and bend string
70                  * for each note in the group. The
71                  * y_adjust gets changed to account for space taken up
72                  * by bend text strings */
73                 y_adjust = 0.0;
74                 for (n = 0; n < gs_p->nnotes; n++) {
75                         y_adjust = pr_tab_note(n, gs_p, y_adjust, mll_p);
76                 }
77
78                 /* if there were any bends, draw the arrow. Otherwise
79                  * remember that there are no bends in progress */
80                 if (y_adjust > 0.0) {
81                         pr_b_arrow(gs_p, mll_p, y_adjust);
82                 }
83                 else {
84                         Last_y_arrow[gs_p->staffno] = 0.0;
85                         Last_x_arrow[gs_p->staffno] = 0.0;
86                 }
87
88                 /* print any slashes */
89                 if (gs_p->slash_alt > 0) {
90                         /* slashes on tab staff get hard-coded
91                          * tilt value of 2.2 * Stdpad. */
92                         if (gs_p->stemdir == UP) {
93                                 pr_slashes(gs_p, (double) gs_p->c[AX],
94                                         (double) (gs_p->notelist[gs_p->nnotes - 1].c[AY]
95                                         + gs_p->stemlen), (double) -1.0,
96                                         (double) 0.0, (double) (2.2 * Stdpad));
97                         }
98                         else {
99                                 pr_slashes(gs_p, (double) gs_p->c[AX],
100                                         (double) (gs_p->notelist[0].c[AY]
101                                         - gs_p->stemlen), (double) 1.0,
102                                         (double) 0.0, (double) (2.2 * Stdpad));
103                         }
104                 }
105
106                 if (gets_roll(gs_p, mll_p->u.staff_p, 0) == YES) {
107                         print_roll(gs_p);
108                 }
109
110                 /* print any "with" list items */
111                 pr_withlist(gs_p);
112
113                 /* on last group in measure, have to look ahead to next group
114                  * if any to see if it has a bend from this note and is on
115                  * the next score. If so, have to draw a horizontal line for
116                  * the bend out to near the end of the current score */
117                 if (gs_p->next == (struct GRPSYL *) 0) {
118                         /* is the last group in the measure. See if we are
119                          * at a scorefeed. First search for end of current
120                          * measure. */
121                         for (m_p = mll_p; m_p != (struct MAINLL *) 0 &&
122                                         m_p->str != S_BAR; m_p = m_p->next) {
123                                 ;
124                         }
125
126                         /* now go until we find either a feed or a staff */
127                         for (     ; m_p != (struct MAINLL *) 0;
128                                                         m_p = m_p->next) {
129                                 if (m_p->str == S_FEED || m_p->str == S_STAFF) {
130                                         break;
131                                 }
132                         }
133                         if (m_p == (struct MAINLL *) 0 || m_p->str != S_FEED) {
134                                 /* no next group or not at a scorefeed */
135                                 continue;
136                         }
137
138                         /* See if there is another group in the next measure */
139                         if ((ngs_p = nextgrpsyl(gs_p, &mll_p))
140                                                 == (struct GRPSYL *) 0) {
141                                 /* no next group */
142                                 continue;
143                         }
144                         if (ngs_p->nnotes == 0) {
145                                 continue;
146                         }
147
148                         for (n = 0; n < ngs_p->nnotes; n++) {
149                                 if (HASREALBEND(ngs_p->notelist[n]) &&
150                                         ngs_p->notelist[n].FRETNO == NOFRET) {
151                                         /* next group has a real, non-prebend
152                                          * bend, so have to deal with it */
153                                         break;
154                                 }
155                                 else if (HASNULLBEND(ngs_p->notelist[n])) {
156                                         /* has null bend. have to do this one */
157                                         break;
158                                 }
159                         }
160                         if (n == ngs_p->nnotes) {
161                                 /* no non-prebend bend */
162                                 continue;
163                         }
164
165                         /* if we got here, this is the special case where we
166                          * do need to draw the line, so do it */
167                         do_linetype(L_NORMAL);
168                         if (Last_y_arrow[gs_p->staffno] != 0.0) {
169                                 draw_line( (double) (Last_x_arrow[gs_p->staffno]),
170                                         (double) (Last_y_arrow[gs_p->staffno]),
171                                         (double) (PGWIDTH
172                                         - eff_rightmargin(mll_p) - Stepsize),
173                                         (double) (Last_y_arrow[gs_p->staffno]));
174                                 Last_x_arrow[gs_p->staffno] =
175                                         PGWIDTH - eff_rightmargin(mll_p)
176                                         - Stepsize;
177                         }
178                         else {
179                                 /* the bend is on the next score, but not a
180                                  * continuation bend. so draw an arrow towards
181                                  * the margin, followed by a short
182                                  * dashed line */
183                                 float xlist[6], ylist[6];  /* curve coords */
184                                 struct GRPSYL dummy_gs;  /* for curve end */
185
186
187                                 /* set place for beginning of curve */
188                                 xlist[0] = gs_p->notelist[n].c[AX] + Stdpad +
189                                         notehorz(gs_p, &(gs_p->notelist[n]), RE);
190                                 ylist[0] = gs_p->notelist[n].c[AY];
191
192                                 /* fill in a dummy GRPSYL struct with just
193                                  * the pieces of information that pr_b_curve
194                                  * will need to draw a bend curve */
195                                 dummy_gs.c[AX] =  PGWIDTH
196                                                 - eff_rightmargin(mll_p)
197                                                 - 2.5 * Stepsize;
198                                 dummy_gs.c[AN] = ylist[0] + 2.4 * Stepsize
199                                                         * TABRATIO;
200                                 /* if we're too close to the margin, make the
201                                  * curve a little longer, so it will look
202                                  * decent, even if it runs into the margin
203                                  * a little ways */
204                                 if (dummy_gs.c[AX] - xlist[0]
205                                                         < 3.0 * Stepsize) {
206                                         dummy_gs.c[AX] = 3.0 * Stepsize;
207                                         /* If that still didn't move it enough
208                                          * to avoid having the bend backwards,
209                                          * adjust some more */
210                                         if (dummy_gs.c[AX] - xlist[0]
211                                                         < 2.0 * Stepsize) {
212                                                 dummy_gs.c[AX] = xlist[0]
213                                                         + 2.0 * Stepsize;
214                                         }  
215                                 }
216
217                                 /* draw the curve */
218                                 pr_b_curve(xlist, ylist, &dummy_gs, 0.0);
219
220                                 /* draw an arrow at the end of the curve */
221                                 pr_muschar( (double) dummy_gs.c[AX],
222                                         (double) (dummy_gs.c[AN] -
223                                         (height(FONT_MUSIC, DFLT_SIZE, C_UWEDGE)
224                                         / 2.0)), C_UWEDGE, DFLT_SIZE, FONT_MUSIC);
225
226                                 /* draw a short dashed line to the right
227                                  * from the arrow, to indicate the actual
228                                  * bend is on the next score */
229                                 do_linetype(L_DASHED);
230                                 ylist[5] += 2.0 * Stdpad;
231                                 draw_line( (double) xlist[5] + Stdpad,
232                                         (double) ylist[5],
233                                         (double) xlist[5] + 3.5 * Stepsize,
234                                         (double) ylist[5]);
235                         }
236                 }
237         }
238 }
239 \f
240
241 /* print things for a tab staff note. Return any adjustment to North needed
242  * to account for bend string */
243
244 static double
245 pr_tab_note(note_index, gs_p, y_adjust, mll_p)
246
247 int note_index;         /* which note in gs_p */
248 struct GRPSYL *gs_p;
249 double y_adjust;        /* to account for bend string space */
250 struct MAINLL *mll_p;   /* main list struct pointing to gs_p */
251
252 {
253         struct NOTE *note_p;
254         char *fretstr;  /* text string to print for fret number */
255
256         note_p = &(gs_p->notelist[note_index]);
257
258         /* print fret number (with parentheses if appropriate) */
259         fretstr = fret_string(note_p, gs_p);
260         if ( *fretstr != '\0') {
261
262                 if (vvpath(gs_p->staffno, gs_p->vno, TABWHITEBOX)->tabwhitebox == YES) {
263                         /* Put a white box behind the number, so the line
264                          * doesn't go through it, making it easier to read. */
265                         do_whitebox( note_p->c[AX] - strwidth(fretstr) / 2.0,
266                                 note_p->c[AY] - strheight(fretstr) / 2.0,
267                                 note_p->c[AX] + strwidth(fretstr) / 2.0,
268                                 note_p->c[AY] + strheight(fretstr) / 2.0);
269                 }
270
271                 pr_string( (double) (note_p->c[AX] - (strwidth(fretstr) / 2.0)),
272                         (double) (note_p->c[AY] + (strheight(fretstr) / 2.0)
273                         - strascent(fretstr)),
274                         fretstr, J_LEFT, gs_p->inputfile, gs_p->inputlineno);
275         }
276
277         /* print a bend string if appropriate */
278         if ( HASBEND((*note_p)) ) {
279                 if (HASREALBEND(*note_p)) {
280                         y_adjust += pr_bstring(gs_p, note_p, note_index,
281                                                         y_adjust, mll_p);
282                 }
283                 else {
284                         /* need to return a little bit, so later code
285                          * knows that there was a bend of some sort */
286                         if (y_adjust == 0.0) {
287                                 y_adjust = STDPAD;
288                         }
289                 }
290         }
291         return (y_adjust);
292 }
293 \f
294
295 /* print the bend string ("full", "1/2", etc). Return its height */
296
297 static double
298 pr_bstring(gs_p, note_p, note_index, y_adjust, mll_p)
299
300 struct GRPSYL *gs_p;    /* group having a bend */
301 struct NOTE *note_p;    /* which note in gs_p has the bend */
302 int note_index;         /* index into gs_p->notelist of note_p */
303 double y_adjust;        /* to account for bend strings done previously
304                          * for this group (on other notes in the group */
305 struct MAINLL *mll_p;   /* main list struct pointing to gs_p */
306
307 {
308         char *bstring;
309         double x_adjust;        /* to center upward and left justify
310                                  * downward, to not collide with curve */
311         struct GRPSYL *pgs_p;   /* previous group */
312         int n;                  /* index through notelist */
313
314
315         /* get what to print for the bend */
316         bstring = bend_string(note_p);
317
318         /* generally, the string should be centered. */
319         x_adjust = strwidth(bstring) / 2.0;
320
321         /* However, if not a prebend and the bend is downward,
322          * then we have to move the bend string over so it 
323          * doesn't collide with the curve. So first get the previous group
324          * and see it is has a bend too. If it does, see if the bend is
325          * downward, and if so, change to left justify rather than center */
326         if (note_p->FRETNO == NOFRET && (pgs_p = prevgrpsyl(gs_p, &mll_p))
327                                                 != (struct GRPSYL *) 0) {
328
329                 for (n = 0; n < pgs_p->nnotes; n++) {
330                         if (pgs_p->notelist[n].STRINGNO
331                                         == gs_p->notelist[note_index].STRINGNO
332                                         && HASBEND(pgs_p->notelist[n])) {
333                                 if (bends_up(gs_p, note_index, pgs_p, NO) == NO) {
334                                         x_adjust = 0.0;
335                                 }
336                         }
337                 }
338         }
339
340         /* print the bend string */
341         pr_string( (double) (gs_p->c[AX] - x_adjust),
342                         (double) (gs_p->c[AN] - strascent(bstring) - y_adjust),
343                         bstring, J_LEFT, gs_p->inputfile, gs_p->inputlineno);
344
345         /* return the height of what was just printed */
346         return(strheight(bstring));
347 }
348 \f
349
350 /* print bend arrow */
351
352 static void
353 pr_b_arrow(gs_p, mll_p, y_adjust)
354
355 struct GRPSYL *gs_p;
356 struct MAINLL *mll_p;
357 double y_adjust;
358
359 {
360         struct GRPSYL *pgs_p;           /* previous grpsyl */
361         struct MAINLL *prev_mll_p;      /* where pgs_p is connected */
362         float xlist[6], ylist[6];       /* coords of bend curve */
363         int n;                          /* note index */
364         int staffno;
365         int carried_in;                 /* YES if bend is carried in */
366
367
368         staffno = gs_p->staffno;
369
370         /* leave a little room between bend string and the arrow. This is
371          * especially needed when bending down, so the string and curve
372          * don't collide */
373         y_adjust += 2.0 * Stdpad;
374
375         /* find the first note with a bend on it */
376         for (n = 0; n < gs_p->nnotes; n++) {
377                 if (HASBEND(gs_p->notelist[n])) {
378                         break;
379                 }
380         }
381
382         /* the function isn't supposed to get called unless there really
383          * is a bend somewhere on the group */
384         if (n == gs_p->nnotes) {
385                 pfatal("pr_b_arrow couldn't find note with bend");
386         }
387
388         /* check if prebend or non-prebend */
389         if (gs_p->notelist[n].FRETNO != NOFRET
390                                 && ! HASNULLBEND(gs_p->notelist[n])) {
391                 /* this is a prebend */
392                 /* draw the line straight up from the fret number */
393                 do_linetype(L_NORMAL);
394                 draw_line( (double) (gs_p->c[AX]),
395                         (double) (gs_p->notelist[n].c[AN] + Stdpad),
396                         (double) (gs_p->c[AX]),
397                         (double) (gs_p->c[AN] - y_adjust));
398
399                 /* draw triangle at top */
400                 pr_arrowhead(gs_p, y_adjust, C_UWEDGE);
401                 return;
402         }
403
404         /* a normal bend, not a prebend */
405         prev_mll_p = mll_p;
406         if ((pgs_p = prevgrpsyl(gs_p, &prev_mll_p)) == (struct GRPSYL *) 0) {
407                 l_ufatal(gs_p->inputfile, gs_p->inputlineno,
408                                 "no previous chord for bend");
409         }
410
411         carried_in = is_carried_in_bend(mll_p, prev_mll_p);
412
413         if (Last_y_arrow[staffno] == 0.0) {
414                 /* not the continuation of an in-progress bend */
415
416                 /* do each note that has a bend */
417                 for (n = 0; n < gs_p->nnotes; n++) {
418                         if ( ! HASBEND(gs_p->notelist[n])) {
419                                 continue;
420                         }
421
422                         if ((mll_p != prev_mll_p && pgs_p->c[AE] > gs_p->c[AX])
423                                         || carried_in == YES) {
424
425                                 /* either an intervening scorefeed or
426                                  * carried in to subsequent ending,
427                                  * so just start a bit west
428                                  * of the current group */
429                                 xlist[0] = gs_p->c[AW] - 2.0 * Stepsize;
430                                 ylist[0] = gs_p->c[AY] +
431                                                 gs_p->notelist[n].stepsup
432                                                 * Stepsize * TABRATIO;
433                         }
434                         else {
435                                 /* beginning of bend arrow is at east
436                                  * and just a bit
437                                  * above the center of the fret number
438                                  * of the previous group */
439                                 xlist[0] = pgs_p->c[AE] + Stdpad;
440                                 ylist[0] = gs_p->notelist[n].c[AY] + Stdpad;
441                         }
442
443                         pr_b_curve(xlist, ylist, gs_p, y_adjust);
444                 }
445                 pr_arrowhead(gs_p, y_adjust, C_UWEDGE);
446         }
447         else {
448                 /* continuation of an in-progress bend */
449
450                 /* find the note that has a bend. Only allowed to be
451                  * one note with a continuation bend, or could be a release,
452                  * in which case we use the first note we find */
453                 for (n = 0; n < gs_p->nnotes; n++) {
454                         if ( HASBEND(gs_p->notelist[n])) {
455                                 break;
456                         }
457                 }
458                 if (n == gs_p->nnotes) {
459                         pfatal("unable to find note with continuation bend");
460                 }
461
462                 /* find the starting point of the bend curve */
463                 if ((mll_p != prev_mll_p && pgs_p->c[AE] > gs_p->c[AX])
464                                         || carried_in == YES) {
465
466                         /* must have been an intervening scorefeed,
467                          * so just start a bit west
468                          * of the current group */
469                         xlist[0] = gs_p->c[AW] - 2.0 * Stepsize;
470                         Last_y_arrow[staffno] = gs_p->c[AN] - y_adjust;
471
472                         /* need to adjust more if bending is actually up */
473                         if (bends_up(gs_p, n, pgs_p, carried_in)) {
474                                 Last_y_arrow[staffno] -= 3.0 * Stepsize;
475                         }
476
477                         /* null bends carried over a scorefeed
478                          * have to be done specially */
479                         if (HASNULLBEND(gs_p->notelist[n])) {
480                                 Last_y_arrow[staffno] = gs_p->notelist[n].c[AN]
481                                         + (2 * gs_p->notelist[n].STRINGNO
482                                         * Stepsize * TABRATIO);
483                                 if (gs_p->notelist[n].STRINGNO == 0) {
484                                         Last_y_arrow[staffno] += 2.0 * Stepsize
485                                                          * TABRATIO;
486                                 }
487                         }
488                 }
489                 else {
490                         /* beginning of bend curve is where last
491                          * one left off */
492                         xlist[0] = Last_x_arrow[staffno];
493                 }
494                 ylist[0] = Last_y_arrow[staffno];
495
496                 /* determine whether curve goes up or down
497                  * and find its endpoint */
498                 if (bends_up(gs_p, n, pgs_p, carried_in) == YES) {
499                         /* bending up some more */
500                         pr_b_curve(xlist, ylist, gs_p, y_adjust);
501                         pr_arrowhead(gs_p, y_adjust, C_UWEDGE);
502                 }
503                 else { /* bending back downwards */
504
505                         if ( ! HASREALBEND(gs_p->notelist[n])) {
506                                 /* null bend. Have to draw curve all the way
507                                  * down to the appropriate "note" */
508                                 y_adjust = gs_p->c[AN] -
509                                         gs_p->notelist[n].c[AN] - Stdpad -
510                                         height(FONT_MUSIC, DFLT_SIZE, C_WEDGE)
511                                         / 2.0;
512                         }
513                         pr_b_curve(xlist, ylist, gs_p, y_adjust);
514                         pr_arrowhead(gs_p, y_adjust, C_WEDGE);
515                 }
516         }
517 }
518 \f
519
520 /* given the first point of a bend curve, plus the gs_p and y adjustment for
521  * the end of the curve, figure out the points of the curve and draw it */
522
523 static void
524 pr_b_curve(xlist, ylist, gs_p, y_adjust)
525
526 float *xlist;   /* arrays of x & y coordinates, with 6 members */
527 float *ylist;
528 struct GRPSYL *gs_p;
529 double y_adjust;        /* how far from the north of the gs_p the curve is */
530
531 {
532         float xlen, ylen;
533
534
535         /* last point of bend arrow is just below
536          * the bend string */
537         xlist[5] = gs_p->c[AX];
538         ylist[5] = gs_p->c[AN] - y_adjust - Stdpad;
539
540         /* fill in intermediate points */
541         xlen = xlist[5] - xlist[0];
542         ylen = ylist[5] - ylist[0];
543         xlist[1] = xlist[0] + xlen * 0.15;
544         ylist[1] = ylist[0];
545         xlist[2] = xlist[0] + xlen * 0.7;
546         ylist[2] = ylist[0] + ylen * 0.2;
547         xlist[3] = xlist[5] - xlen * 0.2;
548         ylist[3] = ylist[5] - ylen * 0.7;
549         xlist[4] = xlist[5];
550         ylist[4] = ylist[5] - ylen * 0.15;
551
552         /* draw the curve */
553         do_linetype(L_NORMAL);
554         pr_allcurve(xlist, ylist, 6, W_NORMAL, NO);
555 }
556 \f
557
558 /* print arrowhead at the end of a bend curve */
559
560 static void
561 pr_arrowhead(gs_p, y_adjust, headchar)
562
563 struct GRPSYL *gs_p;
564 double y_adjust;
565 int headchar;   /* C_WEDGE or C_UWEDGE */
566
567 {
568         pr_muschar( (double) gs_p->c[AX],
569                 (double) (gs_p->c[AN] - y_adjust -
570                 (height(FONT_MUSIC, DFLT_SIZE, headchar) / 2.0)),
571                 headchar, DFLT_SIZE, FONT_MUSIC);
572
573         Last_y_arrow[gs_p->staffno] = gs_p->c[AN] - y_adjust;
574         Last_x_arrow[gs_p->staffno] = gs_p->c[AX];
575 }
576 \f
577
578 /* given a GRPSYL that has a continuation bend, or is a carried-in bend,
579  * return YES if it is bending
580  * upwards from the previous bend, NO if not */
581
582 static int
583 bends_up(gs_p, n, pgs_p, carried_in)
584
585 struct GRPSYL *gs_p;    /* group having a continuation bend */
586 int n;                  /* the note index in gs_p having the bend */
587 struct GRPSYL *pgs_p;   /* previous group */
588 int carried_in;         /* YES if bend is carried in */
589
590 {
591         RATIONAL thisgrp_bend;  /* rational version of bend on this group */
592         RATIONAL prevgrp_bend;  /* rational version of bend on previous group */
593         int i;                  /* index through pgs_p notes */
594
595
596         if (gs_p == (struct GRPSYL *) 0 || pgs_p == (struct GRPSYL *) 0) {
597                 pfatal("null pointer passed to bends_up");
598         }
599
600         /* get rational version of the bend distance on continuation note */
601         thisgrp_bend = ratbend( &(gs_p->notelist[n]) );
602
603         /* find the corresponding note in the previous group */
604         for (i = 0; i < pgs_p->nnotes; i++) {
605                 if (pgs_p->notelist[i].STRINGNO == gs_p->notelist[n].STRINGNO) {
606                         break;
607                 }
608         }
609         if (i == pgs_p->nnotes) {
610                 pfatal("couldn't find the note being bent from");
611         }
612
613         /* get rational version of that bend */
614         if ( ! HASBEND(pgs_p->notelist[i]) ) {
615                 /* if this is a carried-in bend, it may not be a continuation
616                  * bend, but in that case, it has to be bending up */
617                 if (carried_in == YES) {
618                         return(YES);
619                 }
620                 else {
621                         l_ufatal(gs_p->inputfile, gs_p->inputlineno,
622                                 HASNULLBEND(gs_p->notelist[n]) ?
623                                 "bend release not preceded by a bend" :
624                                 "no bend on note supposedly being bent from");
625                 }
626         } 
627         prevgrp_bend = ratbend( &(pgs_p->notelist[i]) );
628
629         /* compare the bends */
630         if (GT(thisgrp_bend, prevgrp_bend)) {
631                 return(YES);
632         }
633         else if (LT(thisgrp_bend, prevgrp_bend)) {
634                 return(NO);
635         }
636         else {
637                 l_ufatal(gs_p->inputfile, gs_p->inputlineno,
638                         "can't bend to the same distance as previous bend");
639                 /*NOTREACHED*/
640                 return(NO);
641         }
642 }
643 \f
644
645 /* Return YES if the bend is carried in to a subsequent ending, NO if just
646  * an ordinary bend.
647  */
648
649 static int
650 is_carried_in_bend(mll_p, prev_mll_p)
651
652 struct MAINLL *mll_p;           /* where group with a bend is attached */
653 struct MAINLL *prev_mll_p;      /* where the previous group is attached */
654
655 {
656         int num_startitems;     /* how many endingloc==STARTITEM were found */
657
658
659         /* if both groups are in the same measure, then it's definitely
660          * not a carried in bend */
661         if (mll_p == prev_mll_p) {
662                 return(NO);
663         }
664
665         /* go forward from prev_mll_p until we get to mll_p. If we encounter
666          * more than 1 STARTITEM in endingloc, checking both normal and
667          * pseudo bars, then this was a carried in bend. */
668         num_startitems = 0;
669         for (   ; prev_mll_p != (struct MAINLL *) 0 && prev_mll_p != mll_p;
670                                         prev_mll_p = prev_mll_p->next) {
671
672                 switch (prev_mll_p->str) {
673
674                 case S_BAR:
675                         if (prev_mll_p->u.bar_p->endingloc == STARTITEM) {
676                                 if (++num_startitems > 1) {
677                                         /* it is carried in bend */
678                                         return(YES);
679                                 }
680                         }
681                         break;
682
683                 case S_CLEFSIG:
684                         if (prev_mll_p->u.clefsig_p->bar_p != (struct BAR *) 0) {
685                                 if (prev_mll_p->u.clefsig_p->bar_p->endingloc
686                                                         == STARTITEM) {
687                                         if (++num_startitems > 1) {
688                                                 return(YES);
689                                         }
690                                 }
691                         }
692                         break;
693
694                 default:
695                         /* nothing else is relevant at this point */
696                         break;
697                 }
698         }
699
700         /* fell out without finding 2 STARTITEMS, so not carried in */
701         return(NO);
702 }
703 \f
704
705 /* given internal representation of bend info,
706  * return string representation. Returns string in
707  * static area that is overwritten on each call.
708  */
709
710 char *
711 bend_string(note_p)
712
713 struct NOTE *note_p;
714
715 {
716         static char buff[12];
717         int intpart, num, den;
718
719         /* separate internal representation into integer, numerator,
720          * and denominator */
721         intpart = BENDINT(*note_p);
722         num = BENDNUM(*note_p);
723         den = BENDDEN(*note_p);
724
725         /* construct the string representation */
726         buff[0] = FONT_HR;
727         buff[1] = adj_size(DFLT_SIZE, Staffscale, (char *) 0, -1);
728
729         if (intpart == 1 && num == 0) {
730                 (void) strcpy(buff+2, "full");
731         }
732         else if (intpart == 0 && num == 0) {
733                 /* no bend at all */
734                 buff[0] = '\0';
735         }
736         else if (num == 0) {
737                 /* integer part only, no fraction */
738                 (void) sprintf(buff+2, "%d", intpart);
739         }
740         else if (intpart == 0) {
741                 /* fraction only */
742                 (void) sprintf(buff+2, "%d/%d", num, den);
743         }
744         else {
745                 /* both integer and fractional parts */
746                 (void) sprintf(buff+2, "%d %d/%d", intpart, num, den);
747         }
748         return(buff);
749 }
750 \f
751
752 /* given a NOTE on a tab staff, return char * of what is to be printed
753  * for the fret number. Returned string is a static area
754  * that is overwritten on each call.
755  */
756
757 char *
758 fret_string(note_p, gs_p)
759
760 struct NOTE *note_p;
761 struct GRPSYL *gs_p;    /* group containing the note */
762
763 {
764         static char fretbuff[8];
765         int size;
766
767
768         /* if no fret, return "" */
769         if (note_p->FRETNO == NOFRET) {
770                 fretbuff[0] = '\0';
771                 return(fretbuff);
772         }
773
774         size = adj_size((note_p->notesize == GS_SMALL ? SMFRETSIZE : DFLT_SIZE),
775                         Staffscale, gs_p->inputfile, gs_p->inputlineno);
776
777         /* make proper string for X-note or normal fret number, in parentheses
778          * if appropriate */
779         if (IS_MUSIC_FONT(note_p->headfont)) {
780                 (void) sprintf(fretbuff,
781                         (note_p->FRET_HAS_PAREN ? "%c%c(%c%c%c)" : "%c%c%c%c%c"),
782                                 FONT_HB, size, mfont2str(note_p->headfont),
783                                 size, note_p->headchar);
784         }
785         else {
786                 (void) sprintf(fretbuff,
787                         (note_p->FRET_HAS_PAREN ? "%c%c(%d)" : "%c%c%d"),
788                         FONT_HB, size, note_p->FRETNO);
789         }
790         return(fretbuff);
791 }
792 \f
793
794 /* print a TAB "clef" which is really just the word "TAB" written vertically.
795  * By convention, this only gets printed once per staff at the very beginning
796  * of the song. To keep things simple, the width of the clef is always 
797  * returned as if the clef was printed even when it really isn't */
798
799 double
800 pr_tabclef(staffno, x, really_print, size)
801
802 int staffno;
803 double x;
804 int really_print;
805 int size;
806
807 {
808         static int did_tab_clef[MAXSTAFFS + 1]; /* set to YES once we print a
809                          * TAB clef on a given staff. Convention is to print
810                          * this "clef" only at the very beginning of a song. */
811         int stafflines;
812         int ptsize;     /* point size to use for "TAB" */
813         double width, widest;   /* of the letters in "TAB" */
814         double height = 0.0;
815         char letter[4]; /* internal format version of one letter of "TAB" */
816         char *tabstr;   /* pointer through "TAB" */
817         double y = 0.0;
818
819
820         /* adjust the size based on how many stafflines there are */
821         stafflines = svpath(staffno, STAFFLINES)->stafflines;
822         if (stafflines < 4) {
823                 ptsize = 7;
824         }
825         else if (stafflines == 4) {
826                 ptsize = 13;
827         }
828         else if (stafflines == 5) {
829                 ptsize = 16;
830         }
831         else {
832                 ptsize = 20;
833         }
834
835         /* if small clef, adjust the size (actually, this shouldn't
836          * ever happen unless we change some other things some day, but this
837          * way we will be prepared if/when that happens). */
838         if (size != DFLT_SIZE) {
839                 ptsize = (int) (ptsize * (size / DFLT_SIZE));
840         }
841         ptsize = adj_size(ptsize, Staffscale, (char *) 0, -1);
842
843         /* print/get width of "TAB" */
844         for (widest = 0, tabstr = "TAB"; *tabstr != '\0'; tabstr++) {
845
846                 /* create internal format string for current letter */
847                 (void) sprintf(letter, "%c%c%c", FONT_HB, ptsize, *tabstr);
848                 /* get its width */
849                 width = strwidth(letter);
850
851                 /* save the widest letter width */
852                 if (width > widest) {
853                         widest = width;
854                 }
855
856                 /* if we're really supposed to print,
857                  * print this letter of "TAB" */
858                 if (really_print == YES && did_tab_clef[staffno] == NO) {
859
860                         /* figure out where to place vertically */
861                         if (*tabstr == 'T') {
862                                 /* place the top letter */
863                                 height = strheight(letter);
864                                 y = Staffs_y[staffno] + height / 2.0
865                                                                 + Stdpad;
866                         }
867                         else {
868                                 /* move subsequent letters down by height
869                                  * of the previous */
870                                 y -= height;
871                         }
872
873                         /* print the letter with a little space before */
874                         pr_string(x + 3.0 * Stdpad, y, letter, J_LEFT,
875                                                         (char *) 0, -1);
876                 }
877         }
878
879         /* only print once per staff */
880         if (really_print == YES) {
881                 did_tab_clef[staffno] = YES;
882         }
883
884         /* allow some space on either side */
885         return(widest + 6.0 * Stdpad);
886 }