/* * This file is part of abc2ps, Copyright (C) 1996,1997 Michael Methfessel * See file abc2ps.c for details. */ #include #include #include #include #include #include #include "abc2ps.h" /* [JSA] Added 10/99 used to carry accidentals. */ /* Used by "AdjustAccidentals()." */ /* [JSA] Changed 05/00 to separate lines of staff. */ static int DispAccs[MAXSTAFF][36] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; static int DispAccs2[MAXSTAFF][36] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; struct BEAM { /* packages info on one beam */ int i1,i2; float a,b; float x,y,t; int stem; float staffb; }; struct VOICE_P { /* used by set_sym_glue */ struct SYMBOL *s0; /* start of bar */ char nplet; char nn; char ncount; short bars; float space, shrink, stretch; }; static struct ENDINGS { /* where to draw endings */ float a,b; /* start and end position */ short num; /* number of the ending */ short staff; /* staff */ } ending[20]; static int num_ending; /* number of endings to draw */ static int mes1, mes2; /* to count measures in an ending */ static int nnote; /* count notes in one staff */ static float alfa_last,beta_last; /* for last short short line.. */ static int mline; /* number music lines in current tune */ static struct GSYM { /* symbols sorted by time */ struct SYMBOL *s; /* symbol */ int is; /* symbol index */ short voice; /* voice */ short seq; /* sequence */ int time; /* starting time */ struct GSYM *g; /* associated symbol on the same staff */ } *gsym; /* allocated by def_gsym, freed by output_music */ static int ngsym; /* number of pointers */ static int ig_st, ig_end; /* start/end of the line in output_music */ int measure_nb = -1; /* measure numbering (-1: no, 0: on the left, or every n bars) */ static int nbar; static int ah_shaddup; #include "style.h" /* globals to define layout style */ static void draw_decorations(struct SYMBOL *s, int voice, BOOL Preview); /* subroutines connected with output of music */ /* ----- nwidth ----- */ /* Sets the prefered width for a note depending on the duration. Return value is default space on right and left side. Function is determined by values at 0, 1/2, 1. Return value is 1.0 for quarter note. */ static float nwidth(int len) { float a,b,x,p,x0,p0; a=(float)2.0*(f1p-2*f5p+f0p); b=f1p-f0p-a; x = (float)len/(float)BASE; p = a*x*x + b*x + f0p; x0 = 0.25; p0 = a*x0*x0 + b*x0 + f0p; p /= p0; /* printf(" nwid l 1/%d w %.3f\n", BASE/len, p); */ return p; } /* ----- xwidth -- --- */ /* same as nwidth but for stretched system */ static float xwidth(int len) { float a,b,x,p,x0,p0; a=(float)2.*(f1x-2*f5x+f0x); b=f1x-f0x-a; x = (float)len/(float)BASE; p = a*x*x + b*x + f0x; x0 = 0.25; p0 = a*x0*x0 + b*x0 + f0x; return p / p0; } /* ----- next_note, prec_note ------ */ static int next_note(int k) { int i; for (i = k + 1; i < nsym; i++) { if ((sym[i].type == NOTE) || (sym[i].type == REST) || (sym[i].type == COMBINEDNOTE) || (sym[i].type == COMBINEDREST)) return i; } return -1; } static int prec_note(int k) { int i; for (i = k - 1; i >= 0; i--) { if ((sym[i].type == NOTE) || (sym[i].type == REST) || (sym[i].type == COMBINEDNOTE) || (sym[i].type == COMBINEDREST)) return i; } return -1; } /* ----- preceded_by_note ------ */ static struct SYMBOL *preceded_by_note(struct SYMBOL *s) { do { s--; } while (s->type == INVISIBLE); if ((s->type == NOTE) || (s->type == REST) || (s->type == COMBINEDNOTE) || (s->type == COMBINEDREST)) return s; return 0; } /* ----- print_syms: show sym properties set by parser ------ */ static void print_syms(int num) { int i,t,j,y; struct SYMBOL *s; static char dsym[21] = {' ', '~', '.', 'J', 'M', 'H', 'u', 'v', 'R'}; static char bsym[10] = {'-', '1' ,'2', '3', '4', '5', '6', '7', '8', '9'}; static char *acc_tb[] = { "", "^", "=", "_", "^^", "__" }; printf("\n---------- Symbol list ----------\n" "word slur eol num description\n"); for (i = 0, s = sym; i < num; i++, s++) { printf(" %c %c %c %c %c ", bsym[(int) s->word_st], bsym[(int) s->word_end], bsym[(int) s->slur_st], bsym[(int) s->slur_end], bsym[(int)s->eoln] ); printf("%4d ", i); t=s->type; switch (t) { case NOTE: printf("NOTE "); case REST: if (t == REST) printf("REST "); if (s->npitch > 1) printf(" ["); for (j=0;jnpitch;j++) { y=3*(s->pits[j]-18); printf(" %s%2d-%-2d", acc_tb[(int) s->accs[j]], y, s->lens[j]); } if (s->npitch>1) printf(" ]"); if (s->p_plet) printf(" (%d:%d:%d", s->p_plet,s->q_plet,s->r_plet); if (s->text != 0) printf(" \"%s\"", s->text); if (s->dc.n>0) { printf(" deco "); for (j=0;jdc.n;j++) printf("%c",dsym[(int) s->dc.t[j]]); } if (s->gr.n>0) { printf(" grace "); for (j=0;jgr.n;j++) { if (j>0) printf("-"); printf("%s%d", acc_tb[(int) s->gr.a[j]], s->gr.p[j]); } } break; case BAR: printf("BAR ======= "); if (s->u==B_SNGL) printf("single"); else if (s->u==B_DBL) printf("double"); else if (s->u==B_LREP) printf("left repeat"); else if (s->u==B_RREP) printf("right repeat"); else if (s->u==B_DREP) printf("double repeat"); else if (s->u==B_FAT1) printf("thick-thin"); else if (s->u==B_FAT2) printf("thin-thick"); else if (s->u==B_INVIS) printf("invisible"); else if (s->v) printf(", ending %d", s->v); break; case CLEF: printf("CLEF "); switch (s->u) { case TREBLE: printf("TREBLE"); break; case BASS: printf("BASS"); break; case ALTO: printf("ALTO"); break; default: printf("unknown (%d)", s->u); break; } break; case TIMESIG: printf("TIMESIG "); if (s->w==1) printf("C"); else if (s->w==2) printf("C|"); else printf("%d/%d", s->u,s->v); break; case KEYSIG: printf("KEYSIG"); if (s->t==A_SH) printf(" sharps"); else if (s->t==A_FT) printf(" flats"); printf(" %d to %d", s->u,s->v); if (s->w <= s->v) printf(", neutrals from %d\n", s->w); break; case INVISIBLE: printf("INVIS"); break; default: printf("UNKNOWN"); break; } printf("\n"); } printf("\n"); } /* ----- set_head_directions ----------- */ /* decide whether to shift heads to other side of stem on chords */ /* also position accidentals to avoid too much overlap */ static void set_head_directions(struct SYMBOL *s) { int i,n,nx,sig,d,da,shift,nac; int i1,i2,m; float dx,xx,xmn; n=s->npitch; sig=-1; if (s->stem>0) sig=1; for (i=0;ishhd[i]=0; s->shac[i]=8; if (s->head==H_OVAL) s->shac[i]+=3; s->xmn=0; s->xmx=0; } if (n<2) return; /* sort heads by pitch */ for (;;) { nx=0; for (i=1;ipits[i]-s->pits[i-1])*sig>0 ) { #define xch(a, b) do { int k; k = a; a = b; b = k; } while (0) xch(s->pits[i],s->pits[i-1]); xch(s->lens[i],s->lens[i-1]); xch(s->accs[i],s->accs[i-1]); xch(s->sl1[i],s->sl1[i-1]); xch(s->sl2[i],s->sl2[i-1]); xch(s->ti1[i],s->ti1[i-1]); xch(s->ti2[i],s->ti2[i-1]); #undef xch nx++; } } if (!nx) break; } shift=0; /* shift heads */ for (i=n-2;i>=0;i--) { d=s->pits[i+1]-s->pits[i]; if (d<0) d=-d; if ((d>=2) || (d==0)) shift=0; else { shift=1-shift; if (shift) { dx=(float)7.8; if (s->head==H_EMPTY) dx=(float)7.8; else if (s->head==H_OVAL) dx=(float)10.0; if (s->stem < 0) s->shhd[i]=-dx; else s->shhd[i]=dx; } } if (s->shhd[i] < s->xmn) s->xmn = s->shhd[i]; if (s->shhd[i] > s->xmx) s->xmx = s->shhd[i]; } shift=0; /* shift accidentals */ i1=0; i2=n-1; if (sig<0) { i1=n-1; i2=0; } for (i=i1; ; i=i+sig) { /* count down in terms of pitch */ xmn=0; /* left-most pos of a close head */ nac=99; /* relative pos of next acc above */ for (m=0;mshhd[m]; d=s->pits[m]-s->pits[i]; da = d > 0 ? d : -d; if ((da<=5) && (s->shhd[m]shhd[m]; if ((d>0) && (daaccs[m]) nac=da; } s->shac[i]=(float)8.5-xmn+s->shhd[i]; /* aligns accidentals in column */ if (s->head==H_EMPTY) s->shac[i] += 1.0; else if (s->head==H_OVAL) s->shac[i] += 3.0; if (s->accs[i]) { if (nac>=6) /* no overlap */ shift=0; else if (nac>=4) { /* weak overlap */ if (shift==0) shift=1; else shift -= 1; } else { /* strong overlap */ switch (shift) { case 0: shift=2; break; case 1: shift=3; break; case 2: shift=1; break; case 3: shift=0; break; } } while (shift>=4) shift -= 4; s->shac[i] += 3*shift; } if (i==i2) break; } } /* ----- set_clef: define the clef for a staff ----- */ /* this function is called only once for the whole tune */ static void set_clef(int voice) { struct SYMBOL *sym1, *s; int nsym1; int i1; int min, max; int last_bar; int clef; int chg; sym1 = voice_tb[voice].sym; nsym1 = voice_tb[voice].nsym; min = max = 16; /* 'C' */ /* count the number of notes upper and lower than 'C' */ for (i1 = 0, s = sym1; i1 < nsym1; i1++, s++) { if (s->type == NOTE) { int xp; for (xp = s->npitch; --xp >= 0;) { if (s->pits[xp] > max) max = s->pits[xp]; else if (s->pits[xp] < min) min = s->pits[xp]; } } } if (min >= 13) /* all upper than 'G,' --> treble clef */ return; if (max <= 19) { /* all lower than 'F' --> bass clef */ staff_tb[(int) voice_tb[voice].staff].clef = BASS; sym1[0].u = BASS; return; } /* set clef changes */ last_bar = -1; clef = TREBLE; chg = 0; for (i1 = 0, s = sym1; i1 < nsym1; i1++, s++) { if (s->type != NOTE) { if (s->type == BAR && last_bar >= 0) last_bar = i1; continue; } if (last_bar < 0) last_bar = 0; if (clef == TREBLE) { int xp = s->npitch - 1; if (s->pits[xp] <= 12 /* 'F,' */ || (s->pits[xp] <= 14 /* 'A,' */ && s[1].type == NOTE && s[1].pits[(int) s[1].npitch - 1] <= 14)) chg++; } else { if (s->pits[0] >= 20 /* 'G' */ || (s->pits[0] >= 18 /* 'E' */ && s[1].type == NOTE && s[1].pits[0] >= 18)) chg++; } if (chg) { chg = 0; if (clef == TREBLE) clef = BASS; else clef = TREBLE; if (last_bar == 0) { staff_tb[(int) voice_tb[voice].staff].clef = clef; sym1[0].u = clef; } else { if (last_bar < 0) last_bar = i1; if (nsym1 >= MAXSYMS) bug("Internal dimension MAXSYMS too small", 1); memmove(&sym1[last_bar+1], &sym1[last_bar], sizeof sym1[0] * (nsym1 - last_bar)); sym = sym1; nsym = last_bar; current_voice = voice; add_sym(CLEF); nsym1++; voice_tb[voice].nsym++; sym[last_bar].u = clef; sym[last_bar].v = 1; i1++; s++; } last_bar = -1; } } } /* ----- set_nplet: change the length of the n-plets ----- */ static void set_nplet(struct SYMBOL *s) { struct SYMBOL *t; int l, r, a, b, n, lplet; l = 0; a = WHOLE; t = s; r = s->r_plet; while (r > 0) { if (t->type == NOTE || t->type == REST) { l += t->len; if (t->len < a) a = t->len; r--; } t++; } n = l / a; lplet = (l * s->q_plet) / s->p_plet; r = s->r_plet; t = s; while (r > 0) { if (t->type == NOTE || t->type == REST) { b = t->len / a; l = (lplet * b) / n; lplet -= l; n -= b; t->len = l; r--; } t++; } } /* ----- define the global symbol table ----- */ /* this function is called only once for the whole tune */ /*fixme: use a djikstra algorithm*/ static void def_gsym(void) { int voice; int ig, seq; int i1, l1; struct SYMBOL *sym; int nsym; struct { int i; char selected; char wait; int time; struct GSYM *g; } vtb[MAXVOICE]; int bars; /* allocate the pointer table */ i1 = 0; for (voice = nvoice + 1; --voice >= 0; ) i1 += voice_tb[voice].nsym; ngsym = i1; /* Changed to allocate one extra slot for the oddball case that was */ /* causing errors when two consecutive info fields, one being a part */ /* were encountered in a tune body. */ gsym = calloc(sizeof *gsym, i1 + 1); memset(vtb, 0, sizeof vtb); /* sort the symbol by time and put them in the pointer table */ l1 = 0; ig = -1; seq = 0; bars = 0; for (voice = 0; voice <= nvoice; voice++) { if (voice_tb[voice].nsym == 0) { #if 0 char strMsg[80]; sprintf(strMsg, "Cannot continue, voice %d is empty: ", voice); rx(strMsg, voice_tb[voice].name); #else printf(">>> voice %s not used\n", voice_tb[voice].name); continue; #endif } ig++; gsym[ig].s = &voice_tb[voice].sym[0]; gsym[ig].is = 0; gsym[ig].voice = voice; gsym[ig].seq = seq; gsym[ig].time = 0; vtb[voice].g = &gsym[ig]; vtb[voice].time = voice_tb[voice].sym[0].len; vtb[voice].i = 1; } for (;;) { int ok; int nbbars; int extra; /* search the next symbol in time */ l1 += 1000; ok = 0; for (voice = 0; voice <= nvoice; voice++) { vtb[voice].selected = 0; sym = voice_tb[voice].sym; if (vtb[voice].i < voice_tb[voice].nsym && vtb[voice].time < l1 && !vtb[voice].wait) { ok = 1; l1 = vtb[voice].time; } } if (!ok) break; /* echu (finished) */ /* put the symbols with no length (but visible bars) before the notes */ extra = 0; nbbars = 0; for (voice = 0; voice <= nvoice; voice++) { nsym = voice_tb[voice].nsym; sym = voice_tb[voice].sym; i1 = vtb[voice].i; if (i1 >= nsym || vtb[voice].time > l1) continue; vtb[voice].selected = 1; vtb[voice].wait = 0; switch (sym[i1].type) { case BAR: if (sym[i1].u != B_INVIS) { nbbars++; break; } /*fall thru*/ case TIMESIG: ah_shaddup = !stricmp(sym[i1].text, "free"); case CLEF: case KEYSIG: extra = 1; break; } } while (extra) { seq++; extra = 0; nbbars = 0; for (voice = 0; voice <= nvoice; voice++) { struct SYMBOL *s; if (!vtb[voice].selected) continue; i1 = vtb[voice].i; s = &voice_tb[voice].sym[i1]; switch (s->type) { case TIMESIG: ah_shaddup = !stricmp(s->text, "free"); /* fall thru */ case CLEF: case KEYSIG: break; case BAR: if (s->u == B_INVIS) break; /*fall thru*/ nbbars++; default: continue; } vtb[voice].i = i1 + 1; ig++; vtb[voice].g = &gsym[ig]; gsym[ig].s = s; gsym[ig].is = i1; gsym[ig].voice = voice; gsym[ig].seq = seq; gsym[ig].time = l1; if (i1 + 1 < voice_tb[voice].nsym) { switch (s[1].type) { case BAR: if (s[1].u != B_INVIS) { nbbars++; break; } /*fall thru*/ case TIMESIG: ah_shaddup = !stricmp(s[1].text, "free"); /* fall thru */ case CLEF: case KEYSIG: extra = 1; break; } } } } /* if bars, check if all are there */ if (nbbars > 0) { if (nbbars == nvoice + 1) { bars++; } else { if (!ah_shaddup) { printf(">> Incorrect number of notes in measure %d\n", bars); } for (voice = 0; voice <= nvoice; voice++) { if (!vtb[voice].selected) continue; i1 = vtb[voice].i; if (voice_tb[voice].sym[i1].type == BAR) { vtb[voice].selected = 0; vtb[voice].wait = 1; } } } } /* set the pointers */ seq++; for (voice = 0; voice <= nvoice; voice++) { struct SYMBOL *s; if (!vtb[voice].selected) continue; i1 = vtb[voice].i; s = &voice_tb[voice].sym[i1]; switch (s->type) { case NOTE: case REST: if (s->p_plet != 0) /* start of a n-plet */ set_nplet(s); break; } vtb[voice].time = l1 + s->len; vtb[voice].i = i1 + 1; ig++; vtb[voice].g = &gsym[ig]; gsym[ig].s = s; gsym[ig].is = i1; gsym[ig].voice = voice; gsym[ig].seq = seq; gsym[ig].time = l1; } /* and associate the symbols on a same staff */ for (voice = 0; voice <= nvoice; voice++) { struct SYMBOL *s; if (!vtb[voice].selected) continue; i1 = vtb[voice].i - 1; s = &voice_tb[voice].sym[i1]; if (voice > 0 && s->staff == vtb[voice-1].g->s->staff) { if (vtb[voice].g->g == 0) vtb[voice].g->g = vtb[voice-1].g; if (vtb[voice-1].g->g == 0) vtb[voice-1].g->g = vtb[voice].g; } if (voice < nvoice && s->staff == vtb[voice+1].g->s->staff) { if (vtb[voice].g->g == 0) vtb[voice].g->g = vtb[voice+1].g; if (vtb[voice+1].g->g == 0) vtb[voice+1].g->g = vtb[voice].g; } } } /* Changed from "!=" to avoid error with internal info-fields and parts. [JSA] */ if (ig < ngsym - 1) rx("Internal bug: ", "some symbols undefined"); } /* ----- set staves and stems when multivoice ----- */ /* this function is called only once for the whole tune */ /* it supposes that the first symbol of each voice is the clef */ /* (this code is limited to 2 voices per staff) */ static void set_multi(void) { int i1, i2; int ig; int staff; int voice; struct SYMBOL *sym1; int nsym1; char staff_clef[MAXSTAFF]; struct SYMBOL *s; int staff2; int pitch; int in_beam; /* set the clefs - when a floating voice, set the symbols on the right staff */ for (voice = 0; voice <= nvoice; voice++) { staff = voice_tb[voice].staff; sym1 = voice_tb[voice].sym; nsym1 = voice_tb[voice].nsym; /* set the clef */ if (!voice_tb[voice].second /* if not a secondary voice */ && !voice_tb[voice].forced_clef) /* and no explicit clef */ set_clef(voice); /* set the main clef */ /* set the pitch of rests */ for (i1 = nsym1, s = sym1; --i1 >= 0; s++) { if (s->type == NOTE) { pitch = s->pits[0]; break; } } for (i1 = nsym1, s = sym1; --i1 >= 0; s++) { if (s->type == NOTE) { pitch = s->pits[0]; if (s[-1].type != NOTE) s[-1].pits[0] = (s[-1].pits[0] + pitch) / 2; } else s->pits[0] = pitch; } /* treat the floating voices */ if (!voice_tb[voice].floating) continue; /* get the beginning staff */ staff2 = staff; for (i1 = nsym1, s = sym1; --i1 >= 0; s++) { if (s->type == NOTE) { if (s->pits[(int) s->npitch - 1] < 16 && s[1].pits[0] < 16) staff2 = staff + 1; break; } } /* set the staff of notes */ in_beam = 0; for (i1 = nsym1, s = sym1; --i1 >= 0; s++) { if (s->word_st && !s->word_end) in_beam = 1; /* fixme:it's more complicated*/ if (staff2 == staff) { int xp = s->npitch - 1; if ((s->type == NOTE || s->type == REST) && (!in_beam || s->flags == 0) && (s->pits[xp] <= 13 /* 'G,' */ || (s->pits[xp] <= 15 /* 'B,' */ && s[1].pits[0] <= 13))) s->staff = ++staff2; } else { if ((s->type == NOTE || s->type == REST) && (!in_beam || s->flags == 0) && (s->pits[0] >= 19 /* 'F' */ || (s->pits[0] >= 17 /* 'D' */ && s[1].pits[0] >= 19))) staff2--; else s->staff = staff2; } s->in_beam = in_beam; if (s->word_end) in_beam = 0; /*fixme: ajust rests before note if staff change*/ } } /* sort the notes by time */ def_gsym(); /* set the starting clefs */ for (voice = 0; voice <= nvoice; voice++) voice_tb[voice].sym[0].u = staff_tb[(int) voice_tb[voice].staff].clef; /* adjust the pitch of the notes */ for (staff = 0; staff <= nstaff; staff++) staff_clef[staff] = staff_tb[staff].clef; for (ig = 0; ig < ngsym; ig++) { int delta; s = gsym[ig].s; staff = s->staff; switch (s->type) { case CLEF: staff_clef[staff] = s->u; continue; default: continue; case NOTE: break; } delta = 0; switch (staff_clef[staff]) { case BASS: delta = 7 + 5; /* A(0) --> F(1) */ break; case ALTO: delta = 6; /* G(0) --> F(1) */ break; } if (delta > 0) { for (i2 = s->npitch; --i2 >= 0; ) s->pits[i2] += delta; for (i2 = s->gr.n; --i2 >= 0; ) s->gr.p[i2] += delta; } } /* set the stems direction (in 'multi') */ /* for the rests, 'u' is used to adjust the height in the staff */ for (ig = 0; ig < ngsym; ig++) { struct SYMBOL *s, *s2; int h; if (gsym[ig].g == 0) continue; /* no associated symbol */ s = gsym[ig].s; s2 = gsym[ig].g->s; if ((s->len != s2->len) || !s->StemDir) { s->multi = gsym[ig].voice < gsym[ig].g->voice ? 1 : -1; } if (s->type == REST) { #if 0 /*fixme: does not work*/ if (s->multi > 0) { h = 3 * ((s2->pits[s2->npitch-1] & ~3) - 18) + 12; if (h < 18) h = 18; } else { h = 3 * ((s2->pits[0] & ~3) - 18) - 12; if (h > 6) h = 6; } #else if (s->multi > 0) { if (s2->type == NOTE) { h = 3 * ((s2->pits[s2->npitch-1] & ~3) - 18) + 12; if (h < 18) h = 18; } else h = 18; } else { if (s2->type == NOTE) { h = 3 * ((s2->pits[0] & ~3) - 18) - 12; if (h > 6) h = 6; } else h = 6; } #endif s->u = h; } } nbar = 1; /* for measure numbering */ } /* ----- set_staff: set the y offset of the staves and return the whole height ----- */ /* Modified by J. S. Atchley, 10/99, to also set the y offset of whistle tablature. */ static float set_staff(void) { int i; int j, SkipStaff; /* [JSA] */ float y = cfmt.LineSep; float staffsep; float yword; /* Whistle tablature is printed under the last stave used. [JSA] */ for (i = nWhistleTab - 1; i >= 0; i--) { if (!VoiceMute_tb[WhistleTab_tb[i].voice]) { WhistleTab_tb[i].y = y; y += 56; } } /* String tablature comes next. [JSA] */ for (i = nStringTab - 1; i >= 0; i--) { if (!VoiceMute_tb[StringTab_tb[i].voice]) { StringTab_tb[i].y = y; y += ((cfmt.StrTabFont.size / (float)2.0) + (StringTab_tb[i].nstrings * (cfmt.StrTabFont.size * (float)1.5))); } } /* Do staves from bottom up. */ staffsep = (float)0.5 * cfmt.staffsep; for (i = nstaff; i >= 0; i--) { /* Skip staff if all of its voices are muted. [JSA] */ SkipStaff = TRUE; for (j = 0; j <= nvoice; j++) { if ((voice_tb[j].staff == i) && !VoiceMute_tb[j]) { SkipStaff = FALSE; break; } } if (SkipStaff) continue; /* bottom */ if (staff_tb[i].nvocal > 0 && !cfmt.musiconly) { yword = cfmt.vocalspace; if (cfmt.vocalfont.size - staff_tb[i].botpos > yword) yword = cfmt.vocalfont.size - staff_tb[i].botpos; y += staffsep + yword + (float)1.1 * cfmt.vocalfont.size * (staff_tb[i].nvocal - 1); } else { if (-staff_tb[i].botpos > staffsep) y += -staff_tb[i].botpos; else y += staffsep; } staff_tb[i].y = y; y += STAFFHEIGHT; if (i == 0) staffsep = (float)0.5 * cfmt.staffsep; else staffsep = (float)0.5 * (float)0.8 * cfmt.staffsep; /* top */ if (staff_tb[i].toppos > staffsep) y += staff_tb[i].toppos; else y += staffsep; } return y; } /* ----- set_sym_chars: set symbol characteristics --- */ /* this function is called only once for the whole tune */ static void set_sym_chars(int voice) { int i,np,m,ymn,ymx; float yav,yy; struct SYMBOL *s; for (i = voice_tb[voice].nsym, s = voice_tb[voice].sym; --i >= 0; s++) { switch (s->type) { case REST: /* if 'multi' is not null, 'u' contains the desired * vertical position (see set_multi) */ if (s->multi == 0) s->y = 12.; else s->y = (float)s->u; break; case NOTE: yav = 0; ymn = 1000; ymx = -1000; np = s->npitch; for (m = 0;m < np; m++) { yy = (float)3 * (s->pits[m] - 18); yav += yy / np; if (yy < ymn) ymn = (int)yy; if (yy > ymx) ymx = (int)yy; } s->y = (float)3 * (s->pits[0] - 18); s->ymn = ymn; s->ymx = ymx; s->yav = (int)yav; s->ylo = (float)ymn - (float)5.0; s->yhi = (float)ymx + (float)3.0; break; } } } /* ----- set_beams: decide on beams ---- */ /* this routine is called once per tune */ static void set_beams(int voice) { int i,start_flag; struct SYMBOL *s, *lastnote; /* separate words at notes without flags */ start_flag = 0; lastnote = 0; for (i = voice_tb[voice].nsym, s = voice_tb[voice].sym; --i >= 0; s++) { if ((s->type == NOTE) || (s->type == COMBINEDNOTE)) { if (start_flag) { s->word_st = 1; start_flag = 0; } if (s->flags == 0) { if (lastnote != 0) lastnote->word_end = 1; s->word_st = 1; s->word_end = 1; start_flag = 1; } lastnote = s; } } /* separate words before and after n-plet */ if (CUT_NPLETS) { int num,nplet; num = nplet = 0; lastnote = 0; for (i = voice_tb[voice].nsym, s = voice_tb[voice].sym; --i >= 0; s++) { if (s->type==NOTE) { num++; if (nplet && num == nplet) { if (lastnote != 0) lastnote->word_end = 1; s->word_st = 1; } if (s->p_plet) { nplet = s->r_plet; num = 0; if (lastnote != 0) lastnote->word_end = 1; s->word_st = 1; } lastnote = s; } } } } /* ----- set_overlap: set a shift when notes overlap ----- */ static void set_overlap(void) { int ig; for (ig = ig_st; ig < ig_end; ig++) { struct SYMBOL *s, *s2; int d; if (gsym[ig].g == 0 /* no associated symbol */ || gsym[ig].seq != gsym[ig].g->seq /* not same sequence */ || gsym[ig].g < &gsym[ig]) /* already done */ continue; s = gsym[ig].s; s2 = gsym[ig].g->s; d = s->pits[0] - s2->pits[0]; if (d < 0 || d == 1 || (d == 0 && s->len != s2->len && (s->len != QUARTER || s2->len != EIGHTH) && (s->len != EIGHTH || s2->len != QUARTER))) { int l; float dx; if (d >= 0) dx = 4.; else dx = -2.; for (l = s->npitch; --l >= 0; ) s->shhd[l] -= dx; s->xmx -= dx; for (l = s2->npitch; --l >= 0; ) s2->shhd[l] += dx; s2->xmx += dx; } } } /* ----- set_stems: decide on stem directions and lengths ---- */ /* We have already determined if we have any unisons (stem up and down) */ /* before we get here. Now we can adjust the staff height to account */ /* for the pesky buggers! */ static void set_stems(void) { int beam, j, laststem, stem; float lasty = 0; struct SYMBOL *s; int staff; float slen; /* set stem directions; near middle, use previous direction */ beam=0; stem = 0; /* (compilation warning) */ laststem=0; for (j = 0, s = sym; j < nsym; j++, s++) { if (s->type != NOTE) { laststem=0; continue; } // if (s->StemDir) { /* Stem set explicitly from %%jsastem=dn|def|up */ // s->stem = s->StemDir; // if (s->word_st && !s->word_end) beam = 1; /* start of beam */ // } else if (s->multi == 0) { /* if not set by set_multi() */ // s->stem=0; if (s->multi == 0) { /* if not set by set_multi() */ if (s->StemDir) { /* Stem set explicitly from %%jsastem=dn|def|up */ s->stem = s->StemDir; if (s->word_st && !s->word_end) beam = 1; /* start of beam */ continue; } s->stem=0; if (s->len < WHOLE) s->stem=1; if (s->yav>=12) s->stem=-s->stem; if ((s->yav>11) && (s->yav<13) && (laststem!=0)) { float dy; dy=s->yav-lasty; if ((dy>-7) && (dy<7)) s->stem=laststem; } if (s->word_st && !s->word_end) { /* start of beam */ float avg; int n, k; avg=0; n=0; for (k=j;k=12) stem=-1; if ((avg>11) && (avg<13) && (laststem!=0)) stem=laststem; beam=1; } if (beam) s->stem=stem; } else { /* stem set by set_multi */ s->stem = s->multi > 0 ? 1 : -1; if (s->word_st && !s->word_end) beam = 1; /* start of beam */ } if (s->word_end) beam=0; if (bagpipe) s->stem=-1; if (s->len>=WHOLE) s->stem=0; laststem=s->stem; if (s->len>=HALF) laststem=0; lasty=(float)s->yav; } for (j = 0, s = sym; j < nsym; j++, s++) { if (s->type != NOTE) continue; /* shift notes in chords (need stem direction to do this) */ set_head_directions(s); /* set height of stem end, without considering beaming for now */ staff = s->staff; slen=STEM; if (s->npitch > 1) slen=STEM_CH; if (s->flags==3) { slen += 4; } else if (s->flags==4) { slen += 9; } if (s->stem > 0) { /* Stem is going up */ if (s->flags > 2) slen -= 1; if (s->pits[0] > 26) { slen -= 2; if (s->pits[0] > 28) slen -= 2; } s->y = (float)s->ymn; s->ys = s->ymx + slen; s->ylo = s->y - (float)3.5; if (s->Unison) s->ylo -= STEM; s->yhi = s->ys; } else if (s->stem < 0) { /* Stem is going down */ if (s->pits[0] < 18) { slen -= 2; if (s->pits[0] < 16) slen -= 2; } s->y = (float)s->ymx; s->ys = s->ymn - slen; s->yhi = s->y + (float)3.5; if (s->Unison) s->yhi += STEM; s->ylo = s->ys; } else { s->ys = s->y = (float)s->ymx; s->ylo = (float)s->ymn - (float)3.5; s->yhi = (float)s->ymx + (float)3.5; } if (staff_tb[staff].toppos < (s->yhi + 1. - STAFFHEIGHT)) { staff_tb[staff].toppos = s->yhi + (float)1. - STAFFHEIGHT; } if (staff_tb[staff].botpos > (s->ylo - 1.)) { staff_tb[staff].botpos = s->ylo - (float)1.; } } } /* ----- set_sym_widths: set widths and prefered space --- */ /* This routine sets the minimal left and right widths wl,wr so that successive symbols are still separated when no extra glue is put between them. It also sets the prefered spacings pl,pr for good output and xl,xr for expanded layout. All distances in pt relative to the symbol center. */ #define AT_LEAST(a,b) if((a)<(b)) a=b; static void set_sym_widths(void) { int i,j,m,n1,n2,k,sl,k1,k2; float xx,w,swfac,spc; char t[81]; swfac=1.0; if (strstr(cfmt.vocalfont.name,"Times-Roman")) swfac=(float)1.00; else if (strstr(cfmt.vocalfont.name,"Times-Bold")) swfac=(float)1.05; else if (strstr(cfmt.vocalfont.name,"Helvetica")) swfac=(float)1.10; else if (strstr(cfmt.vocalfont.name,"Helvetica-Bold")) swfac=(float)1.15; for (i=0;itype) { case INVISIBLE: /* empty space; shrink,space,stretch from u,v,w */ s->wl=s->wr=(float)0.5*s->u; s->pl=s->pr=(float)0.5*s->v; s->xl=s->xr=(float)0.5*s->w; break; case NOTE: case REST: case COMBINEDNOTE: case COMBINEDREST: s->wl=s->wr=4.5; s->pl=s->pr=nwidth(s->len); s->xl=s->xr=xwidth(s->len); if (s->head==H_EMPTY) { s->wl=6.0; s->wr=14.0; } else if (s->head==H_OVAL) { s->wl=8.0; s->wr=18.0; } /* room for shifted heads and accidental signs */ for (m=0;mnpitch;m++) { xx=s->shhd[m]; AT_LEAST(s->wr, xx+6); AT_LEAST(s->wl, -xx+6); if (s->accs[m]) { xx -= s->shac[m]; AT_LEAST(s->wl, -xx+3); } AT_LEAST(s->xl, -xx+3); s->pl=s->wl; s->xl=s->wl; } /* room for slide */ for (k=0;kdc.n;k++) { if (s->dc.t[k]==D_SLIDE) s->wl += 10; } /* room for grace notes */ if (s->gr.n>0) { xx=GSPACE0; if (s->gr.a[0]) xx += 3.5; for (j=1;jgr.n;j++) { xx += GSPACE; if (s->gr.a[j]) xx += 4; } s->wl += xx + 1; s->pl += xx + 1; s->xl += xx + 1; } /* space for flag if stem goes up on standalone note */ if (s->word_st && s->word_end && s->stem > 0 && s->flags > 0) AT_LEAST(s->wr, 12); /* leave room for dots */ if (s->dots>0) { AT_LEAST(s->wr,12+s->xmx); if (s->dots>=2) s->wr += 3.5; /* special case: standalone with up-stem and flags */ if (s->flags && s->stem > 0 && !((int) s->y % 6)) if (s->word_st && s->word_end) { s->wr += DOTSHIFT; s->pr += DOTSHIFT; s->xr += DOTSHIFT; } } /* extra space when down stem follows up stem */ if ((prev = preceded_by_note(s)) != 0) { if (prev->stem > 0 && s->stem < 0) AT_LEAST(s->wl, 7); /* make sure helper lines don't overlap */ if (s->y > 27 && prev->y > 27) AT_LEAST(s->wl, 7.5); } /* leave room guitar chord */ if (s->text != 0) { /* special case: guitar chord under ending 1 or 2 */ /* leave some room to the left of the note */ if ((i>0) && (sym[i-1].type==BAR)) { if (sym[i-1].v) AT_LEAST(s->wl, 18); } /* rest is same for all guitar chord cases */ tex_str(s->text, t, &w); xx=cfmt.gchordfont.size*w; spc=xx*GCHPRE; k1=prec_note(i); k2=next_note(i); if (spc>8.0) spc=8.0; if (k1 > 0 && sym[k1].text != 0) AT_LEAST(s->wl, spc); if (k2 > 0 && sym[k2].text != 0) AT_LEAST(s->wr, xx-spc); } /* leave room for vocals under note */ if (!cfmt.musiconly) { for (j=0;jwordp[j]) { sl=tex_str(s->wordp[j],t,&w); xx = swfac*cfmt.vocalfont.size * (w + 2 * cwid(' ')); AT_LEAST(s->wl,xx*VOCPRE); AT_LEAST(s->wr,xx*((float)1.0-VOCPRE)); } } } AT_LEAST(s->pl, s->wl); AT_LEAST(s->xl, s->wl); AT_LEAST(s->pr, s->wr); AT_LEAST(s->xr, s->wr); break; case BAR: switch (s->u) { case B_SNGL: s->wl=s->wr=3; break; case B_DBL: s->wl=7; s->wr=4; break; case B_LREP: s->wl=5; s->wr=12; break; case B_RREP: s->wl=12; s->wr=5; break; case B_DREP: s->wl=s->wr=12; break; case B_FAT1: s->wl=3; s->wr=9; break; case B_FAT2: s->wl=9; s->wr=3; break; case B_INVIS: s->wl=s->wr=0; break; } s->pl=s->xl=s->wl; s->pr=s->xr=s->wr; break; case CLEF: s->wl=s->wr=s->xl=12; s->pl=s->pr=s->xr=12; break; case KEYSIG: n1=s->u; n2=s->v; if (n2>=n1) { s->wl=5; s->wr=(float)5*(n2-n1+1)+5; s->pl=s->xl=s->wl; s->pr=s->xr=s->wr; } else { s->wl=s->pl=s->xl=3; s->wr=s->pr=s->xr=3; } break; case TIMESIG: if (stricmp(s->text, "free")) { /* Not free meter, use original width calculation. */ s->wl = (float)8 + 4 * (strlen(s->text) - 1); } else { /* Free meter, calculate width based on display style. */ switch (cfmt.FreeMetStyle) { case 1: /* "free" */ s->wl = (float)20; break; case 2: /* "free" over "meter" */ s->wl = (float)24; break; case 3: /* "fm" */ s->wl = (float)12; break; case 4: /* nothing */ s->wl = (float)0; break; default: /* 0 = "none" */ s->wl = (float)20; } } if (s->wl != (float)0) s->wr = s->wl + 4; s->pl = s->xl = s->wl; s->pr = s->xr = s->wr; break; default: printf(">>> cannot set width for sym type %d\n", s->type); s->wl=s->wr=s->xl=0; s->pl=s->pr=s->xr=0; break; } } } /* ----- set_piece: set the end of a piece of tune */ /* ig_end is the new end in gsym */ static void set_piece(void) { int ig, voice; if (ig_end < ngsym && gsym[ig_end].s->type == KEYSIG) { int seq; /* signature change on the next line, * put it at the end of the current line */ /*fixme: may overflow*/ seq = gsym[ig_end].seq; while (ig_end < ngsym && gsym[ig_end].seq == seq) ig_end++; } for (voice = nvoice + 1 ; --voice >= 0; ) { for (ig = ig_end; --ig >= ig_st; ) if (gsym[ig].voice == voice) { voice_tb[voice].nsym = gsym[ig].is + 1 - voice_tb[voice].nsym0; gsym[ig].s->last = 1; break; } } } /* ----- set_spacing: set the basic spacing before symbol ----- */ static void set_spacing(struct SYMBOL *s, struct VOICE_P *voice_p) { struct SYMBOL *prev; float w0, w1, w2; s->shrink = s[-1].wr + s->wl; s->space = s[-1].pr + s->pl; s->stretch = s[-1].xr + s->xl; prev = preceded_by_note(s); switch (s->type) { case NOTE: case REST: case COMBINEDNOTE: case COMBINEDREST: if (prev != 0) { /* two notes behind each other */ w1 = lnnp * nwidth(prev->len); w2 = lnnp * nwidth(s->len); s->space = bnnp * w1 + (1 - bnnp) * (float)0.5 * (w1 + w2); w1 = lnnx * xwidth(prev->len); w2 = lnnx * xwidth(s->len); s->stretch = bnnx * w1 + (1 - bnnx) * (float)0.5 * (w1 + w2); /* squeeze notes a bit if big jump in pitch */ if (((s->type == NOTE) || (s->type == COMBINEDNOTE)) && ((prev->type == NOTE) || (prev->type == COMBINEDNOTE))) { float dy, fac; dy = s->y - prev->y; if (dy < 0) dy =- dy; fac = (float)1. - (float)0.010 * dy; if (fac < 0.9) fac = (float)0.9; s->space *= fac; s->stretch *= fac; } } else { /* note at start of bar */ int vbnp = (int)((rbnp * meter1 * BASE) / meter2); int vbnx = (int)((rbnx * meter1 * BASE) / meter2); w1 = lbnp * nwidth(s->len); w0 = lbnp * nwidth(vbnp); s->space = bbnp * w1 + (1 - bbnp) * w0 + s[-1].pr; w1 = lbnx * xwidth(s->len); w0 = lbnx * xwidth(vbnx); s->stretch = bbnx * w1 + (1 - bbnx) * w0 + s[-1].xr; voice_p->s0 = s; /* remember start of bar */ voice_p->nn = 0; /* counter for notes in bar */ } if (!s->word_st) { /* reduce spacing within a beam */ s->space *= fnnp; s->stretch *= fnnx; } if (s->p_plet>0) /* reduce spacing in n-plet */ voice_p->nplet = s->r_plet - 1; else if (voice_p->nplet > 0) { voice_p->nplet--; s->space *= gnnp; s->stretch *= gnnx; } voice_p->nn++; voice_p->ncount++; if (voice_p->bars < 0) voice_p->bars = 0; break; default: break; case BAR: { int vnbp = (int)((rnbp * meter1 * BASE) / meter2); int vnbx = (int)((rnbx * meter1 * BASE) / meter2); if (prev == 0) break; /* end of bar reached */ w1 = lnbp * nwidth(prev->len); w0 = lnbp * nwidth(vnbp); s->space = bnbp * w1 + (1 - bnbp) * w0 + s->pl; w1 = lnbx * xwidth(prev->len); w0 = lnbx * xwidth(vnbx); s->stretch = bnbx * w1 + (1 - bnbx) * w0 + s->xl; if (voice_p->nn == 1 /* only one note in measure */ && nvoice == 0) { /* (and only 1 voice in the tune) */ /* special treatment only if length at least one-half measure */ if ((2*prev->len) >= ((meter1*BASE)/meter2)) { w0 = (float)0.5*ln0p*nwidth(prev->len); voice_p->s0->space = bn0p*w0 + (1-bn0p) * voice_p->s0->space; s->space = bn0p*w0 + (1-bn0p)*s->space; w0 = (float)0.5*ln0x*xwidth(prev->len); voice_p->s0->stretch = bn0x*w0 + (1-bn0x) * voice_p->s0->stretch; s->stretch = bn0x*w0 + (1-bn0x)*s->stretch; } } break; } } } /* ----- set_sym_glue --------- */ /* Sets the characteristics of the glue between symbols, then positions the symbols along the staff. If staff is overfull, only does symbols which fit in, returns this number. */ static int set_sym_glue(float width, float *realwidth) { float alfa0, beta0; float alfa, beta; int cut = 0; int voice, ig, seq; struct SYMBOL *sym; int nsym; int i1; struct VOICE_P voice_p[MAXVOICE]; float space, shrink, stretch; float w; struct SYMBOL *s; int prev_len; float next_space, next_stretch; alfa0 = ALFA_X; /* max shrink and stretch */ if (cfmt.continueall) alfa0 = cfmt.maxshrink; if (gmode == G_SHRINK || gmode == G_STRETCH) alfa0 = 1.0; else if (gmode == G_SPACE) alfa0 = 0.0; beta0 = BETA_X; if (cfmt.continueall) beta0 = BETA_C; memset(voice_p, 0, sizeof voice_p); /* advance in time */ ig = ig_st; /* set spacing for the first symbols (1 for each voice) */ seq = gsym[ig].seq; s = gsym[ig].s; space = s->space; shrink = s->shrink; stretch = s->stretch; while (ig < ig_end && gsym[ig].seq == seq) { s = gsym[ig].s; voice = gsym[ig].voice; s->space = s->pl; s->shrink = s->wl; s->stretch = s->xl; voice_p[voice].space = space; voice_p[voice].shrink = shrink; voice_p[voice].stretch = stretch; /* set spacing for the next note */ set_spacing(s + 1, &voice_p[voice]); ig++; } prev_len = 0; next_space = s[1].space; next_stretch = s[1].stretch; /* then loop over the symbols */ while (ig < ig_end) { float min_space, min_shrink; int i, ig2, ig3, len; /* get the notes at this time, set spacing * and get the min shrinking */ cut = 0; seq = gsym[ig].seq; min_space = 1000.; min_shrink = shrink; len = gsym[ig].time - gsym[ig - 1].time; if (prev_len > 0) { next_space *= (float) len / prev_len; next_stretch *= (float) len / prev_len; } for (ig2 = ig; ig2 < ig_end; ig2++) { if (gsym[ig2].seq != seq) break; voice = gsym[ig2].voice; s = gsym[ig2].s; /* if the previous symbol length is greater than the length from the previous elements, adjust */ if (s[-1].len > len) { s->space = next_space; s->stretch = next_stretch; s->shrink = s->wl; } /* make sure that shrink <= space <= stretch */ if (s->space < s->shrink) s->space = s->shrink; if (s->stretch < s->space) s->stretch = s->space; if (voice_p[voice].shrink + s->shrink > min_shrink) min_shrink = voice_p[voice].shrink + s->shrink; } /* get the smallest spacing, greater than minimal shrinking */ ig3 = ig; for (i = ig; i < ig2; i++) { s = gsym[i].s; voice = gsym[i].voice; if (s->space < min_space && voice_p[voice].shrink + s->shrink >= min_shrink) { min_space = s->space; ig3 = i; } } /* set the horizontal position goal */ s = gsym[ig3].s; space += s->space; shrink += s->shrink; stretch += s->stretch; /* adjust spacing and advance */ prev_len = 512; next_space = 1000.; for ( ;ig < ig2; ig++) { s = gsym[ig].s; voice = gsym[ig].voice; s->space = space - voice_p[voice].space; s->shrink = shrink - voice_p[voice].shrink; s->stretch = stretch - voice_p[voice].stretch; /* if a single rest in the measure, center */ if (s->type == BAR && voice_p[voice].nn == 1 && s[-1].type == REST) { s[-1].space = s->space = (s[-1].space + s->space) * (float)0.5; s[-1].stretch = s->stretch = (s[-1].stretch + s->stretch) * (float)0.5; if (s[-1].len > HALF) { s[-1].head = H_OVAL; s[-1].dots = 0; } } voice_p[voice].space = space; voice_p[voice].shrink = shrink; voice_p[voice].stretch = stretch; if (verbose > 21) printf("glue [%d:%d] %d (%.1f,%.1f,%.1f)" " shr,sp %.1f %.1f %d %d\n", voice, gsym[ig].is, s->type, s->shrink, s->space, s->stretch, shrink, space, voice_p[voice].bars, voice_p[voice].ncount); /* set the spacing for the next note and keep info about the shortest element */ if (s->len < prev_len) prev_len = s->len; if (!s->last) { set_spacing(s + 1, &voice_p[voice]); if (s->len == prev_len && s[1].space < next_space) { next_space = s[1].space; next_stretch = s[1].stretch; } } else { if (s->len == prev_len && s->space < next_space) { next_space = s->space; next_stretch = s->stretch; } } /* count the number of bars */ if (s->type == BAR && s->u != B_INVIS && voice_p[voice].ncount > 0 && voice_p[voice].bars >= 0) voice_p[voice].bars++; } if ((s == 0) || (s->eoln && !cfmt.continueall && (cfmt.barsperstaff == 0))) break; /* check total width */ alfa = 0; if (space > shrink) alfa = (space - width) / (space - shrink); if (alfa > alfa0 && (voice_p[voice].ncount > 1 || voice_p[voice].bars > 0)) { if (!cfmt.continueall || verbose > 4) { if (verbose <= 3) printf("\n"); printf("++++ Overfull after %d bar%s in staff %d voice %d\n", voice_p[voice].bars, voice_p[voice].bars == 1 ? "" : "s", mline, voice); } cut = 1; break; } } /* if cut, get back to the previous bar */ if (cut) { int i; for (i = ig; --i > ig_st; ) if (gsym[i].s->type == BAR) break; ig_end = i > ig_st ? i + 1 : ig; set_piece(); } /* compute the glue on the voice 0 */ nsym = voice_tb[0].nsym; sym = &voice_tb[0].sym[voice_tb[0].nsym0]; space = shrink = stretch = 0; for (i1 = 0; i1 < nsym; i1++) { space += sym[i1].space; shrink += sym[i1].shrink; stretch += sym[i1].stretch; } /* if last symbol is not a bar, add preceding space again */ i1 = nsym - 1; if (sym[i1].type != BAR) { shrink += sym[i1].shrink; space += sym[i1].space; stretch += sym[i1].stretch; } /* ---- set the glue, calculate final symbol positions ---- */ if (verbose > 9) printf("Output width %.2f, shrink,space,stretch %.2f, %.2f, %.2f\n", width, shrink, space, stretch); alfa = beta = 0; if (space > width) { alfa = 99; if (space > shrink) alfa = (space - width) / (space - shrink); } else { beta = 99; if (stretch > space) beta = (width - space) / (stretch - space); } if (verbose > 12) printf("start with alfa=%.2f, b=%.2f\n", alfa, beta); if (gmode == G_SHRINK) { alfa=1; beta=0; /* force minimal spacing */ } else if (gmode==G_STRETCH) { alfa=0; beta=1; /* force stretched spacing */ } else if (gmode==G_SPACE) { alfa=beta=0; /* force natural spacing */ } if (alfa > alfa0) { alfa = alfa0; beta = 0; } if (beta > beta0) { if (!cfmt.continueall || cut) { /*??*/ if (verbose <= 3) printf("\n"); printf("++++ Underfull (%.0fpt of %.0fpt) in staff %d\n", (beta0 * stretch + (1 - beta0) * space) * cfmt.scale, cfmt.staffwidth, mline); } alfa = 0; if (!cfmt.stretchstaff) beta=0; if (!cfmt.stretchlast && voice_tb[0].nsym0 != 0) { /*??*/ alfa=alfa_last; /* shrink underfull last line same as previous */ beta=beta_last; } } if (verbose > 12) printf("now alfa=%.3f, beta=%.3f\n", alfa, beta); w = alfa * shrink + beta * stretch + (1 - alfa - beta) * space; if (verbose>=3) { if (alfa>0) printf("Shrink staff %.0f%%", 100*alfa); else if (beta>0) printf("Stretch staff %.0f%%", 100*beta); else printf("No shrink or stretch"); printf(" to width %.0f (%.0f,%.0f,%.0f)\n",w,shrink,space,stretch); } for (voice = nvoice + 1 ; --voice >= 0; ) { float x; nsym = voice_tb[voice].nsym; sym = &voice_tb[voice].sym[voice_tb[voice].nsym0]; x = 0; for (i1 = 0; i1 < nsym; i1++) { x += alfa * sym[i1].shrink + beta * sym[i1].stretch + (1 - alfa - beta) * sym[i1].space; sym[i1].x = x; if (verbose > 22) printf("pos[%d:%d]: type=%d pos=%.2f\n", voice, i1, sym[i1].type, x); } } /* add small random shifts to positions */ if (nvoice == 0) { for (i1 = 1; i1 < nsym-1; i1++) { if (sym[i1].type == NOTE || sym[i1].type == REST) { float w1, w2; w1 = sym[i1].x - sym[i1-1].x; w2 = sym[i1+1].x - sym[i1].x; if (w2 < w1) w1 = w2; sym[i1].x += RANFAC * ranf(-w1, w1); } } } alfa_last = alfa; beta_last = beta; *realwidth = w; return cut; } /* ----- draw_left: draw the left side of the staves ----- */ static void draw_left(void) { int i; int j, SkipStaff; float LowestStaffY, HighestStaffY; /* Draw only for staffs actually used. [JSA] */ for (i = 0; i <= nstaff; i++) { SkipStaff = TRUE; for (j = 0; j <= nvoice; j++) { if ((voice_tb[j].staff == i) && !VoiceMute_tb[j]) { SkipStaff = FALSE; break; } } if (!SkipStaff) LowestStaffY = staff_tb[i].y; } for (i = nstaff; i >= 0; i--) { SkipStaff = TRUE; for (j = 0; j <= nvoice; j++) { if ((voice_tb[j].staff == i) && !VoiceMute_tb[j]) { SkipStaff = FALSE; break; } } if (!SkipStaff) HighestStaffY = staff_tb[i].y; } PUT2("%.1f 0 %.1f bar\n", HighestStaffY - LowestStaffY + 24., LowestStaffY); for (i = 0; i <= nstaff; i++) { float y; if (staff_tb[i].brace) { SkipStaff = TRUE; for (j = 0; j <= nvoice; j++) { if ((voice_tb[j].staff == i) && !VoiceMute_tb[j]) { SkipStaff = FALSE; break; } } if (SkipStaff) continue; y = staff_tb[i].y + (float)24.; PUT2("%.1f 0 %.1f brace\n", y - staff_tb[++i].y, y); } /* Bracket code superseded by line following this loop else if (staff_tb[i].bracket) { y = staff_tb[i++].y + (float)24.; while (!staff_tb[i].bracket_end) i++; PUT2("%.1f 0 %.1f bracket\n", y - staff_tb[i].y, y); } */ } /* [JSA] This replaced the bracket code in the loop above. */ PUT2("%.1f 0 %.1f bracket\n", HighestStaffY - LowestStaffY + 24., HighestStaffY + 24.); if (measure_nb == 0) { set_font(cfmt.composerfont, 0); PUT2("0 %.1f M (%d) show\n", staff_tb[0].y + 24. + 16., nbar); } else if (measure_nb > 0 && nbar % measure_nb == 0) { set_font(cfmt.composerfont, 0); PUT2("22 %.1f M (%d) show\n", staff_tb[0].y + +24. + 6., nbar); } } /* ----- draw_timesig ------- */ static void draw_timesig(float x, struct SYMBOL *s) { int i; int j, SkipStaff; /* [JSA] */ for (i = nstaff; i >= 0; i--) { SkipStaff = TRUE; for (j = 0; j <= nvoice; j++) { if ((voice_tb[j].staff == i) && !VoiceMute_tb[j]) { SkipStaff = FALSE; break; } } if (SkipStaff) continue; if (!stricmp(s->text, "free")) { /* Free meter -- [JSA] */ switch (cfmt.FreeMetStyle) { case 1: PUT4("%.1f %.1f (%s) (%s) tsig\n", x, staff_tb[i].y, "free", ""); break; case 2: PUT4("%.1f %.1f (%s) (%s) tsig\n", x, staff_tb[i].y, "free", "meter"); break; case 3: PUT4("%.1f %.1f (%s) (%s) tsig\n", x, staff_tb[i].y, "fm", ""); break; case 4: break; default: PUT4("%.1f %.1f (%s) (%s) tsig\n", x, staff_tb[i].y, "none", ""); } } else if (s->w == 1) { PUT2("%.1f %.1f csig\n", x, staff_tb[i].y); } else if (s->w == 2) { PUT2("%.1f %.1f ctsig\n", x, staff_tb[i].y); } else { PUT4("%.1f %.1f (%s) (%d) tsig\n", x, staff_tb[i].y, s->text, s->v); } } } /* ----- draw_keysig ------- */ /*fixme: what about the alto clef*/ static void draw_keysig(int voice, float x, struct SYMBOL *s) { int n1,n2,n3; static int sh_pos[8] = {0,24,15,27,18, 9,21,12}; static int ft_pos[8] = {0,12,21, 9,18, 6,15, 3}; char *func; int *pos; n1 = s->u; /* which symbol to start with */ n2 = s->v; /* up to which symbol to go */ n3 = s->w; /* draw neutrals instead starting from this one */ if (n2 == 0) return; if (n2>7) { printf("++++ Keysig seems to have %d symbols ???\n", n2); return; } switch (s->t) { /* type of symbol: sharp or flat */ case A_SH: pos = sh_pos; func = "sh0"; break; case A_FT: pos = ft_pos; func = "ft0"; break; default: bug("wrong type in draw_keysig", 0); return; } if (!voice_tb[voice].second) { int staff, i; float staffb; staff = voice_tb[voice].staff; staffb = staff_tb[staff].y; if (staff_tb[staff].clef != TREBLE) staffb -= 6.; for (i = n1; i <= n2; i++) { if (i >= n3) PUT2("%.1f %.1f nt0 ", x, pos[i] + staffb); else PUT3("%.1f %.1f %s ", x, pos[i] + staffb, func); x += 5; } PUT0("\n"); } voice_tb[voice].sharps_flats = s->z; } /* ---- draw_bar1 ---- */ static void draw_bar1(float x, float y, float h, struct SYMBOL *s) { switch (s->u) { case B_SNGL: PUT3("%.1f %.1f %.1f bar\n", h, x, y); break; case B_DBL: PUT3("%.1f %.1f %.1f dbar\n", h, x, y); break; case B_LREP: PUT3("%.1f %.1f %.1f fbar1\n", h, x, y); break; case B_RREP: PUT3("%.1f %.1f %.1f fbar2\n", h, x, y); break; case B_DREP: PUT3("%.1f %.1f %.1f fbar1 ", h, x-1, y); PUT3("%.1f %.1f %.1f fbar2\n", h, x+1, y); break; case B_FAT1: PUT3("%.1f %.1f %.1f fbar1\n", h, x, y); break; case B_FAT2: PUT3("%.1f %.1f %.1f fbar2\n", h, x, y); break; case B_INVIS: break; default: printf(">>> cannot draw bar type %d\n", s->u); PUT0("\n"); break; } } /* ----- draw_bar ------- */ static void draw_bar(float x, struct SYMBOL *s) { int staff; float stafft; int dotsb = 0; int dotsa = 0; int bracket; int i, j, SkipStaff, SkipStaffs[MAXSTAFF]; float LowestStaffY, HighestStaffY; bracket = 0; // [JSA] 05/03/00 - fix the dots too // TODO: Clean this up and integrate with other mod. for (i = 0; i <= nstaff; i++) { SkipStaffs[i] = TRUE; for (j = 0; j <= nvoice; j++) { if ((voice_tb[j].staff == i) && !VoiceMute_tb[j]) { SkipStaffs[i] = FALSE; break; } } } /* Modification 10/99, J.S. Atchley. Stop bars from */ /* crossing vocals. [JSA] */ if (!s->eoln) { for (staff = 0; staff <= nstaff; staff++) { SkipStaff = TRUE; for (j = 0; j <= nvoice; j++) { if ((voice_tb[j].staff == staff) && !VoiceMute_tb[j]) { SkipStaff = FALSE; break; } } if (SkipStaff) continue; draw_bar1(x, staff_tb[staff].y, (float)24.0, s); } } else { /* [JSA] This is the original code but modified for */ /* voice muting. */ /* Draw only for staffs actually used. [JSA] */ for (i = 0; i <= nstaff; i++) { SkipStaff = TRUE; for (j = 0; j <= nvoice; j++) { if ((voice_tb[j].staff == i) && !VoiceMute_tb[j]) { SkipStaff = FALSE; break; } } if (!SkipStaff) LowestStaffY = staff_tb[i].y; } for (i = nstaff; i >= 0; i--) { SkipStaff = TRUE; for (j = 0; j <= nvoice; j++) { if ((voice_tb[j].staff == i) && !VoiceMute_tb[j]) { SkipStaff = FALSE; break; } } if (!SkipStaff) HighestStaffY = staff_tb[i].y; } stafft = HighestStaffY + (float)24.; /* top of upper staff */ for (staff = 0; staff < nstaff; staff++) { SkipStaff = TRUE; for (j = 0; j <= nvoice; j++) { if ((voice_tb[j].staff == staff) && !VoiceMute_tb[j]) { SkipStaff = FALSE; break; } } if (SkipStaff) continue; if (staff_tb[staff].bracket) bracket = 1; if (staff_tb[staff].bracket_end) bracket = 0; if (!bracket && staff_tb[staff].nvocal > 0) { draw_bar1(x, staff_tb[staff].y, stafft - staff_tb[staff].y, s); stafft = staff_tb[staff + 1].y + (float)24.; } } draw_bar1(x, LowestStaffY, stafft - LowestStaffY, s); } switch (s->u) { case B_LREP: dotsb = 10; break; case B_RREP: dotsa = 10; break; case B_DREP: dotsb = 9; dotsa = 9; break; } if (dotsb != 0) { for (staff = nstaff + 1; --staff >= 0;) { if (!SkipStaffs[staff]) PUT2("%.1f %.1f rdots ", x + dotsb, staff_tb[staff].y); } PUT0("\n"); } if (dotsa != 0) { for (staff = nstaff + 1; --staff >= 0; ) { if (!SkipStaffs[staff]) PUT2("%.1f %.1f rdots ", x - dotsa, staff_tb[staff].y); } PUT0("\n"); } } /* ----- update_endings: remember where to draw endings ------- */ static void update_endings(struct SYMBOL *s) { float x; int i; x = s->x; if (num_ending>0) { i=num_ending-1; if (ending[i].num==1) mes1++; else { mes2++; if (mes2==mes1) ending[i].b=x; } } if (s->v) { if (num_ending>0) if (ending[num_ending-1].num==1) ending[num_ending-1].b=x-3; ending[num_ending].a=x; ending[num_ending].b=-1; ending[num_ending].num=s->v; ending[num_ending].staff=s->staff; if (s->v==1) mes1=0; else mes2=0; num_ending++; } } /* ----- draw_endings ------- */ static void draw_endings(void) { int i; for (i=0;istaff].y; /* bottom of staff */ if (s->invis) return; PUT2("%.2f %.0f", x, yy + staffb); if (s->head==H_OVAL) { PUT0(" r1"); if (yy < -6 /* add one helper line */ || yy >= 24) { PUT1(" %.1f hl", yy + 6. + staffb); } } else if (s->head==H_EMPTY) { PUT0(" r2"); if (yy <= -6 /* add one helper line */ || yy >= 30) { PUT1(" %.1f hl", yy + staffb); } } else { char *p; switch (s->flags) { case 0: p = " r4"; break; case 1: p = " r8"; break; case 2: p = " r16"; break; case 3: p = " r32"; break; default: p = " r64"; break; } PUT0(p); } if (s->head==H_OVAL) { dotx=8; doty=-3; } else if (s->head==H_EMPTY) { dotx=8; doty=3; } else if ((int) yy % 6) { dotx=6.5; doty=0; /* dots */ } else { dotx=6.5; doty=3; } for (i=0;idots;i++) { PUT2(" %.1f %.1f dt", dotx, doty); dotx += 3.5; } PUT0("\n"); } /* ----- draw_gracenotes ----- */ static void draw_gracenotes(float x, float w, float d, struct SYMBOL *s) { int i,n,y,acc,ii,m; float xg[20],yg[20],lg,px[20],py[20],xx,yy; float s1,sx,sy,sxx,sxy,a,b,delta,lmin; float x0,y0,x1,y1,x2,y2,x3,y3,bet1,bet2,dy1,dy2,dx,dd,fac,facx; float staffb; static char *acc2_tb[] = { "", "gsh0", "gnt0", "gft0", "gds0", "gdf0" }; n = s->gr.n; if (n == 0) return; staffb = staff_tb[(int) s->staff].y; /* bottom of staff */ facx=(float)0.3; fac=d/w-1; if (fac<0) fac=0; fac=1+(fac*facx)/(fac+facx); dx=0; for (m=0;mnpitch;m++) { /* room for accidentals */ dd=-s->shhd[m]; if (s->accs[m]) dd = -s->shhd[m]+s->shac[m]; if (s->accs[m] == A_FT || s->accs[m] == A_NT) dd -= 2; if (dx= 0; ) { /* set note positions */ yg[i]=(float)3*(s->gr.p[i]-18); if (i==n-1) { /* some subtle shifts.. */ if(yg[i]>=s->ymx) xx += 1; /* gnote above a bit closer */ if((yg[i]ymn-7)&&(n==1)) xx -= 2; /* below with flag further */ } if (iyg[i+1]+8) xx += fac * (float)1.8; xg[i]=xx; xx -= fac*GSPACE; if (s->gr.a[i]) xx -= 3.5; } if (n>1) { s1=sx=sy=sxx=sxy=0; /* linear fit through stems */ for (i=0;iBEAM_SLOPE) a=BEAM_SLOPE; else if (a<-BEAM_SLOPE) a=-BEAM_SLOPE; b=(sy-a*sx)/s1; if (bagpipe) { a=0; b=35; } lmin=100; /* shift to get min stems */ for (i=0;i1) { px[i]=xg[i]+GSTEM_XOFF; py[i]=a*px[i]+b; lg=py[i]-yg[i]; PUT3("%.1f %.1f %.1f gnt ", xg[i],yg[i] + staffb,lg); } else { lg=GSTEM; PUT3("%.1f %.1f %.1f gn1 ", xg[i],yg[i] + staffb,lg); } if ((acc = s->gr.a[i]) != 0) PUT3("%.1f %.1f %s ", xg[i] - 4.5, yg[i] + staffb, acc2_tb[acc]); y = (int)yg[i]; /* helper lines */ if (y<=-6) { if (y%6) PUT2("%.1f %.1f ghl ", xg[i], y+3 + staffb); else PUT2("%.1f %.1f ghl ", xg[i], y + staffb); } if (y>=30) { if (y%6) PUT2("%.1f %.1f ghl ", xg[i], y-3 + staffb); else PUT2("%.1f %.1f ghl ", xg[i], y + staffb); } } if (n>1) { /* beam */ if (bagpipe) { PUT4("%.1f %.1f %.1f %.1f gbm3 ", px[0],py[0] + staffb,px[n-1],py[n-1] + staffb); } else { PUT4("%.1f %.1f %.1f %.1f gbm2 ", px[0],py[0] + staffb,px[n-1],py[n-1] + staffb); } } bet1=(float)0.2; /* slur */ bet2=(float)0.8; yy=1000; for (i=n-1;i>=0;i--) if (yg[i]<=yy) { yy=yg[i]; ii=i; } x0=xg[ii]; y0=yg[ii]-5; if (ii>0) { x0 -= 4; y0 += 1; } x3=x-1; y3=(float)s->ymn-5; dy1=(x3-x0)*(float)0.4; if (dy1>3) dy1=3; dy2=dy1; if (yg[ii]>s->ymn+7){ x0=xg[ii]-1; y0=yg[ii]-(float)4.5; y3=s->ymn+(float)1.5; x3=x-dx-(float)5.5; dy2=(y0-y3)*(float)0.2; dy1=(y0-y3)*(float)0.8; bet1=0.0; } if (y3>y0+4) { y3=y0+4; x0=xg[ii]+2; y0=yg[ii]-4; } x1=bet1*x3+(1-bet1)*x0; y1=bet1*y3+(1-bet1)*y0-dy1; x2=bet2*x3+(1-bet2)*x0; y2=bet2*y3+(1-bet2)*y0-dy2; PUT4(" %.1f %.1f %.1f %.1f", x1,y1 + staffb,x2,y2 + staffb); PUT4(" %.1f %.1f %.1f %.1f gsl\n", x3,y3 + staffb,x0,y0 + staffb); } /* ----- draw_basic_note: draw m-th head with accidentals and dots -- */ static void draw_basic_note(float x, float w, float d, struct SYMBOL *s, int m, int voice) { int y,i,yy; float dotx,doty,xx,dx,avail,add,fac; float staffb; static char *acc_tb[] = { "", "sh", "nt", "ft", "dsh", "dft" }; staffb = staff_tb[(int) s->staff].y; /* bottom of staff */ y = 3 * (s->pits[m] - 18); /* height on staff */ xx = x + s->shhd[m]; /* draw head */ PUT2("%.1f %.1f", xx, (float) y + staffb); if (s->head == H_OVAL) PUT0(" HD"); else if (s->head == H_EMPTY) PUT0(" Hd"); else if (s->head == H_FULL) PUT0(" hd"); if (s->shhd[m]) { yy=0; if (y>=30) { yy=y; if (yy%6) yy -= 3; } if (y<=-6) { yy=y; if (yy%6) yy += 3; } if (yy) PUT1(" %.1f hl", (float) yy + staffb); } if (s->dots) { /* add dots */ if (y % 6) { dotx = 8; doty = 0; } else { dotx = 8; doty = 3; } /* if (s->stem < 0) dotx += s->xmx - s->shhd[m]; else dotx += s->xmx - s->shhd[m]; The above lines do the same thing regardless of the condition. Until we figure out what the original author intended, I've replaced it with the next line. */ dotx += s->xmx - s->shhd[m]; /* The addition of the "Unison" flag is used here to prevent the */ /* flags from obscuring dots in the case of a single unison chord. */ if (s->dots && s->flags && ((s->stem > 0) || s->Unison) && !(y % 6)) if (s->word_st && s->word_end && ((s->npitch == 1) || s->Unison)) dotx += DOTSHIFT; if (s->head==H_EMPTY) dotx += 1; else if (s->head==H_OVAL) dotx += 2; for (i=0;idots;i++) { PUT2(" %.1f %.1f dt", dotx, doty); dotx += 3.5; } } if (s->accs[m]) { /* add accidentals */ fac=1.0; avail=d-w-3; add=(float)0.3*avail; fac=1+add/s->wl; if (fac<1) fac=1; else if (fac>1.2) fac=(float)1.2; dx=fac*s->shac[m]; PUT2(" %.1f %s", dx, acc_tb[(int) s->accs[m]]); } } /* ----- CalcVertical --------- */ /* Calculates vertical offsets */ /* for decorations. [JSA] */ /* DecoHt is the height in points of the decoration. DecoOrg is the origin of */ /* the decoration. When "Below" is true the values will be calculated to place */ /* the decoration under the staff. s->ylo and s->yhi are updated with new */ /* extents. */ static float CalcDecoY(float DecoHt, float DecoOrg, BOOL Below, struct SYMBOL *s) { float y, htop, hds; htop = DecoHt - DecoOrg; hds = cfmt.DecoSpacing / (float)2.0; if (Below) { y = (float)-2.0 - (hds + htop); if (y > (s->ylo - (hds + htop))) y = s->ylo - (htop + hds); if (s->ylo > (y - (DecoOrg + hds))) s->ylo = y - (DecoOrg + hds); } else { y = STAFFHEIGHT + hds + DecoOrg; if (y < (s->yhi + hds + DecoOrg)) y = s->yhi + hds + DecoOrg; if (s->yhi < (y + htop + hds)) s->yhi = y + htop + hds; } return y; } /* ----- MoveToBetweenLines ----- */ /* As the name suggests, this function adjusts a y value to be between the */ /* lines of a staff. Set Dir to +1 to move up, -1 to move down, or zero to */ /* move either direction (to the closest one). */ static float MoveToBetweenLines(float yc, int Dir) { int y; y = (int)yc; if ((yc - (float)y) >= (float)0.5) y++; if ((y >= 0) && (y < 24)) { if (Dir == 0.0) { switch (y % 6) { case 0: yc += 3; break; case 1: yc += 2; break; case 2: yc += 1; break; case 3: break; case 4: yc -= 1; break; case 5: yc -= 2; } } else { switch (y % 6) { case 0: yc += 3 * Dir; break; case 1: case 5: yc += 2 * Dir; break; case 2: case 4: yc += 1 * Dir; break; case 3: break; } } } return yc; } /* ----- DrawCloseDecorations ----- */ /* Moved drawing of decorations that are close to the notehead into this */ /* separate function. That allows us to delay printing decorations that */ /* are above or below the staff until after slurs/ties have been output. */ static void DrawCloseDecorations(float x, struct SYMBOL *s, int voice, BOOL Preview) { int k, deco, m; float yc, xc, dx, dy; float staffb, hds, htop; tFIFO *Fifo; char *p; BOOL Unison; hds = cfmt.DecoSpacing / (float)2.0; htop = cfmt.DcDsFont.size - (float)3.0; Unison = s->Unison; staffb = staff_tb[(int) s->staff].y; /* bottom of staff */ /* Decos close to head. */ /* Note, it was necessary to reverse the direction of this loop */ /* to maintain text synchronization in FIFO buffers. */ for (k = 0; k < s->dc.n; k++) { switch (deco = s->dc.t[k]) { case D_BREATH: if (!cfmt.BreathsClose) break; xc = x + 6; if (s->stem > 0) { /* Put mark to right near top of stem */ if (s->flags) xc += 3; yc = s->ys - 3; } else if ((s->stem < 0) && s->Unison) { if (s->flags) xc += 3; yc = (float)s->ymx + STEM - 3; } else { yc = (float)s->ymx; } yc = MoveToBetweenLines(yc, 0); /* Adjust for fact that apostrophe is shown in the top third of the text */ yc -= 9; if (!Preview) { PUT2(" gsave %.1f %.1f moveto ", xc, yc + staffb); PUT2("/%s %.1f selectfont (\') show grestore ", cfmt.DcDsFont.name, cfmt.DcDsFont.size + 5); } break; case D_ROLL: /* roll sign */ /* I moved this into the "close to head" group because */ /* we now have the option to put the roll sign next to */ /* the notehead and because, even when we move it to */ /* the staff lines we want it to be the closest symbol */ /* to the notehead. */ /* TODO: I think we can get this a little closer so it */ /* kind of wraps around the notehead. */ if (s->stem > 0) { yc = s->ymn - (float)5.0; if (Unison) yc -= STEM; if (!cfmt.RollsClose && (yc > (float)-2.0)) yc = (float) -2.0; if (yc > (s->ylo - 2.0)) yc = s->ylo - (float)2.0; if (!Preview) PUT1(" %.2f cpd", yc + staffb); if (s->ylo > (yc - (float)5.0)) s->ylo = yc - (float)5.0; } else { if (s->dots && !(s->ymx % 6)) { yc = s->ymx + (float)6.0; } else { yc = s->ymx + (float)5.0; } if (Unison && s->stem) yc += STEM; if (!cfmt.RollsClose && (yc < 26)) yc = 26; if (yc < (s->yhi + 1)) yc = s->yhi + 1; if (!Preview) PUT1(" %.2f cpu", yc + staffb); if (s->yhi < (yc + 8)) s->yhi = yc + 8; } break; case D_STACC: case D_EMBAR: /* dot or bar mark */ /* Place below the bottom notehead in all cases except when */ /* a downward stem is present (either by default or due to */ /* a unison). */ if ((s->stem < 0) || ((s->stem > 0) && Unison)) { yc = MoveToBetweenLines((float)s->ymx + 6, 1); } else { yc = MoveToBetweenLines((float)s->ymn - 6, -1); } if (!Preview) PUT2(" %.1f %s", yc + staffb, deco == D_STACC ? "stc" : "emb"); if (s->yhi < (yc + (float)2.0)) s->yhi = yc + (float)2.0; break; case D_SLIDE: /* slide */ yc=(float)s->ymn; xc=5; for (m=0;mnpitch;m++) { dx=5-s->shhd[m]; if (s->head==H_OVAL) dx += 2.5; if (s->accs[m]) dx=4-s->shhd[m]+s->shac[m]; dy=3*(s->pits[m]-18)-yc; if ((dy<10) && (dx>xc)) xc=dx; } if (!Preview) PUT2(" %.1f %.1f sld", yc + staffb, xc); break; case D_NTEXT: /* text left or right of notehead */ yc = (float)s->ymn - (cfmt.DcDsFont.size / (float)3.0); xc = cfmt.DcDsFont.size / (float)2.0; for (m=0;mnpitch;m++) { dx=5 - s->shhd[m]; if (s->head == H_OVAL) dx += 2.5; if (s->accs[m]) dx = 4 - s->shhd[m] + s->shac[m]; dy = 3 * (s->pits[m] - 18) - yc; if ((dy < 10) && (dx > xc)) xc = dx; } Fifo = &voice_tb[voice].TextHdDecs; if (Fifo->strings[Fifo->outidx]) { p = Fifo->strings[Fifo->outidx]; if (Fifo->anchor[Fifo->outidx] == TXTA_LT) { xc = (float)0.0 - xc; } if (!Preview) { PUT0(" gsave "); set_font(cfmt.DcDsFont, FALSE); PUT2(" %.1f %.1f moveto", xc + s->x + Fifo->dx[Fifo->outidx], yc + staffb + Fifo->dy[Fifo->outidx]); switch (Fifo->just[Fifo->outidx]) { case TXTJ_RT: PUT1("(%s) lshow ", p); break; case TXTJ_CN: PUT1("(%s) cshow ", p); break; default: PUT1("(%s) show ", p); } PUT0(" grestore "); free(Fifo->strings[Fifo->outidx]); Fifo->strings[Fifo->outidx] = NULL; } Fifo->outidx = (Fifo->outidx + 1) % MAXSTRDECO; if (s->ylo > (yc - ((float)3.0 + hds))) s->ylo = yc - ((float)3.0 + hds); if (s->yhi < (yc + htop + hds)) s->yhi = yc + htop + hds; } else { // Warn user of underflow. printf("Non-fatal error - text decoration FIFO underflow in voice %d.\n", voice); } break; } } } /* ----- draw_decorations ----- */ /************************************************************/ /* 05/00 [JSA] I basically rewrote this entire function. */ /* There was a huge amount of duplicated code, most likely */ /* that happened when each developer did a bit of cut and */ /* paste to add a new symbol. I simplified this function */ /* somewhat by moving the calculations for all of the */ /* decorations printed away from the notehead into a simple */ /* function. I also added the capability to print such */ /* decorations below the staff instead of above it using */ /* a "DecosBelowSt" field in the format file. Finally, I'm */ /* using the "ylo" and "yhi" fields of the symbol structure */ /* to track the true limits of the symbol, including decos. */ /* This appears to have been the intent for these fields, */ /* it was just never carried through. The fields were being */ /* set to reflect (sort-of) the notehead limits, but were */ /* never being used as input to any functionality as far as */ /* I could tell. Using ylo and yhi also eliminated the need*/ /* need to separately track a top value, eliminating that */ /* parameter from the call. The absolute top is no longer */ /* returned, as it is readily available in the yhi field of */ /* the symbol structure. */ /* TODO: There is still a major problem with this design, */ /* because symbols are output as they are encountered, there*/ /* is no way to avoid collisions with symbols yet to be */ /* output and there is no way to adjust the staff spacing */ /* because the staff offsets have to be set before we get */ /* here. We need to split this into two functions. The */ /* first would determine and store the y offset for each */ /* symbol and set ylo and yhi. The second would actually */ /* output the symbols. Unfortunately, to get there we have */ /* to back up even farther to the loop that draws notes to */ /* make sure that we are only looping through one line of */ /* symbols for each pair of function calls and we also have */ /* to store the y offsets for all of the decorations that */ /* we've parsed in the first function until they're ouptut */ /* in the second. */ /************************************************************/ static void draw_decorations(struct SYMBOL *s, int voice, BOOL Preview) { int k, deco; float x, yc; float staffb, hds, htop; tFIFO *Fifo; char *p; BOOL Unison; hds = cfmt.DecoSpacing / (float)2.0; htop = cfmt.DcDsFont.size - (float)3.0; x = s->x; Unison = s->Unison; staffb = staff_tb[(int) s->staff].y; /* bottom of staff */ /* Since this function is no longer called immediately after a */ /* note is drawn, we need this to set the x location for the */ /* original decorations (fermata, etc) that draw relative to an */ /* assumed x location. */ if (!Preview) PUT2(" %.1f /x exch def %.1f /y exch def\n", x, staffb); /* Decos above or below the staff. */ /* Note, it was necessary to reverse the direction of this loop */ /* to maintain text synchronization in FIFO buffers. */ for (k = 0; k < s->dc.n; k++) { switch (deco = s->dc.t[k]) { case D_GRACE: /* gracing marker */ yc = CalcDecoY((float)6.0, 4.0, cfmt.DecosBelow[voice], s); if (!Preview) PUT1(" %.2f grm", yc + staffb); break; case D_HOLD: /* hold sign */ yc = CalcDecoY((float)10.0, 2.0, cfmt.DecosBelow[voice], s); if (!Preview) { PUT2(" %.1f %.1f moveto ", x, yc + staffb); PUT1(" %.1f hld", yc + staffb); } break; case D_TRILL: /* trill sign */ yc = CalcDecoY((float)10.0, 2.0, cfmt.DecosBelow[voice], s); if (!Preview) PUT1(" %.1f trl", yc + staffb); break; case D_UPBOW: yc = CalcDecoY((float)12.0, 3.0, cfmt.DecosBelow[voice], s); if (!Preview) PUT1(" %.1f upb", yc + staffb); break; case D_DOWNBOW: /* bowing signs */ yc = CalcDecoY((float)9.0, 2.0, cfmt.DecosBelow[voice], s); if (!Preview) PUT1(" %.1f dnb", yc + staffb); break; case D_ACCENT: /* Accent sign JSA 04/21/00 */ yc = CalcDecoY((float)8.0, 5.0, cfmt.DecosBelow[voice], s); if (!Preview) PUT2(" %.1f %.1f accent", s->x, yc + staffb); break; case D_SEGNO: /* Segno symbol JSA 05/00 */ yc = CalcDecoY((float)15.0, 6.0, cfmt.DecosBelow[voice], s); if (!Preview) PUT2(" %.1f %.1f segno", s->x, yc + staffb); break; case D_CODA: /* Coda symbol JSA 05/00 */ yc = CalcDecoY((float)18.0, 11.0, cfmt.DecosBelow[voice], s); if (!Preview) PUT2(" %.1f %.1f coda", s->x, yc + staffb); break; case D_DC: case D_DS: case D_FINE: /* da Segno, etc. JSA 05/00 */ yc = CalcDecoY(cfmt.DcDsFont.size * (float)0.8, 2.0, cfmt.DecosBelow[voice], s); if (!Preview) { set_font(cfmt.DcDsFont, FALSE); PUT2(" %.1f %.1f moveto ", s->x, yc + staffb); switch (deco) { case D_DC: PUT0("(D.C.) lshow "); break; case D_DS: PUT0("(D.S.) lshow "); break; case D_FINE: PUT0("(Fine) lshow "); break; } } break; case D_BREATH: if (cfmt.BreathsClose) break; yc = CalcDecoY((cfmt.DcDsFont.size + 5) * (float)0.8, 2.0, cfmt.DecosBelow[voice], s); if (!Preview) { PUT2(" %.1f %.1f moveto ", s->x, yc + staffb); PUT2("/%s %.1f selectfont (\') show ", cfmt.DcDsFont.name, cfmt.DcDsFont.size + 5); } break; case D_STEXT: Fifo = &voice_tb[voice].TextDecs; if (Fifo->strings[Fifo->outidx]) { p = Fifo->strings[Fifo->outidx]; if (Fifo->anchor[Fifo->outidx] == TXTA_CLAMP) { yc = STAFFHEIGHT + 1; } else { yc = CalcDecoY(cfmt.DcDsFont.size, 3.0, (Fifo->anchor[Fifo->outidx] == TXTA_BOT), s); } if (!Preview) { set_font(cfmt.DcDsFont, FALSE); PUT2(" %.1f %.1f moveto ", s->x + Fifo->dx[Fifo->outidx], yc + staffb + Fifo->dy[Fifo->outidx]); switch (Fifo->just[Fifo->outidx]) { case TXTJ_RT: PUT1("(%s) lshow ", p); break; case TXTJ_CN: PUT1("(%s) cshow ", p); break; default: PUT1("(%s) show ", p); } free(Fifo->strings[Fifo->outidx]); Fifo->strings[Fifo->outidx] = NULL; } if (Fifo->dy[Fifo->outidx] != 0) { /* adjust ylo and yhi as needed */ yc += Fifo->dy[Fifo->outidx]; if (Fifo->anchor[Fifo->outidx] == TXTA_BOT) { if (yc > (s->ylo - (hds + htop))) yc = s->ylo - (htop + hds); if (s->ylo > (yc - ((float)3.0 + hds))) s->ylo = yc - ((float)3.0 + hds); } else { if (yc < (s->yhi + hds + (float)3.0)) yc = s->yhi + hds + (float)3.0; if (s->yhi < (yc + htop + hds)) s->yhi = yc + htop + hds; } } Fifo->outidx = (Fifo->outidx + 1) % MAXSTRDECO; } else { // Warn user of underflow. printf("Non-fatal error - text decoration FIFO underflow in voice %d.\n", voice); } break; case D_sfz: case D_ffff: case D_fff: case D_ff: case D_f: case D_mf: case D_p: case D_pp: case D_ppp: case D_pppp: /* dynamics - [JSA] 05/00 */ yc = CalcDecoY(cfmt.DynFont.size * (float)0.8, 2.0, cfmt.DecosBelow[voice], s); if (!Preview) { set_font(cfmt.DynFont, FALSE); PUT2(" %.1f %.1f moveto (", s->x, yc + staffb); switch(deco) { case D_sfz: PUT0("sfz"); break; case D_ffff: PUT0("ffff"); break; case D_fff: PUT0("fff"); break; case D_ff: PUT0("ff"); break; case D_f: PUT0("f"); break; case D_mf: PUT0("mf"); break; case D_p: PUT0("p"); break; case D_pp: PUT0("pp"); break; case D_ppp: PUT0("ppp"); break; default: PUT0("pppp"); break; } PUT0(") show "); } break; case D_STCRESC: case D_STDIM: yc = CalcDecoY(8.0, 4.0, cfmt.DecosBelow[voice], s); staff_tb[(int)voice_tb[(int)voice].staff].CrescendoStX = s->x; staff_tb[(int)voice_tb[(int)voice].staff].CrescendoStY = yc; break; case D_ENDCRESC: yc = CalcDecoY(8.0, 4.0, cfmt.DecosBelow[voice], s); if (yc > staff_tb[(int)voice_tb[voice].staff].CrescendoStY) staff_tb[(int)voice_tb[voice].staff].CrescendoStY = yc; if (yc < staff_tb[(int)voice_tb[voice].staff].CrescendoStY) { yc = staff_tb[(int)voice_tb[voice].staff].CrescendoStY; if (s->ylo > (yc - 5)) s->ylo = yc - 5; if (s->yhi < (yc + 5)) s->yhi = yc + 5; } if (!Preview && (s->x > staff_tb[(int)voice_tb[voice].staff].CrescendoStX)) { PUT3(" %.1f %.1f %.1f crescendo ", staff_tb[(int)voice_tb[voice].staff].CrescendoStX, s->x, yc + staffb); } break; case D_ENDDIM: yc = CalcDecoY(8.0, 4.0, cfmt.DecosBelow[voice], s); if (yc > staff_tb[(int)voice_tb[voice].staff].CrescendoStY) staff_tb[(int)voice_tb[voice].staff].CrescendoStY = yc; if (yc < staff_tb[(int)voice_tb[voice].staff].CrescendoStY) { yc = staff_tb[(int)voice_tb[voice].staff].CrescendoStY; if (s->ylo > (yc - 5)) s->ylo = yc - 5; if (s->yhi < (yc + 5)) s->yhi = yc + 5; } if (!Preview && (s->x > staff_tb[(int)voice_tb[voice].staff].CrescendoStX)) { PUT3(" %.1f %.1f %.1f diminuendo ", staff_tb[(int)voice_tb[voice].staff].CrescendoStX, s->x, yc + staffb); } break; case D_GCHORD: if (Preview) { if (s->yhi < 38) s->yhi = 38; s->yhi += cfmt.gchordfont.size; } break; case D_GCHORDB: if (Preview) { if (s->yhi < 38) s->yhi = 38; s->yhi += cfmt.gchordfont.size * 2; } break; } } return; } /* ----- draw_note ----- */ static void draw_note(float x, float w, float d, struct SYMBOL *s, int fl, int voice) { char c,cc; int y, i, m; float yc, ycd1, ycd2, slen, slen0, xx; float staffb, fy; int display_pitch, whistle_i; char display_name[8]; int whistle_octave, whistle_pitch, Str, StrPitch, Fret, j, n, p; int Str1, StrN, IncDec; BOOL StrUsed[8], NoteTabbed, IsMarked; const char DulcFrets[22] = { 0, 2, 4, 5, 7, 9, 10, 12, 14, 16, 17, 19, 21, 22, 24, 26, 28, 29, 31, 33, 34, 36 }; char TabMsg[8]; short Pitches[MAXHD], oPitches[MAXHD], Decos[MAXHD], SlidePitch; slen0 = STEM; staffb = staff_tb[(int) s->staff].y; if (s->type == COMBINEDNOTE) { /* This note was combined with one on another voice */ /* on this staff. Get the x value from that other */ /* note for later use by the whistle tablature */ /* code below. [JSA] */ s->x = s->lnk->x; x = s->x + s->shhd[0]; /* draw head */ for (whistle_i = 0; whistle_i < nWhistleTab; whistle_i++) { /* For each whistle tablature, if any. */ whistle_octave = 0; if (voice == WhistleTab_tb[whistle_i].voice) break; } fy = WhistleTab_tb[whistle_i].y + (float)4.5; /* height on staff */ PUT2(" %.1f /x exch def %.1f /y exch def\n", x, fy); } else { /* This note has not been combined. Draw it which */ /* also adjusts "x" which is used by the whistle */ /* tablature code below. [JSA] */ draw_gracenotes(x, w, d, s); /* draw grace notes */ c = 'd'; cc='u'; if (s->stem > 0) { c='u'; cc='d'; } slen = s->stem * (s->ys - s->y); s->dc.top = (float)s->ymx; s->dc.bot = (float)s->ymn; if (s->stem > 0) { s->dc.top = s->ys - 2; if (s->Unison) s->dc.bot = (s->ymn - STEM) + 2; } if (s->stem < 0) { s->dc.bot = s->ys + 2; if (s->Unison) s->dc.top = (s->ymx + STEM) - 2; } for (m = 0;m < s->npitch; m++) { if (m>0) PUT0(" "); draw_basic_note(x, w, d, s, m, voice); /* draw note heads */ xx = 3 * (s->pits[m] - 18) - s->y; xx *= xx; if (xx < 0.01) { /* add stem */ if (s->stem) PUT2(" %.1f s%c", slen, c); if (fl && (s->flags > 0)) PUT3(" %.1f f%d%c", slen, s->flags, c);/* add flags */ } if (s->Unison) { /* unions */ if (s->stem) PUT2(" %.2f s%c", slen0, cc); if (s->flags > 0) PUT3(" %.1f f%d%c", slen0, s->flags, cc); } } /* Only notes have close decorations (staccato, etc). */ DrawCloseDecorations(x, s, voice, FALSE); y = s->ymn; /* lower helper lines */ if (y<=-6) { for (i=-6;i>=y;i=i-6) PUT1(" %.1f hl", (float) i + staffb); if (s->head==H_OVAL) PUT0("1"); } y = s->ymx; /* upper helper lines */ if (y>=30) { for (i=30;i<=y;i=i+6) PUT1(" %.1f hl", (float) i + staffb); if (s->head==H_OVAL) PUT0("1"); } PUT0("\n"); } /*********************************************************/ /* */ /* Begin modification for whistle tablature. */ /* J. S. Atchley, 10/99. [JSA] */ /* */ /*********************************************************/ for (whistle_i = 0; whistle_i < nWhistleTab; whistle_i++) { /* For each whistle tablature, if any. */ whistle_octave = 0; if ((voice == WhistleTab_tb[whistle_i].voice) && /* correct voice for this tablature set... */ ((s->type == NOTE) || (s->type == COMBINEDNOTE)) && /* ...and is a printed note... */ !s->ti2[0] && !s->EndTie) { /* ...and is not tied to the previous note.*/ whistle_pitch = WhistleTab_tb[whistle_i].keypitch; display_pitch = s->abspitch[0]; /* always use first note on stem*/ while (display_pitch < whistle_pitch) { display_pitch += 12; whistle_octave--; } while (display_pitch > (whistle_pitch + 35)) { display_pitch -= 12; whistle_octave++; } switch ((display_pitch - whistle_pitch) % 36) { case 0: strcpy(display_name, "tw_60"); break; case 1: strcpy(display_name, "tw_55"); break; case 2: strcpy(display_name, "tw_50"); break; case 3: strcpy(display_name, "tw_45"); break; case 4: strcpy(display_name, "tw_40"); break; case 5: strcpy(display_name, "tw_30"); break; case 6: strcpy(display_name, "tw_25"); break; case 7: strcpy(display_name, "tw_20"); break; case 8: strcpy(display_name, "tw_15"); break; case 9: strcpy(display_name, "tw_10"); break; case 10: strcpy(display_name, "tw_05"); break; case 11: strcpy(display_name, "tw_00"); break; case 12: strcpy(display_name, "tw_602"); break; case 13: strcpy(display_name, "tw_552"); break; case 14: strcpy(display_name, "tw_502"); break; case 15: strcpy(display_name, "tw_452"); break; case 16: strcpy(display_name, "tw_402"); break; case 17: strcpy(display_name, "tw_302"); break; case 18: strcpy(display_name, "tw_252"); break; case 19: strcpy(display_name, "tw_202"); break; case 20: strcpy(display_name, "tw_152"); break; case 21: strcpy(display_name, "tw_102"); break; case 22: strcpy(display_name, "tw_052"); break; case 23: strcpy(display_name, "tw_002"); break; case 24: strcpy(display_name, "tw_603"); break; case 25: strcpy(display_name, "tw_553"); break; case 26: strcpy(display_name, "tw_503"); break; case 27: strcpy(display_name, "tw_453"); break; case 28: strcpy(display_name, "tw_403"); break; case 29: strcpy(display_name, "tw_303"); break; case 30: strcpy(display_name, "tw_253"); break; case 31: strcpy(display_name, "tw_203"); break; case 32: strcpy(display_name, "tw_153"); break; case 33: strcpy(display_name, "tw_103"); break; case 34: strcpy(display_name, "tw_053"); break; case 35: strcpy(display_name, "tw_003"); break; } PUT2("%.2f %s\n", WhistleTab_tb[whistle_i].y + (float)4.5, display_name); if (whistle_octave < 0) PUT1("%.2f tw_under\n", WhistleTab_tb[whistle_i].y + (float)4.5); if (whistle_octave > 0) PUT1("%.2f tw_over\n", WhistleTab_tb[whistle_i].y + (float)4.5); } } /*********************************************************/ /* */ /* End modification for whistle tablature. */ /* J. S. Atchley, 10/99. */ /* */ /*********************************************************/ /*********************************************************/ /* */ /* Begin modification for string tablature. */ /* J. S. Atchley, 11/99. [JSA] */ /* */ /*********************************************************/ for (i = 0; i < nStringTab; i++) { if (VoiceMute_tb[StringTab_tb[i].voice]) continue; if (StringTab_tb[i].voice != voice) continue; PUT2("/%s %f selectfont\n", cfmt.StrTabFont.name, cfmt.StrTabFont.size); for (j = 0; j < StringTab_tb[i].nstrings; j++) StrUsed[j] = FALSE; /* Sort absolute pitches so highest is first in list. */ for (m = 0; m < s->npitch; m++) oPitches[m] = s->abspitch[m]; for (m = 0; m < s->npitch; m++) { Pitches[m] = oPitches[0]; n = 0; for (j = 1; j < s->npitch; j++) { if (oPitches[j] > Pitches[m]) { Pitches[m] = oPitches[j]; n = j; } } oPitches[n] = -99; /* Decorations are attached to heads and must move with */ /* the pitch. */ Decos[m] = s->TabDeco[n]; } /* First, output notes for which a string has been specified */ /* explicitly in the ABC. */ for (m = 0; m < s->npitch; m++) { NoteTabbed = FALSE; Str = Decos[m] & TD_STRMASK; if (!Str) continue; if (Str > StringTab_tb[i].nstrings) { /* specified string doesn't exist, warn user */ printf("WARNING - Invalid string (%d) specified in tablature\n", Str); continue; } Str--; /* Used as a Zero based index from here on */ if (StrUsed[Str]) { printf("WARNING - Specified tablature string (%d) already used\n", Str + 1); continue; } p = n = StrPitch = StringTab_tb[i].pitches[Str]; /* Calculate min and max actual pitch on capoed string. */ if (StringTab_tb[i].fretting == TABCHROMATIC) { n += StringTab_tb[i].capo; p += 24; } else { n += DulcFrets[StringTab_tb[i].capo]; p += DulcFrets[21]; } if ((Pitches[m] < n) || (Pitches[m] > p)) { /* Pitch is out of range of the specified string. */ /* Warn the user, then ignore the string specification. */ printf("WARNING - Pitch is out of range of specified tablature string (%d)\n", Str + 1); continue; } /* If we get here, we can tab the note on the specified */ /* string. */ yc = StringTab_tb[i].y + (((StringTab_tb[i].nstrings - 1) - Str) * (cfmt.StrTabFont.size * cfmt.StrTabSpace)); ycd1 = yc + cfmt.StrTabFont.size; ycd2 = yc + (cfmt.StrTabFont.size * cfmt.StrTabSpace) - (float)1.0; switch (StringTab_tb[i].fretting) { case TABDULCIMERPLUS: for (Fret = 6; Fret <= 21; Fret += 7) { if (Fret < StringTab_tb[i].capo) continue; if ((StrPitch + DulcFrets[Fret] + 1) == Pitches[m]) { StrUsed[Str] = NoteTabbed = TRUE; sprintf(TabMsg, "%d+", Fret); break; } } /* If the note was not tabbed at one of the 1/2 */ /* frets then we fall through to the next case. */ if (NoteTabbed) break; case TABDULCIMER: for (Fret = StringTab_tb[i].capo; Fret <= 21; Fret++) { if ((StrPitch + DulcFrets[Fret]) == Pitches[m]) { StrUsed[Str] = NoteTabbed = TRUE; sprintf(TabMsg, "%d", Fret); break; } } break; default: /* TABCHROMATIC */ for (Fret = StringTab_tb[i].capo; Fret <= 24; Fret++) { if ((StrPitch + Fret) == Pitches[m]) { StrUsed[Str] = NoteTabbed = TRUE; sprintf(TabMsg, "%d", Fret); break; } } } if (NoteTabbed) { PUT2("x %.1f moveto (%s) cshow\n", yc + (float)1.5, TabMsg); /* tablature decorations */ if (Decos[m] & TD_HAMMER) PUT2("%.1f %.1f tw_hammer\n", ycd1, ycd2); if (Decos[m] & TD_PULL) PUT2("%.1f %.1f tw_pull\n", ycd1, ycd2); if (Decos[m] & TD_TRILL) PUT2("%.1f %.1f tw_trill\n", ycd1, ycd2); if ((Decos[m] & TD_TYPEMASK) == TD_BEND) { if (Decos[m] & TD_FULL) { if (Decos[m] & TD_UP) { PUT2("%.1f %.1f tw_fullbend\n", ycd1, ycd2); } else { PUT2("%.1f %.1f tw_fullrelease\n", ycd1, ycd2); } } else { if (Decos[m] & TD_UP) { PUT2("%.1f %.1f tw_halfbend\n", ycd1, ycd2); } else { PUT2("%.1f %.1f tw_halfrelease\n", ycd1, ycd2); } } } else if (Decos[m] & TD_TYPEMASK) { /* Make sure we have a fret the specified number of half steps away. */ if (Decos[m] & TD_UP) { if ((Decos[m] & TD_TYPEMASK) == TD_SLIDETO) { SlidePitch = Pitches[m] + ((Decos[m] & TD_FRETMASK) >> 4); } else { SlidePitch = Pitches[m] - ((Decos[m] & TD_FRETMASK) >> 4); } } else { if ((Decos[m] & TD_TYPEMASK) == TD_SLIDEFROM) { SlidePitch = Pitches[m] + ((Decos[m] & TD_FRETMASK) >> 4); } else { SlidePitch = Pitches[m] - ((Decos[m] & TD_FRETMASK) >> 4); } } NoteTabbed = FALSE; /* Temporarily clear */ switch (StringTab_tb[i].fretting) { case TABDULCIMERPLUS: for (Fret = 6; Fret <= 21; Fret += 7) { if (Fret < StringTab_tb[i].capo) continue; if ((StrPitch + DulcFrets[Fret] + 1) == SlidePitch) { NoteTabbed = TRUE; sprintf(TabMsg, "%d+", Fret); break; } } /* If the note was not tabbed at one of the 1/2 */ /* frets then we fall through to the next case. */ if (NoteTabbed) break; case TABDULCIMER: for (Fret = StringTab_tb[i].capo; Fret <= 21; Fret++) { if ((StrPitch + DulcFrets[Fret]) == SlidePitch) { NoteTabbed = TRUE; sprintf(TabMsg, "%d", Fret); break; } } break; default: /* TABCHROMATIC */ for (Fret = StringTab_tb[i].capo; Fret <= 24; Fret++) { if ((StrPitch + Fret) == SlidePitch) { NoteTabbed = TRUE; sprintf(TabMsg, "%d", Fret); break; } } } if (NoteTabbed) { if ((Decos[m] & TD_TYPEMASK) == TD_SLIDETO) { if (Decos[m] & TD_UP) { PUT3("%.1f %.1f (%s) tw_slideupto\n", ycd1, ycd2, TabMsg); } else { PUT3("%.1f %.1f (%s) tw_slidedownto\n", ycd1, ycd2, TabMsg); } } else { if (Decos[m] & TD_UP) { PUT3("%.1f %.1f (%s) tw_slideupfrom\n", ycd1, ycd2, TabMsg); } else { PUT3("%.1f %.1f (%s) tw_slidedownfrom\n", ycd1, ycd2, TabMsg); } } } NoteTabbed = TRUE; } Pitches[m] = -99; } else { printf("WARNING - Unable to tab pitch on specified string\n"); } } /* Now, we can output notes which haven't had a string specified*/ /* explicitly in the ABC. */ /* Guitar tab typically runs high to low (top to */ /* bottom) while dulcimer tab is the other way around */ if (StringTab_tb[i].pitches[0] > StringTab_tb[i].pitches[StringTab_tb[i].nstrings - 1]) { /* We have guitar-style tab with the treble strings on top. */ Str1 = 0; StrN = StringTab_tb[i].nstrings; IncDec = 1; } else { /* We have dulcimer-style tab with the treble on the bottom.*/ Str1 = StringTab_tb[i].nstrings - 1; StrN = -1; IncDec = -1; } for (m = 0; m < s->npitch; m++) { /* For all of the notes in this chord. */ NoteTabbed = TRUE; if (Pitches[m] == -99) continue; if (s->ti2[m] || s->EndTie) continue; /* TODO: consider sorting */ NoteTabbed = FALSE; for (Str = Str1; Str != StrN; Str += IncDec) { /* For all of the strings in this tablature. */ if (StrUsed[Str]) continue; /* string already used */ n = StrPitch = StringTab_tb[i].pitches[Str]; /* Calculate minimum actual pitch on capoed string. */ if (StringTab_tb[i].fretting == TABCHROMATIC) { n += StringTab_tb[i].capo; } else { n += DulcFrets[StringTab_tb[i].capo]; } if (Pitches[m] < n) continue; /* pitch too low for this string */ yc = StringTab_tb[i].y + (((StringTab_tb[i].nstrings - 1) - Str) * (cfmt.StrTabFont.size * cfmt.StrTabSpace)); ycd1 = yc + cfmt.StrTabFont.size; ycd2 = yc + (cfmt.StrTabFont.size * cfmt.StrTabSpace) - (float)1.0; switch (StringTab_tb[i].fretting) { case TABDULCIMERPLUS: for (Fret = 6; Fret <= 21; Fret += 7) { if (Fret < StringTab_tb[i].capo) continue; if ((StrPitch + DulcFrets[Fret] + 1) == Pitches[m]) { StrUsed[Str] = NoteTabbed = TRUE; sprintf(TabMsg, "%d+", Fret); break; } } /* If the note was not tabbed at one of the 1/2 */ /* frets then we fall through to the next case. */ if (NoteTabbed) break; case TABDULCIMER: for (Fret = StringTab_tb[i].capo; Fret <= 21; Fret++) { if ((StrPitch + DulcFrets[Fret]) == Pitches[m]) { StrUsed[Str] = NoteTabbed = TRUE; sprintf(TabMsg, "%d", Fret); break; } } break; default: /* TABCHROMATIC */ for (Fret = StringTab_tb[i].capo; Fret <= 24; Fret++) { if ((StrPitch + Fret) == Pitches[m]) { StrUsed[Str] = NoteTabbed = TRUE; sprintf(TabMsg, "%d", Fret); break; } } } if (NoteTabbed) { PUT2("x %.1f moveto (%s) cshow\n", yc + (float)1.5, TabMsg); /* tablature decorations */ if (Decos[m] & TD_HAMMER) PUT2("%.1f %.1f tw_hammer\n", ycd1, ycd2); if (Decos[m] & TD_PULL) PUT2("%.1f %.1f tw_pull\n", ycd1, ycd2); if (Decos[m] & TD_TRILL) PUT2("%.1f %.1f tw_trill\n", ycd1, ycd2); if ((Decos[m] & TD_TYPEMASK) == TD_BEND) { if (Decos[m] & TD_FULL) { if (Decos[m] & TD_UP) { PUT2("%.1f %.1f tw_fullbend\n", ycd1, ycd2); } else { PUT2("%.1f %.1f tw_fullrelease\n", ycd1, ycd2); } } else { if (Decos[m] & TD_UP) { PUT2("%.1f %.1f tw_halfbend\n", ycd1, ycd2); } else { PUT2("%.1f %.1f tw_halfrelease\n", ycd1, ycd2); } } } else if (Decos[m] & TD_TYPEMASK) { /* Make sure we have a fret the specified number of half steps away. */ if (Decos[m] & TD_UP) { if ((Decos[m] & TD_TYPEMASK) == TD_SLIDETO) { SlidePitch = Pitches[m] + ((Decos[m] & TD_FRETMASK) >> 4); } else { SlidePitch = Pitches[m] - ((Decos[m] & TD_FRETMASK) >> 4); } } else { if ((Decos[m] & TD_TYPEMASK) == TD_SLIDEFROM) { SlidePitch = Pitches[m] + ((Decos[m] & TD_FRETMASK) >> 4); } else { SlidePitch = Pitches[m] - ((Decos[m] & TD_FRETMASK) >> 4); } } NoteTabbed = FALSE; /* Temporarily clear */ switch (StringTab_tb[i].fretting) { case TABDULCIMERPLUS: for (Fret = 6; Fret <= 21; Fret += 7) { if (Fret < StringTab_tb[i].capo) continue; if ((StrPitch + DulcFrets[Fret] + 1) == SlidePitch) { NoteTabbed = TRUE; sprintf(TabMsg, "%d+", Fret); break; } } /* If the note was not tabbed at one of the 1/2 */ /* frets then we fall through to the next case. */ if (NoteTabbed) break; case TABDULCIMER: for (Fret = StringTab_tb[i].capo; Fret <= 21; Fret++) { if ((StrPitch + DulcFrets[Fret]) == SlidePitch) { NoteTabbed = TRUE; sprintf(TabMsg, "%d", Fret); break; } } break; default: /* TABCHROMATIC */ for (Fret = StringTab_tb[i].capo; Fret <= 24; Fret++) { if ((StrPitch + Fret) == SlidePitch) { NoteTabbed = TRUE; sprintf(TabMsg, "%d", Fret); break; } } } if (NoteTabbed) { if ((Decos[m] & TD_TYPEMASK) == TD_SLIDETO) { if (Decos[m] & TD_UP) { PUT3("%.1f %.1f (%s) tw_slideupto\n", ycd1, ycd2, TabMsg); } else { PUT3("%.1f %.1f (%s) tw_slidedownto\n", ycd1, ycd2, TabMsg); } } else { if (Decos[m] & TD_UP) { PUT3("%.1f %.1f (%s) tw_slideupfrom\n", ycd1, ycd2, TabMsg); } else { PUT3("%.1f %.1f (%s) tw_slidedownfrom\n", ycd1, ycd2, TabMsg); } } } NoteTabbed = TRUE; } break; } } /* end for each string */ if (!NoteTabbed) printf("Warning, note out of tab range.\n"); } /* end for each pitch */ /* If the style is "marked" and we have an up or down bow */ /* mark all of the unused strings at the capo fret. */ if ((StringTab_tb[i].style == TABMARKED) && !s->EndTie){ IsMarked = FALSE; for (j = 0; j < s->dc.n; j++) { if ((s->dc.t[j] == D_UPBOW) || (s->dc.t[j] == D_DOWNBOW)) { IsMarked = TRUE; break; } } if (IsMarked) { sprintf(TabMsg, "%d", StringTab_tb[i].capo); for (Str = 0; Str < StringTab_tb[i].nstrings; Str++) { if (!StrUsed[Str]) { yc = StringTab_tb[i].y + (((StringTab_tb[i].nstrings - 1) - Str) * (cfmt.StrTabFont.size * cfmt.StrTabSpace)); PUT2("x %.1f moveto (%s) cshow\n", yc + (float)1.5, TabMsg); } } } } } /* end for each string tablature set */ /*********************************************************/ /* */ /* End modification for string tablature. */ /* J. S. Atchley, 11/99. */ /* */ /*********************************************************/ } /* ----- rnd6: up/down shift needed to get k*6 ----- */ static float rnd6(float x) { int ix,iy,ir; ix=(int)(x+600.999-3.0); ir=ix%6; iy=ix-600; if (ir>0) iy += 6-ir; return (float) iy - x; } /* ----- b_pos ----- */ static float b_pos(int stem, int flags, float b) { float bb,d1,d2,add; float top,bot; if (stem > 0) { top=b; bot=b-(flags-1)*BEAM_SHIFT-BEAM_DEPTH; if (bot>26) return b; } else { bot=b; top=b+(flags-1)*BEAM_SHIFT+BEAM_DEPTH; if (top<-2) return b; } d1=rnd6(top-BEAM_OFFSET); d2=rnd6(bot+BEAM_OFFSET); add=d1; if (d1*d1>d2*d2) add=d2; bb=b+add; /* printf("stem %d top %.1f, bot%.1f, choices %.1f %.1f => %.1f\n", stem, top,bot, d1,d2, add); */ /* printf("b_pos(%d) b=%.1f to %.1f\n", stem,b,bb); */ return bb; } /* ----- calculate_beam ----- */ static int calculate_beam(int j1, struct BEAM *bm) { int j,j2,i,stem,notes,flags; float x,y,ys,a,b,max_stem_err,stem_err,min_stem,slen,yyg,yg,try; float s,sx,sy,sxx,sxy,syy,delta,hh,a0; j2=-1; /* find first and last note in beam */ bm->staffb = staff_tb[(int) sym[j1].staff].y; /* bottom of staff */ stem=sym[j1].stem; for (j=j1;jflags) flags=sym[j].flags; notes++; } } s=sx=sy=sxx=sxy=syy=0; /* linear fit through stem ends */ for (j=j1;j<=j2;j++) if (sym[j].type==NOTE) { int stem_len; x = sym[j].xs; stem_len = (int)(STEM * stem); if (stem > 0) { if (sym[j].pits[0] > 26) { stem_len -= 2; if (sym[j].pits[0] > 28) stem_len -= 2; } } else { if (sym[j].pits[0] < 18) { stem_len += 2; if (sym[j].pits[0] < 16) stem_len += 2; } } y = (float)sym[j].ymx + stem_len; s += 1; sx += x; sy += y; sxx += x*x; sxy += x*y; syy += y*y; } delta=s*sxx-sx*sx; /* beam fct: y=ax+b */ a=(s*sxy-sx*sy)/delta; b=(sy-a*sx)/s; /* the next few lines modify the slope of the beam */ if (notes >= 3) { hh = syy-a*sxy-b*sy; /* flatten if notes not in line */ 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); /* to decide if to draw flat etc. use normalized slope a0 */ a0=a*(sym[j2].xs-sym[j1].xs)/(20*(notes-1)); if ((a0-BEAM_THRESH)) a=0; /* flat below threshhold */ b=(sy-a*sx)/s; /* recalculate b for new slope */ /* if (flags>1) b=b+2*stem;*/ /* leave a bit more room if several beams */ if (bagpipe) { b=-11; a=0; } max_stem_err=0; /* check stem lengths */ for (j=j1;j<=j2;j++) if (sym[j].type==NOTE) { if (sym[j].npitch==1) { min_stem=STEM_MIN; if (sym[j].flags==2) min_stem=STEM_MIN2; else if (sym[j].flags==3) min_stem=STEM_MIN3; else if (sym[j].flags==4) min_stem=STEM_MIN4; } else { min_stem=STEM_CH_MIN; if (sym[j].flags==2) min_stem=STEM_CH_MIN2; else if (sym[j].flags==3) min_stem=STEM_CH_MIN3; else if (sym[j].flags==4) min_stem=STEM_CH_MIN4; } min_stem += BEAM_DEPTH + BEAM_SHIFT * (sym[j].flags-1); ys=a*sym[j].xs+b; if (stem > 0) { slen = ys - sym[j].ymx; #if 1 if (sym[j].pits[0] > 26) { min_stem -= 2; if (sym[j].pits[0] > 28) min_stem -= 2; } #endif } else { slen = sym[j].ymn - ys; #if 1 if (sym[j].pits[0] < 18) { min_stem -= 2; if (sym[j].pits[0] < 16) min_stem -= 2; } #endif } stem_err = min_stem - slen; if (max_stem_err < stem_err) max_stem_err = stem_err; } if (max_stem_err > 0) /* shift beam if stems too short */ b += stem * max_stem_err; for (j=j1+1;j<=j2;j++) if (sym[j].type==NOTE) { /* room for gracenotes */ for (i=0;i 0) { try = yg + GSTEM - yyg - BEAM_DEPTH - 2; if (try>0) b += try; } else { try = yg - yyg + BEAM_DEPTH + 7; if (try<0) b += try; } } } if ((a<0.01) && (a>-0.01)) /* shift flat beams onto staff lines */ b=b_pos(stem,flags,b); for (j=j1;j<=j2;j++) if (sym[j].type==NOTE) /* final stems */ sym[j].ys = a * sym[j].xs + b; bm->i1 = j1; /* save beam parameters in struct */ bm->i2 = j2; bm->a = a; bm->b = b; bm->stem = stem; bm->t = stem * BEAM_DEPTH; return 1; } /* ----- rest_under_beam ----- */ static float rest_under_beam(float x, int head, struct BEAM *bm) { float y,tspace,bspace; int j1,j2,j,nf,iy; tspace=9; bspace=11; if ((head==H_OVAL)||(head==H_EMPTY)) tspace = bspace = 4; j1=bm->i1; j2=bm->i2; nf=0; for (j=j1;j<=j2;j++) if ((sym[j].type==NOTE)||(sym[j].flags>nf)) nf=sym[j].flags; if (bm->stem > 0) { y = bm->a * x + bm->b - BEAM_DEPTH - (nf - 1) * BEAM_SHIFT - tspace; if (y > 12) y = 12; } else { y = bm->a * x + bm->b + BEAM_DEPTH + (nf - 1) * BEAM_SHIFT + bspace; if (y < 12) y = 12; } if ((head==H_OVAL)||(head==H_EMPTY)) { iy = (int)((y + 3.0) / 6.0); y = (float)6*iy; } return y; } /* ----- draw_beam_num: draw number on a beam ----- */ static void draw_beam_num(struct BEAM *bm, int num, float xn) { float yn; if (bm->stem < 0) yn=bm->a*xn+bm->b-12; else yn=bm->a*xn+bm->b+4; PUT3("%.1f %.1f (%d) bnum\n", xn, yn + bm->staffb, num); } /* ----- draw_beam: draw a single beam ----- */ static void draw_beam (float x1, float x2, float dy, struct BEAM *bm) { float y1,y2; y1=bm->a*x1+bm->b-bm->stem*dy + bm->staffb; y2=bm->a*x2+bm->b-bm->stem*dy + bm->staffb; PUT5("%.1f %.1f %.1f %.1f %.1f bm\n", x1,y1,x2,y2,bm->t); } /* ----- draw_beams: draw the beams for one word ----- */ static void draw_beams(struct BEAM *bm) { int j,j1,j2,j3,inbeam,k1,k2,p,r; float x1,x2,xn; j1=bm->i1; j2=bm->i2; /* make first beam over whole word */ x1=sym[j1].xs; x2=sym[j2].xs; for (j=j1;j<=j2;j++) { /* numbers for nplets on same beam */ if (sym[j].p_plet>0) { p=sym[j].p_plet; r=sym[j].r_plet; j3=j+r-1; if (j3<=j2) { xn=(float)0.5*(sym[j].xs+sym[j3].xs); draw_beam_num(bm,p,xn); sym[j].p_plet=0; } } } draw_beam(x1,x2,0.0,bm); /* second beams where two or more flags */ k1 = k2 = 0; inbeam=0; for (j=j1;j<=j2;j++) { if (sym[j].type!=NOTE) continue; if (!inbeam && (sym[j].flags>=2)) { k1=j; inbeam=1; } if (inbeam && ((sym[j].flags<2) || (j==j2))) { if ((sym[j].flags>=2) && (j==j2)) k2=j; x1=sym[k1].xs; x2=sym[k2].xs; inbeam=0; if (k1==k2) { if (k1==j1) draw_beam(x1+BEAM_STUB,x1,BEAM_SHIFT,bm); else draw_beam(x1-BEAM_STUB,x1,BEAM_SHIFT,bm); } else draw_beam(x1,x2,BEAM_SHIFT,bm); inbeam=0; } k2=j; } /* third beams where three or more flags */ k1 = k2 = 0; inbeam=0; for (j=j1;j<=j2;j++) { if (sym[j].type!=NOTE) continue; if ((!inbeam) && (sym[j].flags>=3)) { k1=j; inbeam=1; } if (inbeam && ((sym[j].flags<3) || (j==j2))) { if ((sym[j].flags>=3) && (j==j2)) k2=j; x1=sym[k1].xs; x2=sym[k2].xs; inbeam=0; if (k1==k2) { if (k1==j1) draw_beam(x1+BEAM_STUB,x1,2*BEAM_SHIFT,bm); else draw_beam(x1-BEAM_STUB,x1,2*BEAM_SHIFT,bm); } else draw_beam(x1,x2,2*BEAM_SHIFT,bm); inbeam=0; } k2=j; } /* fourth beams where four or more flags */ k1 = k2 = 0; inbeam=0; for (j=j1;j<=j2;j++) { if (sym[j].type!=NOTE) continue; if ((!inbeam) && (sym[j].flags>=4)) { k1=j; inbeam=1; } if (inbeam && ((sym[j].flags<4) || (j==j2))) { if ((sym[j].flags>=4) && (j==j2)) k2=j; x1=sym[k1].xs; x2=sym[k2].xs; inbeam=0; if (k1==k2) { if (k1==j1) draw_beam(x1+BEAM_STUB,x1,3*BEAM_SHIFT,bm); else draw_beam(x1-BEAM_STUB,x1,3*BEAM_SHIFT,bm); } else draw_beam(x1,x2,3*BEAM_SHIFT,bm); inbeam=0; } k2=j; } } /* ----- extreme: return min or max, depending on s ----- */ static float extreme(int s, float a, float b) { if (s>0) return a > b ? a : b; return a > b ? b : a; } /* ----- draw_bracket ----- */ static void draw_bracket(int p, int j1, int j2) { float x1,x2,y1,y2,xm,ym,s,s0,xx,yy,yx,dy; int j; float staffb; x1=sym[j1].x-4; x2=sym[j2].x+4; y1=(float)sym[j1].ymx+10; y2=(float)sym[j2].ymx+10; if (sym[j1].stem > 0) { y1=sym[j1].ys+4; x1 += 3; } if (sym[j2].stem > 0) { y2=sym[j2].ys+4; x2 += 3; } if (y1<30) y1=30; if (y2<30) y2=30; xm=(float)0.5*(x1+x2); ym=(float)0.5*(y1+y2); s=(y2-y1)/(x2-x1); s0=(sym[j2].ymx-sym[j1].ymx)/(x2-x1); if (s0>0) { if (s<0) s=0; if (s>s0) s=s0; } else { if (s>0) s=0; if (s 0) yx=sym[j].ys+5; if (yx-yy>dy) dy=yx-yy; } } ym += dy; y1=ym+s*(x1-xm); y2=ym+s*(x2-xm); staffb = staff_tb[(int) sym[j1].staff].y; /* bottom of staff */ xx=xm-6; yy=ym+s*(xx-xm) + staffb; PUT4("%.1f %.1f %.1f %.1f hbr ", x1,y1 + staffb,xx,yy); xx=xm+6; yy=ym+s*(xx-xm) + staffb; PUT4("%.1f %.1f %.1f %.1f hbr ", x2,y2 + staffb,xx,yy); yy=(float)0.5*(y1+y2) + staffb; PUT3("%.1f %.1f (%d) bnum\n", xm, yy-4, p); } /* ----- draw_nplet_brackets ----- */ static void draw_nplet_brackets(void) { int i,j,r; for (i = 0; i < nsym; i++) { if ((sym[i].type == NOTE || sym[i].type == REST) && sym[i].p_plet > 0) { r=sym[i].r_plet; for (j=i;j40) { add=(float)0.2*(dx-40)/100; alfa=(float)0.3+add; if (alfa>0.7) alfa=(float)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=(float)0.5*(x1+x2); my=(float)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=(float)0.03*(x2-x1); if (dx>10.0) dx=(float)10.0; dy=(float)1.0; dz=(float)0.20; if (x2-x1>100) dz=dz+(float)0.001*(x2-x1); if (dz>0.6) dz=(float)0.6; PUT4("%.1f %.1f %.1f %.1f ", xx2-dx, yy2+shift+s*dy, xx1+dx, yy1+shift+s*dy); PUT3("%.1f %.1f 0 %.1f ", x1,y1+shift+s*dz,s*dz); PUT4("%.1f %.1f %.1f %.1f ", xx1,yy1+shift,xx2,yy2+shift); PUT4("%.1f %.1f %.1f %.1f SL\n", x2,y2+shift, x1,y1+shift); /*PUT4("%.2f %.2f %.2f %.2f ", xx1,yy1+shift,xx2,yy2+shift); PUT4("%.2f %.2f %.2f %.2f sl\n", x2,y2+shift, x1,y1+shift);*/ /* Adjust required clearance for all symbols that are within the */ /* slur and on the same staff. */ Voice = sym[k1].Voice; Staff = voice_tb[Voice].staff; xk1 = sym[k1].x; xk2 = sym[k2].x; y1_yy1_l = y1_yy1_h = y1; if (y1_yy1_l > yy1) y1_yy1_l = yy1; if (y1_yy1_h < yy1) y1_yy1_h = yy1; y_m = ((s * height) + my);// - staff_tb[voice_tb[sym[k1].Voice].staff].y; yy2_y2_l = yy2_y2_h = yy2; if (yy2_y2_l > y2) yy2_y2_l = y2; if (yy2_y2_h < y2) yy2_y2_h = y2; for (i = k1; i <= k2; i++) { if (voice_tb[(int)sym[i].Voice].staff != Staff) continue; /* Rather than just force all decorations the full height away, */ /* make a better guess using the bezier points of the slur. */ if ((sym[i].x >= xk1) && (sym[i].x <= xx1)) { if (sym[i].ylo > y1_yy1_l) sym[i].ylo = y1_yy1_l; if (sym[i].yhi < y1_yy1_h) sym[i].yhi = y1_yy1_h; } else if ((sym[i].x >= xx1) && (sym[i].x <= xk2)) { if (sym[i].ylo > y_m) sym[i].ylo = y_m; if (sym[i].yhi < y_m) sym[i].yhi = y_m; } else { if (sym[i].ylo > yy2_y2_l) sym[i].ylo = yy2_y2_l; if (sym[i].yhi < yy2_y2_h) sym[i].yhi = yy2_y2_h; } } } /* ----- draw_slur (not a pretty routine, this) ----- */ static void draw_slur(int k1, int k2, int nn, int level) { // float x01,x02,y01,y02; float xa[3], ya[3]; int ka[3]; float x1,y1,x2,y2,yy,height,addx,addy; float shift,hmin,a; float x,y,z,h,dx,dy; int i, s, j; ka[1] = k1; ka[2] = k2; if ((sym[ka[1]].type == COMBINEDNOTE) || (sym[ka[2]].type == COMBINEDNOTE)) return; /* [JSA] */ s = slur_direction(ka[1], ka[2]); /* fix endpoints */ for (j = 1; j <= 2; j++) { if (sym[ka[j]].type == NOTE) { /* here if k1 points to note */ xa[j] = sym[ka[j]].x; yy = (float)sym[ka[j]].dc.bot; if (s > 0) yy = (float)sym[ka[j]].dc.top; ya[j] = extreme(s, yy + (s * 6), sym[ka[j]].ys + (s * 2)); if (sym[ka[j]].word_end) { yy = (float)sym[ka[j]].dc.bot; if (s > 0) yy=(float)sym[ka[j]].dc.top; ya[j]= yy + (s * 6); if ((s > 0) && ((sym[ka[j]].stem > 0) || (sym[ka[j]].stem && sym[ka[j]].Unison))) xa[j] += 4; } } } /* This code makes the assumption that at least one of the */ /* ends of the slur was a note and had its y value set above. */ /* Don't know if that's a problem or not, at this point. */ /* In any case, this change to two loops is functionally */ /* equivalent to the 4 separate blocks of the previous code. */ for (j = 1; j <= 2; j++) { if (sym[ka[j]].type != NOTE) { xa[j] = sym[ka[j]].x + sym[ka[j]].wr; ya[j] = ya[3 - j] + ((float)1.2 * s); if (nn > 1) { if (s > 0) { if (ya[j] < 28) ya[j] = 28; } else { if (ya[j] > -4) ya[j] = -4; } } } } /* shift endpoints */ addx =(float)0.04 * (xa[2] - xa[1]); if (addx > 3.0) addx = 3.0; addy = (float)0.02 * (xa[2] - xa[1]); if (addy > 3.0) addy = 3.0; x1 = xa[1] + addx; x2 = xa[2] - addx; y1 = ya[1] + (s * addy); y2 = ya[2] + (s * addy); a=(y2-y1)/(x2-x1); /* slur steepness */ if (a > SLUR_SLOPE) a = SLUR_SLOPE; else if (a < -SLUR_SLOPE) a = -SLUR_SLOPE; if (a>0) { if (s > 0) y1=y2-a*(x2-x1); else y2=y1+a*(x2-x1); } else { if (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=(float)0.5*z; dy=(float)0.3*z; if (y>0) { if (s > 0) { x2 -= dx; y2 -= dy; } else { x1 += dx; y1 += dy; } } else { if (s > 0) { x1 += dx; y1 -= dy; } else { x2 -= dx; y2 += dy; } } h=0; for (i=k1+1; i0) yy=(float)sym[i].ymx; y = extreme(s, yy+6*s, sym[i].ys+2*s); z = (y2*(x-x1)+y1*(x2-x))/(x2-x1); h = extreme(s, h, y-z); } y1 += (float)0.4*h; y2 += (float)0.4*h; h *= (float)0.6; hmin=((float)0.03*(x2-x1)+(float)8.)*s; if (nn>3) hmin=s*((float)0.12*(x2-x1)+12); height = extreme(s, hmin, (float)3.0*h); height = extreme(-s, height, (float)50.*s); y=y2-y1; if (y<0) y=-y; if (s > 0 && (height< 0.8*y)) height=(float)0.8*y; else if (s < 0 && (height>-0.8*y)) height=(float)-0.8*y; shift=(float)3.*s*level; output_slur(x1,y1,x2,y2,(float)s,height, shift + staff_tb[(int) sym[k1].staff].y, k1, k2); } /* ----- prev_scut, next_scut: find place to terminate/start slur --- */ static int next_scut(int j) { for (j++;j=0;j--) { if (sym[j].type==BAR && (sym[j].u==B_LREP || sym[j].u==B_DREP || sym[j].u==B_FAT1 || sym[j].u==B_FAT2 || sym[j].v==2)) return j; } /* return sym before first note */ for (j=0;jptop) ptop=p1; } for (i=0;i3.0) addx=(float)3.0; addy=(float)0.02*(x2-x1); if (addy>3.0) addy=(float)3.0; x1 += 3+addx; x2 -= 3+addx; if (s > 0 && sym[k1].stem > 0) x1 += 1.5; else if (s < 0 && sym[k2].stem < 0) x2 -= 1.5; y=3*(p1-18); y1=y2=y+s*(4+addy); y=3*(p2-18); y2=y+s*(4+addy); if (s > 0 && !(y%6) && (sym[k1].dots>0)) { y2=y1=y+s*((float)5.5+addy); x1 -= 2; x2 += 2; } height=s*((float)0.04*(x2-x1)+5); shift=0; output_slur(x1,y1,x2,y2,s,height, shift + staff_tb[(int)sym[k1].staff].y, k1, k2); } } /* ----- draw_slurs: draw slurs/ties between neighboring notes/chords --- */ static void draw_slurs(int k1, int k2, int job) { int i,m1,m2; int mhead1[MAXHD],mhead2[MAXHD],nslur,nh1,nh2; if (nbuf+100>BUFFSZ) rx("PS output exceeds reserved space per staff", " -- increase BUFFSZ1"); nslur=0; if (job==2) { /* half slurs from last note in line */ nh1=sym[k1].npitch; for (i=1;i<=nh1;i++) { for (m1=0;m1 BUFFSZ) rx("PS output exceeds reserved space per staff", " -- increase BUFFSZ1"); nn = 0; for (i=k1;i<=k2;i++) if (sym[i].type == NOTE || sym[i].type == REST) nn++; draw_slur(k1, k2, nn, level); } /* ----- draw_all_slurs: draw all slurs/ties between neighboring notes --- */ static void draw_all_slurs(void) { int i,i1,i2; i1=-1; for (i=0;i=0) { cut=next_scut(i); if (cut yword) yword = cfmt.vocalfont.size - staff_tb[staff].botpos; staffb = staff_tb[staff].y /* bottom of staff */ - yword; for (j=0;j1) && (word[l-1]=='^')) { word[l-1]='\0'; hyflag=1; } if ((l==1) && (word[0]=='_')) { if (lastx<0) lastx=sym[i-1].x+sym[i-1].wr; PUT3("%.1f %.1f %.1f wln ", lastx+3, sym[i].x+1, staffb); } else if ((l==1) && (word[0]=='^')) { PUT2("%.1f %.1f whf ", x0, staffb); lastx=x0+vfsize*swfac*w; } else { tex_str(word,t,&w); if (isdigit(word[0])) x0 -= 3 * vfsize * swfac * cwid('1'); else x0 -= VOCPRE * vfsize * swfac * w; PUT3("(%s) %.1f %.1f wd ", t, x0, staffb); lastx=x0+vfsize*swfac*w; } } } if (hyflag) PUT2("%.1f %.1f whf ", lastx+5, staffb); staffb -= lskip; } } /********************************************************/ /* Moved guitar-chord drawing here as we are now doing */ /* some simple formatting of the chords (now that */ /* other text is handled separately). [JSA] 05/00 */ /* This displays guitar chords as: */ /* Chd(AltChrd) */ /* --- --- */ /* B B (bass notes if present) */ /* It works with either of two syntax structures, i.e. */ /* "Gm/E(A)" and "Gm/E""A" will draw identical output. */ /********************************************************/ static void DrawGuitarChords(int voice, float gchy) { const int IdxPrim = 0, IdxPrimBass = 1, IdxAlt = 2, IdxAltBass = 3; static char buffs[4][32], psbuffs[4][81], raw[128]; char *p, *pPrimBass, *pAlt, *pAltBass, *tp; int i, iDeco, iPart, iBass, iChord; float w[4], swfac, wPrim, wAlt, y, xPrim, xAlt, dx; struct SYMBOL *s; tFIFO *Fifo; BOOL HasBass; set_font(cfmt.gchordfont,0); swfac=1.0; if (strstr(cfmt.gchordfont.name,"Times-Bold")) swfac=(float)1.05; else if (strstr(cfmt.gchordfont.name,"Helvetica")) swfac=(float)1.10; else if (strstr(cfmt.gchordfont.name,"Helvetica-Bold")) swfac=(float)1.15; /* Preview the line for even one instance of an optional bass note. */ /* We need to leave extra vertical space on other chords if even */ /* one has a bass note. */ HasBass = FALSE; for (i = 0, s = sym; i < nsym; i++, s++) { if ((s->type == NOTE) || (s->type == REST)) { for (iDeco = 0; iDeco < s->dc.n; iDeco++) { if (s->dc.t[iDeco] == D_GCHORDB) { HasBass = TRUE; break; } } } if (HasBass) break; } Fifo = &voice_tb[voice].Chords; /* check all symbols on this line for gchords */ for (i = 0, s = sym; i < nsym; i++, s++) { if ((s->type == NOTE) || (s->type == REST)) { /* Clear gchord part buffers for new symbol */ for (iPart = 0; iPart <= IdxAltBass; iPart++) buffs[iPart][0] = '\0'; iChord = IdxPrim; iBass = IdxPrimBass; for (iDeco = 0; iDeco < s->dc.n; iDeco++) { if ((s->dc.t[iDeco] == D_GCHORD) || (s->dc.t[iDeco] == D_GCHORDB)) { /* Get the guitar chord from the FIFO buffer. */ if (!(Fifo->strings[Fifo->outidx])) { /* warn user of error */ printf("Non-fatal error - guitar chord FIFO underflow in voice %d.\n", voice); /* we can't just return at this point because there */ /* may be a chord pending output. */ continue; } /* remove the chord string from the Fifo buffer */ strncpy(raw, Fifo->strings[Fifo->outidx], 127); p = raw; free(Fifo->strings[Fifo->outidx]); Fifo->strings[Fifo->outidx] = NULL; Fifo->outidx = (Fifo->outidx + 1) % MAXSTRDECO; if (iChord > IdxAlt) continue; /* We already have two chords. */ /* Get pointers to four possible parts. */ pPrimBass = strchr(p, '/'); pAlt = strchr(p, '('); pAltBass = NULL; if (pAlt) { pAlt++; pAltBass = strchr(pAlt, '/'); } if (pPrimBass == pAltBass) pPrimBass = NULL; if (pPrimBass) pPrimBass++; if (pAltBass) pAltBass++; /* Copy primary parts to buffers (even though this is */ /* a primary chord in the source it might be an alternate */ /* chord because there may have been a previous chord field */ /* attached to this symbol). */ if (iChord == IdxAlt) { buffs[iChord][0] = '('; strcpy(&buffs[iChord][1], p); } else { strcpy(buffs[iChord], p); } tp = strpbrk(&buffs[iChord][1], "()/"); if (tp) *tp = '\0'; if (iChord == IdxAlt) strcat(buffs[iChord], ")"); if (pPrimBass) { strcpy(buffs[iBass], pPrimBass); tp = strpbrk(buffs[iBass], "()/"); if (tp) *tp = '\0'; } else { buffs[iBass][0] = '\0'; } iChord += 2; iBass += 2; if (iChord > IdxAlt) continue; /* We already have two chords. */ /* Copy alternate parts to buffers. */ if (pAlt) { buffs[iChord][0] = '('; strcpy(&buffs[iChord][1], pAlt); tp = strpbrk(&buffs[iChord][1], "()/"); if (tp) *tp = '\0'; strcat(buffs[iChord], ")"); } else { buffs[iChord][0] = '\0'; } if (pAltBass) { strcpy(buffs[iBass], pAltBass); tp = strpbrk(buffs[iBass], "()/"); if (tp) *tp = '\0'; } else { buffs[iBass][0] = '\0'; } if (pAlt) { iChord += 2; iBass += 2; } } /* ...if decoration = guitar chord. */ } /* ...for each decoration on this symbol */ /* Now that we have the four possible parts separated, */ /* do the tex formatting and calculate space required. */ for (iPart = 0; iPart <= IdxAltBass; iPart++) { if (buffs[iPart][0]) { tex_str(buffs[iPart], psbuffs[iPart], &w[iPart]); w[iPart] *= cfmt.gchordfont.size; w[iPart] *= swfac; } else { psbuffs[iPart][0] = '\0'; w[iPart] = 0; } } wPrim = w[IdxPrim]; if (wPrim < w[IdxPrimBass]) wPrim = w[IdxPrimBass]; wAlt = w[IdxAlt]; if (wAlt < w[IdxAltBass]) wAlt = w[IdxAltBass]; if (wPrim <= 0) continue; /* no gchord for this symbol */ if (wAlt > 0) { /* adjust horizontal spacing... */ xPrim = s->x - (wPrim / 2); xAlt = s->x + (wAlt / 2); /* ...but don't move too far left. */ dx = (s->x - xPrim) - 8; if (dx > 0) { xPrim += dx; xAlt += dx; } } else { xPrim = s->x; } /* we have at least one bass note to output */ if (psbuffs[IdxPrimBass][0]) { y = gchy; PUT3(" %.1f %.1f moveto (%s) cshow ", xPrim, y, psbuffs[IdxPrimBass]); y += (cfmt.gchordfont.size * (float)0.85); PUT3("%.1f %.1f moveto %.1f 0 rlineto stroke ", (xPrim - (wPrim / 2)) + 1, y, wPrim - 2); y += 2; } if (psbuffs[IdxAltBass][0]) { y = gchy; PUT3(" %.1f %.1f moveto (%s) cshow ", xAlt, y, psbuffs[IdxAltBass]); y += (cfmt.gchordfont.size * (float)0.85); PUT3("%.1f %.1f moveto %.1f 0 rlineto stroke ", (xAlt - (wAlt / 2)) + 1, y, wAlt - 2); y += 2; } y = gchy; if (HasBass) y += (cfmt.gchordfont.size * (float)0.85) + 2; if (psbuffs[IdxPrim][0]) { PUT3(" %.1f %.1f moveto (%s) cshow ", xPrim, y, psbuffs[IdxPrim]); } if (psbuffs[IdxAlt][0]) { PUT3(" %.1f %.1f moveto (%s) cshow ", xAlt, y, psbuffs[IdxAlt]); } } /* ...if symbol is note or rest. */ } /* ...for each symbol on this line. */ } /* ----- draw_symbols: draw symbols at proper positions on staff ----- */ static void draw_symbols(int voice) { int i,inbeam,nwl; float x,y,xl,d,w,gchy; struct BEAM bm; struct SYMBOL *s; inbeam=0; gchy = 38; for (i = 0, s = sym; i < nsym; i++, s++) { /* draw the symbols */ if (nbuf+100>BUFFSZ) rx("PS output exceeds reserved space per staff", " -- increase BUFFSZ1"); x = s->x; /* [JSA] Modification for voice muting. */ /* Only allow bar symbols for non-printed voices. */ /* This is required because bars are only in the */ /* first voice. If the first voice is muted the */ /* bars would disappear if we don't call this */ /* function. */ if (VoiceMute_tb[voice] && (s->type != BAR) && (s->type != TIMESIG)) continue; switch (s->type) { case NOTE: nnote++; xl=0; if (i>0) xl=s[-1].x; d=x-xl; w=s->shrink; if (s->word_st && !s->word_end) { if (calculate_beam(i, &bm)) inbeam=1; } draw_note(x, w, d, s, inbeam == 0, voice); if (inbeam && i == bm.i2) { inbeam=0; draw_beams(&bm); } break; case REST: nnote++; y=s->y; if (inbeam) y = rest_under_beam(s->x,s->head,&bm); draw_rest(x, y, s, voice); break; case BAR: update_endings(s); if (voice != 0) break; draw_bar(x, s); nbar++; if (!s->eoln && measure_nb > 0 && nbar % measure_nb == 0) { set_font(cfmt.composerfont, 0); PUT3("%.1f %.1f M (%d) show\n", x, staff_tb[0].y + +24. + 6., nbar); } break; case CLEF: { int staff; float staffb; char ct = 't'; /* clef type - def: treble */ staff = s->staff; if (voice_tb[voice].second) continue; /* only one clef per staff */ staff_tb[staff].clef = s->u; /* (for next lines) */ staffb = staff_tb[staff].y; switch (s->u) { case BASS: ct = 'b'; break; case ALTO: ct = 'c'; break; default: bug("unknown clef type", 0); case TREBLE: break; } PUT4("%.1f %.1f %c%cclef\n", x, staffb, s->v ? 's' : ' ', ct); break; } case TIMESIG: if (voice != 0) break; draw_timesig(x, s); break; case KEYSIG: draw_keysig(voice, x, s); break; case INVISIBLE: case COMBINEDREST: break; case COMBINEDNOTE: /* The only parameters we're actually using on this */ /* call are s and voice. */ draw_note(x, w, d, s, inbeam == 0, voice); break; default: printf(">>> cannot draw symbol type %d\n", s->type); } } if (!VoiceMute_tb[voice]) draw_all_slurs(); if (!VoiceMute_tb[voice]) draw_nplet_brackets(); if (!VoiceMute_tb[voice]) draw_all_phrasings(); for (i = 0, s = sym; i < nsym; i++, s++) { /* draw the far decorations */ if (!VoiceMute_tb[voice] && !cfmt.NoDecos[voice] && ((s->type == NOTE) || (s->type == REST) || (s->type == BAR))) { draw_decorations(s, voice, FALSE); /* add decorations */ if (s->yhi > gchy) gchy = s->yhi; } } /* draw guitar chords */ gchy += staff_tb[(int)voice_tb[voice].staff].y; DrawGuitarChords(voice, gchy); /* [JSA] Modification for voice muting. */ /* Only allow bar symbols and guitar chords for */ /* non-printed voices. */ /* This is required because bars are only in the */ /* first voice. If the first voice is muted the */ /* bars would disappear if we don't call this */ /* function. */ if (VoiceMute_tb[voice]) return; /* This turns the endings brackets off */ /* when we turn off other decorations. */ /* [JSA] */ if (!cfmt.NoDecos[voice]) draw_endings(); nwl = staff_tb[(int) sym[0].staff].nvocal; if ((nwl > 0) && !cfmt.musiconly) draw_vocals(nwl); } /* ----- any_symbol: check if any "real" symbol in the piece ---- */ static int any_symbol(void) { int voice, i; struct SYMBOL *s; for (voice = 0; voice <= nvoice; voice++) { for (i = voice_tb[voice].nsym, s = &voice_tb[voice].sym[voice_tb[voice].nsym0]; --i >= 0; s++) { switch (s->type) { case NOTE: case REST: case BAR: return 1; } } } return 0; } /* ----- check_keysigs: delete duplicate keysigs at staff start ---- */ static void check_keysigs(void) { int i,klast,t; int voice; struct SYMBOL *sym; int nsym; for (voice = 0; voice <= nvoice; voice++) { sym = &voice_tb[voice].sym[voice_tb[voice].nsym0]; nsym = voice_tb[voice].nsym; klast=-1; for (i=0;ieoln) break; } } else { /* count the measures */ /* This first loop skips to the next note or rest. */ /* TODO: We MIGHT need to add COMBINEDNOTE and COMBINEDREST */ for (ig = ig_st; ig < ngsym; ig++) { if ((gsym[ig].s->type == NOTE) || (gsym[ig].s->type == REST)) break; } /* Now count bars */ for (; ig < ngsym; ig++) { struct SYMBOL *s; /* Use only the symbols from the first voice */ /* when counting measures. */ if (gsym[ig].voice) continue; s = gsym[ig].s; /* Don't count symbols that aren't bars or are invisible. */ if ((s->type != BAR) || (s->u == B_INVIS)) continue; /* Don't count double bars. */ if ((gsym[ig].is > 0) && (voice_tb[gsym[ig].voice].sym[gsym[ig].is - 1].type == BAR)) continue; /* We have a bar, count it by decrementing the */ /* temporary copy of cfmt.barsperstaff */ number--; if (number <= 0) break; } } /* cut at the last symbol of the sequence */ seq = gsym[ig].seq; while (ig < ngsym && gsym[ig].seq == seq) ig++; ig_end = ig; set_piece(); for (i = nstaff + 1; --i >= 0;) { staff_tb[i].toppos = 0; staff_tb[i].botpos = 0; } } /* ----- cut_symbols: cut out symbols up to number ------ */ static void cut_symbols(void) { int voice; int head_nb; int ig, j; int t; t = gsym[ig_end].time; head_nb = 2; /* clef & keysig */ /* cannot have time signature */ if (insert_btype) head_nb++; /* bar */ ig_st = ig_end - head_nb * (nvoice + 1); ig_end = ngsym; for (voice = 0; voice <= nvoice; voice++) { voice_tb[voice].nsym0 += voice_tb[voice].nsym - head_nb; sym = &voice_tb[voice].sym[voice_tb[voice].nsym0]; nsym = 0; init_music_line(voice); } ig = ig_st; for (j = 0; j < head_nb; j++) { for (voice = 0; voice <= nvoice; voice++) { gsym[ig].s = &voice_tb[voice].sym[voice_tb[voice].nsym0 + j]; gsym[ig].is = voice_tb[voice].nsym0 + j; gsym[ig].voice = voice; gsym[ig].seq = j; gsym[ig].time = t; ig++; } } mes1 = mes2 = 0; } /* ----- AdjustAccidentals: ----- */ /* [JSA] Added 10/99 corrects printing of accidentals. */ /* Causes a printed accidental to apply across all voices in */ /* a staff, and to every instance of a note, and to */ /* persist to the end of the measure (i.e. the way standard */ /* music notation is usually written). */ /* Called from "output_music()" after note spacing has been */ /* finalized and bars have been adjusted. */ void AdjustAccidentals() { int staff, voice, i, j, m; int HaveBar; int nsym[MAXVOICE], isym[MAXVOICE]; float LeftX, HitX, x[MAXVOICE]; struct SYMBOL *s[MAXVOICE], *Sym; // Each staff is handled individually. for (staff = 0; staff <= nstaff; staff++) { // Any voice might be assigned to any staff. for (voice = 0; voice <= nvoice; voice++) { nsym[voice] = voice_tb[voice].nsym; s[voice] = voice_tb[voice].sym; s[voice] += voice_tb[voice].nsym0; isym[voice] = 0; } // Start at the left edge. LeftX = 0; // Loop until entire line is done. do { // Find the next note or bar event for each voice. for (voice = 0; voice <= nvoice; voice++) { // Only consider voices assigned to this staff. if (voice_tb[voice].staff != staff) continue; // Ignore voices that are exhausted. if (isym[voice] >= nsym[voice]) continue; // Find the next bar or note event for this voice. while ((isym[voice] < nsym[voice]) && (s[voice]->type != BAR) && ((s[voice]->type != NOTE) || VoiceMute_tb[voice])) { isym[voice]++; s[voice]++; } if (isym[voice] < nsym[voice]) { x[voice] = s[voice]->x; } else { x[voice] = (float)999999999.0; } } // Find the left most unprocessed position on this staff. HitX = (float)999999998.0; for (voice = 0; voice <= nvoice; voice++) { // Only consider voices assigned to this staff. if (voice_tb[voice].staff != staff) continue; // Ignore voices that are exhausted. if (isym[voice] >= nsym[voice]) continue; // Get leftmost position not already processed. if ((x[voice] > LeftX) && (x[voice] < HitX)) HitX = x[voice]; } if (HitX >= (float)999999990.0) continue; // We're done with this staff. // Now process all applicable symbols at this postition. // Realistically we should never encounter both notes and bars at the // same x location. Nonetheless, we'll delay the bar action until // after any possible notes have been processed, just in case. HaveBar = FALSE; for (voice = 0; voice <= nvoice; voice++) { // Only consider voices assigned to this staff and skip muted voices. if ((voice_tb[voice].staff != staff) || VoiceMute_tb[voice]) continue; // Ignore voices that are exhausted. if (isym[voice] >= nsym[voice]) continue; // Only consider symbols at the current location. if (x[voice] != HitX) continue; Sym = s[voice]; isym[voice]++; if (isym[voice] < nsym[voice]) s[voice]++; if (Sym->type == BAR) HaveBar = TRUE; if (Sym->type == NOTE) { // Process this note (we know it's not muted because of the decision above). for (m = 0; m < Sym->npitch; m++) { // Check for "helper" accidental if first occurence in bar. if (cfmt.HelperAccs && !Sym->accs[m] && DispAccs2[staff][Sym->pits[m] % 36]) { // Place a "helper" accidental. // NOTE, This is going to puke anywhere we have a keychange. switch(key_convert[sharps_flats + 7][(Sym->pits[m] - 2) % 7]) { case -1: // A flat in the key sig. Sym->accs[m] = A_FT; break; case 1: // A sharp in the key sig. Sym->accs[m] = A_SH; break; default: Sym->accs[m] = A_NT; } // We don't want the helper if the accidental in the previous // measure was returning to the key signature! if (Sym->accs[m] == DispAccs2[staff][Sym->pits[m] % 36]) { Sym->accs[m] = 0; } } else { if (Sym->accs[m] == DispAccs[staff][Sym->pits[m] % 36]) { // Eliminate accidental (if any) from note. Sym->accs[m] = 0; } else { // Adjust the accidental and the running table. if (Sym->accs[m]) { // Display the accidental and put it in the running table DispAccs[staff][Sym->pits[m] % 36] = Sym->accs[m]; } else { // We are going from an accidental back to the key signature. // NOTE, This is going to puke anywhere we have a keychange // and accidentals together! switch(key_convert[sharps_flats + 7][(Sym->pits[m] - 2) % 7]) { case -1: // A flat in the key sig. Sym->accs[m] = A_FT; break; case 1: // A sharp in the key sig. Sym->accs[m] = A_SH; break; default: Sym->accs[m] = A_NT; } DispAccs[staff][Sym->pits[m] % 36] = 0; } } } // Clear this pitch in the previous measure table. DispAccs2[staff][Sym->pits[m] % 36] = 0; } // end for m (noteheads) } } if (HaveBar) { // We had a bar, update the running tables for the measure boundary. for (i = 0; i <= nstaff; i++) { for (j = 0; j < 36; j++) { DispAccs2[i][j] = DispAccs[i][j]; DispAccs[i][j] = 0; } } } // Update record of positions already processed by moving the left edge // to the right. LeftX = HitX; } while (HitX < (float)999999990.0); } } /* ----- combine voices into chords on a note-by-note basis ----- */ /* [JSA] Added 10/19/99 */ /* This is to pretty up the output while still permitting split */ /* rythms. */ void CombineVoices() { int staff, voice, voice2; int i, j, m; struct SYMBOL *s, *s2; float x; /* Set a temporary and fake x value for ordering the notes */ /* based solely on the duration of notes and rests. */ for (voice = 0; voice <= nvoice; voice++) { s = voice_tb[voice].sym; x = 0; for (i = 0; i < voice_tb[voice].nsym; i++, s++) { if ((s->type == NOTE) || (s->type == REST)) { s->x = x; x += s->len; } } } for (staff = 0; staff <= nstaff; staff++) { /* Find first voice on staff. */ voice = 0; while ((voice <= nvoice) && ((voice_tb[voice].staff != staff) || VoiceMute_tb[voice])) voice++; if (voice_tb[voice].staff != staff) continue; /* Now that we have the first voice, compare all other */ /* staff voices against each note in it. */ s = voice_tb[voice].sym; for (i = 0; i <= voice_tb[voice].nsym; i++, s++) { if (s->type == NOTE) { /* We combine a note into a chord if the note */ /* is at the same x location and of the same */ /* duration. */ for (voice2 = voice + 1; voice2 <= nvoice; voice2++) { if ((voice_tb[voice2].staff != staff) || (VoiceMute_tb[voice2])) continue; s2 = voice_tb[voice2].sym; for (j = 0; j <= voice_tb[voice2].nsym; j++, s2++) { if ((s2->type == NOTE) && (s2->x == s->x) && (s2->len == s->len) && (s2->in_beam == s->in_beam) && ((s2->npitch + s->npitch) < MAXHD)) { /* these are both notes of same duration at same x location. */ for (m = 0; m < s2->npitch; m++) { s->accs[(int)s->npitch] = s2->accs[m]; s->pits[(int)s->npitch] = s2->pits[m]; s->lens[(int)s->npitch] = s2->lens[m]; s->abspitch[(int)s->npitch] = s2->abspitch[m]; s->TabDeco[(int)s->npitch] = s2->TabDeco[m]; s->shhd[(int)s->npitch] = s2->shhd[m]; s->shac[(int)s->npitch] = s2->shac[m]; s->npitch++; } if (s2->EndTie) s->EndTie = s2->EndTie; s2->type = COMBINEDNOTE; s2->lnk = s; } } } } else if (s->type == REST) { /* We eliminate second rests if they are at the */ /* same location and of the same duration. */ for (voice2 = voice + 1; voice2 <= nvoice; voice2++) { if (voice_tb[voice2].staff != staff) continue; s2 = voice_tb[voice2].sym; for (j = 0; j <= voice_tb[voice2].nsym; j++, s2++) { if ((s2->type == REST) && (s2->x == s->x) && (s2->len == s->len)) { /* these are both rests of same duration at same x location. */ s2->type = COMBINEDREST; } } } } } } } /* ----- SetUnisons -----*/ /* This function is called after voices are recombined and note spacing is set. */ /* It sets a flag if there is a chord with a unison in it. */ static void SetUnisons() { int voice; int i, m, n; struct SYMBOL *s; for (voice = 0; voice <= nvoice; voice++) { s = voice_tb[voice].sym; for (i = 0; i < voice_tb[voice].nsym; i++, s++) { if (s->type == NOTE) { s->Unison = FALSE; for (m = 0; m < s->npitch; m++) { for (n = m + 1; n < s->npitch; n++) { if (s->pits[n] == s->pits[m]) { s->Unison = TRUE; break; } } } } } } } /* ----- PreviewSymbols -----*/ /* This function previews deocorations to more accurately */ /* determine the height needed for each staff. */ static void PreviewSymbols(int voice) { int i; struct SYMBOL *s; float slo, shi, stop, sbot; int sChdIdx, sHdTxtIdx, sTxtIdx, staff; if (VoiceMute_tb[voice]) return; /* Save FIFO indexes */ sChdIdx = voice_tb[voice].Chords.outidx; sHdTxtIdx = voice_tb[voice].TextHdDecs.outidx; sTxtIdx = voice_tb[voice].TextDecs.outidx; for (i = 0, s = sym; i < nsym; i++, s++) { /* Save symbol limits */ slo = s->ylo; shi = s->yhi; stop = s->dc.top; sbot = s->dc.bot; if ((s->type == NOTE) || (s->type == REST)) { /* Preview decorations, this will adjust symbol limits. */ DrawCloseDecorations(s->x, s, voice, TRUE); if (!cfmt.NoDecos[voice]) draw_decorations(s, voice, TRUE); /* Use previewed limits to extend staff dims. */ staff = voice_tb[voice].staff; if (staff_tb[staff].toppos < (s->yhi + 1. - STAFFHEIGHT)) { staff_tb[staff].toppos = s->yhi + (float)1. - STAFFHEIGHT; } if (staff_tb[staff].botpos > (s->ylo - 1.)) { staff_tb[staff].botpos = s->ylo - (float)1.; } } /* Restore original symbol limits */ s->ylo = slo; s->yhi = shi; s->dc.top = stop; s->dc.bot = sbot; } /* Restore FIFO indexes */ voice_tb[voice].Chords.outidx = sChdIdx; voice_tb[voice].TextHdDecs.outidx = sHdTxtIdx; voice_tb[voice].TextDecs.outidx = sTxtIdx; } /* ----- output_music: output for parsed symbol list ----- */ void output_music(FILE *fp) { float realwidth; float staffheight; float lscale,lwidth; int i; int voice; int j, p, SkipStaff; /* [JSA] */ float y; /* [JSA] */ const char TabPitchTable[24][4] = { /* [JSA] */ "C\0", "C#\0", "D\0", "Eb\0", "E\0", "F\0", "F#\0", "G\0", "Ab\0", "A\0", "Bb\0", "B\0", "c\0", "c#\0", "d\0", "eb\0", "e\0", "f\0", "f#\0", "g\0", "ab\0", "a\0", "bb\0", "b\0" }; char CapoMsg[8]; /* [JSA] */ voice_tb[current_voice].nsym = nsym; if (verbose > 8) { for (voice = 0; voice <= nvoice; voice++) { sym = voice_tb[voice].sym; nsym = voice_tb[voice].nsym; printf("output_music: voice %d '%s' nsym=%d\n", voice, voice_tb[voice].name, nsym); if (verbose > 9) print_syms(nsym); } } if (!file_initialized && !epsf) { init_ps(fout,infostr,0,0.0,0.0,0.0,0.0); init_page(fout); } if (nsym == 0) return; mline = 0; alfa_last = (float)0.1; beta_last = 0.0; lwidth = cfmt.staffwidth; lscale = cfmt.scale; check_margin(cfmt.leftmargin); /* dump buffer if not enough space for a staff line */ check_buffer(fp, BUFFSZ1); set_multi(); /* set global characteristics */ /* [JSA] Combine voices into chords when possible. */ if (cfmt.CombineVoices) CombineVoices(); /* [JSA] This is the earliest we can check for unisons, do so. */ SetUnisons(); for (voice = 0; voice <= nvoice; voice++) { voice_tb[voice].nsym0 = 0; /* [JSA] for voice muting. */ if (!VoiceMute_tb[voice]) { set_sym_chars(voice); /* set symbol characteristics */ set_beams(voice); } } /* set all symbols */ ig_st = 0; ig_end = ngsym; for (;;) { /* loop over pieces of line for output */ mline++; find_piece(); if (any_symbol()) { check_keysigs(); if (verbose>9) printf("line %d, nvoice %d nstaff %d\n", mline, nvoice, nstaff); for (voice = 0; voice <= nvoice; voice++) { sym = &voice_tb[voice].sym[voice_tb[voice].nsym0]; nsym = voice_tb[voice].nsym; set_stems(); /* Preview symbols and adjust staff top and bottom. */ PreviewSymbols(voice); set_sym_widths(); } set_overlap(); set_sym_glue(lwidth/lscale,&realwidth); staffheight = set_staff(); PUT1("\n%% --- line %d\n", mline); bskip(lscale * staffheight); PUT3("gsave %.3f %.3f scale %.2f setlinewidth\n", lscale, lscale, BASEWIDTH); /********************************************************/ /* Modification to show whistle pitch with tab 05/00 */ /* [JSA] */ /********************************************************/ for (i = nWhistleTab - 1; i >= 0; i--) { // Skip tab if its voice is muted. if (VoiceMute_tb[WhistleTab_tb[i].voice]) continue; PUT0("/Helvetica 8.0 selectfont\n"); PUT2("0 %.1f moveto 90 rotate (%s) show -90 rotate\n", WhistleTab_tb[i].y + (float)8.0, "WHISTLE"); PUT0("/Helvetica-Bold 36.0 selectfont\n"); PUT2("0 %.1f moveto (%s) show\n", WhistleTab_tb[i].y + (float)8.0, TabPitchTable[WhistleTab_tb[i].keypitch % 12]); } /********************************************************/ /* Modification for string tablature 11/99 [JSA] */ /********************************************************/ for (i = nStringTab; i >= 0; i--) { /* Skip tab if its voice is muted. */ if (!VoiceMute_tb[StringTab_tb[i].voice]) { /* Draw strings and string pitches. */ for (j = 0; j < StringTab_tb[i].nstrings; j++) { y = StringTab_tb[i].y + (((StringTab_tb[i].nstrings - 1) - j) * (cfmt.StrTabFont.size * cfmt.StrTabSpace)); p = StringTab_tb[i].pitches[j]; while (p < 24) p += 12; while (p > 47) p -= 12; PUT2("/%s %f selectfont\n", cfmt.StrTabFont.name, cfmt.StrTabFont.size); PUT3("%.1f %.1f moveto (%s) show\n", (float)35.0, y + (float)1.5, TabPitchTable[p - 24]); PUT2("35 %.2f moveto %.2f 0 rlineto\n", y, realwidth - 35); switch(j) { case 1: PUT2("/%s %f selectfont\n", "Helvetica", (float)15.0); if (StringTab_tb[i].fretting == TABCHROMATIC) { PUT3("%.1f %.1f moveto (%s) show\n", (float)0.1, y + (float)1.5, "CHR"); } else { PUT3("%.1f %.1f moveto (%s) show\n", (float)0.1, y + (float)1.5, "DUL"); } break; case 2: if (StringTab_tb[i].capo) { PUT2("/%s %f selectfont\n", "Times-Roman", (float)9.0); sprintf(CapoMsg, "Capo %d", StringTab_tb[i].capo); PUT3("%.1f %.1f moveto (%s) show\n", (float)0.1, y + (float)1.5, CapoMsg); } break; default: y = y; } } } } /********************************************************/ /* End modification for string tablature 11/99 [JSA] */ /********************************************************/ for (i = nstaff; i >= 0; i--) { /* Skip staff if all of its voices are muted. [JSA] */ SkipStaff = TRUE; for (j = 0; j <= nvoice; j++) { if ((voice_tb[j].staff == i) && !VoiceMute_tb[j]) { SkipStaff = FALSE; break; } } if (SkipStaff) continue; PUT2("0 %.2f moveto %.2f staff\n", staff_tb[i].y, realwidth); } if (nstaff > 0) draw_left(); for (voice = 0; voice <= nvoice; voice++) { sym = &voice_tb[voice].sym[voice_tb[voice].nsym0]; nsym = voice_tb[voice].nsym; check_bars(); } /* [JSA] Correction 10/99 establishes standard treatment */ /* of accidentals (i.e. accidentals apply across all voices in */ /* a staff and persist to the end of the measure). */ if (cfmt.CarryAccs) AdjustAccidentals(); for (voice = 0; voice <= nvoice; voice++) { sym = &voice_tb[voice].sym[voice_tb[voice].nsym0]; nsym = voice_tb[voice].nsym; draw_symbols(voice); if (verbose>8) printf("... line %d voice %d '%s', wrote %d symbols of %d\n\n", mline, voice, voice_tb[voice].name, nsym, voice_tb[voice].nsym0 + nsym); } PUT0(" grestore\n"); /* bskip(0.5 * cfmt.staffsep * lscale);*/ buffer_eob(fp); } if (ig_end >= ngsym) break; cut_symbols(); } if (gsym != 0) { free(gsym); gsym = 0; } voice_tb[0].nsym = nsym = 0; sym = voice_tb[0].sym; nvoice = 0; voice_tb[0].name = ""; nstaff = 0; memset(&staff_tb[0], 0, sizeof staff_tb[0]); current_voice = 0; } /* ----- process_textblock ----- */ static void process_textblock(FILE *fpin, FILE *fp, int job) { char w1[81],ln[BSIZE],ln1[BSIZE]; float lwidth,baseskip,parskip; int i,ll,add_final_nl; baseskip = cfmt.textfont.size * cfmt.lineskipfac; parskip = cfmt.textfont.size * cfmt.parskipfac; add_final_nl=0; if (job==OBEYLINES) add_final_nl=1; lwidth=cfmt.staffwidth; output_music(fp); buffer_eob(fp); set_font(cfmt.textfont, 0); ntxt=0; for (i=0;i<100;i++) { if (feof(fpin)) rx("EOF reached scanning text block",""); fgets(ln, BSIZE, fpin); ll=strlen(ln); linenum++; if (ln[ll-1]=='\n') ln[ll-1]='\0'; if ((verbose>=5) || (vb>=10)) printf("%3d %s\n", linenum, ln); if ((ln[0]=='%') && (ln[1]=='%')) { strcpy(ln1,ln+2); strcpy(ln,ln1); } w1[0] = '\0'; sscanf(ln,"%s",w1); if (!strcmp(w1,"endtext")) break; if (job!=SKIP) { if (isblankstr(ln)) { write_text_block(fp,job); ntxt=0; } else add_to_text_block(ln,add_final_nl); } } if (job!=SKIP) write_text_block(fp,job); } /* ----- process_pscomment ----- */ void process_pscomment(FILE *fpin, FILE *fp, char line[]) { char w[81],unum1[41]; float h1,len,lwidth; int i,nch; if (verbose >= 99) { printf("process_pscomment(fpin=%p, fp=%p, line=%s)\n", fpin, fp, line); printf(" within_block=%d, do_this_tune=%d\n", within_block, do_this_tune); fflush(stdout); } lwidth = cfmt.staffwidth; line[0]=' '; line[1]=' '; for (i=0;i<(int)strlen(line);i++) if (line[i]=='%') line[i]='\0'; w[0] = '\0'; sscanf(line,"%s%n", w, &nch); if (!strcmp(w,"begintext")) { int job; char fstr[81]; if (epsf && !within_block) return; fstr[0] = '\0'; sscanf(line, "%*s %s", fstr); if (isblankstr(fstr)) strcpy(fstr,"obeylines"); if (!strcmp(fstr,"obeylines")) job=OBEYLINES; else if (!strcmp(fstr,"align")) job=ALIGN; else if (!strcmp(fstr,"skip")) job=SKIP; else if (!strcmp(fstr,"ragged")) job=RAGGED; else rx("bad argument for begintext: ",fstr); if (within_block && !do_this_tune) job=SKIP; process_textblock(fpin,fp,job); return; } if (!strcmp(w,"text") || !strcmp(w,"center")) { if (epsf && !within_block) return; if (within_block && !do_this_tune) return; output_music(fp); set_font(cfmt.textfont, 0); ntxt=0; add_to_text_block(line+nch+1,1); if (!strcmp(w,"text")) write_text_block(fp,OBEYLINES); else write_text_block(fp,OBEYCENTER); buffer_eob(fp); } else if (!strcmp(w,"sep")) { char unum2[41],unum3[41]; float h2; if (within_block && !do_this_tune) return; output_music(fp); unum1[0] = '\0'; unum2[0] = '\0'; unum3[0] = '\0'; sscanf(line,"%*s %s %s %s", unum1,unum2,unum3); g_unum(unum1,0,&h1); g_unum(unum2,0,&h2); g_unum(unum3,0,&len); if (h1*h1<0.00001) h1=(float)0.5*CM; if (h2*h2<0.00001) h2=h1; if (len*len<0.0001) len=(float)3.0*CM; bskip(h1); PUT2("%.1f %.1f sep0\n", lwidth/2-len/2, lwidth/2+len/2); bskip(h2); buffer_eob(fp); } else if (!strcmp(w,"vskip")) { if (within_block && !do_this_tune) return; output_music(fp); unum1[0] = '\0'; sscanf(line,"%*s %s", unum1); g_unum(unum1,0,&h1); if (h1*h1<0.00001) h1=(float)0.5*CM; bskip(h1); buffer_eob(fp); } else if (!strcmp(w,"newpage")) { if (epsf || (within_block && !do_this_tune)) return; output_music(fp); write_buffer(fp); use_buffer=0; write_pagebreak(fp); } else if (!strcmp(w,"staves")) { if (within_block && !do_this_tune) return; get_staves(line+nch+1); } else if (!strcmp(w, "jsastem=dn")) { if (within_block && !do_this_tune) return; voice_tb[current_voice].StemDir = -1; } else if (!strcmp(w, "jsastem=up")) { if (within_block && !do_this_tune) return; voice_tb[current_voice].StemDir = 1; } else if (!strcmp(w, "jsastem=def")) { if (within_block && !do_this_tune) return; voice_tb[current_voice].StemDir = 0; } else { if (!dfmt.NoInlineFmt) { if (within_block) { interpret_format_line(line,&cfmt); ops_into_fmt(&cfmt); } else { interpret_format_line(line,&dfmt); ops_into_fmt(&dfmt); cfmt=dfmt; } } } } /* ----- reset_gen: reset the generator ----- */ void reset_gen(void) { nnote = 0; num_ending = 0; mes1 = mes2 = 0; }