/* * yaps - program to convert abc files to PostScript. * Copyright (C) 1999 James Allwright * e-mail: J.R.Allwright@westminster.ac.uk * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /* drawtune.c */ /* This file contains routines for generating final PostScript Output */ /* There are 2 stages to this process. First the symbol positions are */ /* calculated, then the PostScript symbols are generated. */ #include #ifndef PCCFIX #include #include #endif #include "abc.h" #include "structs.h" #include "sizes.h" extern struct tune thetune; extern int debugging; extern int pagenumbering; /* external functions */ struct key* newkey(char* name, int sharps, char accidental[], int mult[]); char* addstring(); extern int lineno; extern int separate_voices; FILE* f; struct feature* beamset[64]; struct feature* gracebeamset[32]; int beamctr, gracebeamctr;; int rootstem; int fontsize, fontnum; int donemeter; int ingrace, inchord; int chordcount; struct feature* chordhead; float scale, descend, totlen, oldplace; float yend, yinstruct, ygchord, ywords; int pagecount = 1; int pagelen, pagewidth, xmargin, ymargin; float scaledlen, scaledwidth; enum placetype {left, right, centre}; struct font textfont; struct font titlefont; struct font subtitlefont; struct font wordsfont; struct font composerfont; struct font vocalfont; struct font gchordfont; struct font partsfont; /* arrays giving character widths for characters 32-255 */ /* font for lyrics : 13 point Times-Bold */ float timesbold_width[224] = { 3.249, 4.328, 7.214, 6.499, 6.499, 12.999, 10.828, 4.328, 4.328, 4.328, 6.499, 7.409, 3.249, 7.409, 3.249, 3.613, 6.499, 6.499, 6.499, 6.499, 6.499, 6.499, 6.499, 6.499, 6.499, 6.499, 4.328, 4.328, 7.409, 7.409, 7.409, 6.499, 12.089, 9.385, 8.67, 9.385, 9.385, 8.67, 7.942, 10.113, 10.113, 5.056, 6.499, 10.113, 8.67, 12.271, 9.385, 10.113, 7.942, 10.113, 9.385, 7.227, 8.67, 9.385, 9.385, 12.999, 9.385, 9.385, 8.67, 4.328, 3.613, 4.328, 7.552, 6.499, 4.328, 6.499, 7.227, 5.771, 7.227, 5.771, 4.328, 6.499, 7.227, 3.613, 4.328, 7.227, 3.613, 10.828, 7.227, 6.499, 7.227, 7.227, 5.771, 5.056, 4.328, 7.227, 6.499, 9.385, 6.499, 6.499, 5.771, 5.121, 2.859, 5.121, 6.759, 3.249, 3.249, 3.249, 3.249, 3.249, 3.249, 3.249, 3.249, 3.249, 3.249, 3.249, 3.249, 3.249, 3.249, 3.249, 3.249, 3.249, 3.613, 4.328, 4.328, 4.328, 4.328, 4.328, 4.328, 4.328, 4.328, 3.249, 4.328, 4.328, 3.249, 4.328, 4.328, 4.328, 3.249, 4.328, 6.499, 6.499, 6.499, 6.499, 2.859, 6.499, 4.328, 9.71, 3.899, 6.499, 7.409, 4.328, 9.71, 4.328, 5.199, 7.409, 3.899, 3.899, 4.328, 7.227, 7.019, 3.249, 4.328, 3.899, 4.289, 6.499, 9.749, 9.749, 9.749, 6.499, 9.385, 9.385, 9.385, 9.385, 9.385, 9.385, 12.999, 9.385, 8.67, 8.67, 8.67, 8.67, 5.056, 5.056, 5.056, 5.056, 9.385, 9.385, 10.113, 10.113, 10.113, 10.113, 10.113, 7.409, 10.113, 9.385, 9.385, 9.385, 9.385, 9.385, 7.942, 7.227, 6.499, 6.499, 6.499, 6.499, 6.499, 6.499, 9.385, 5.771, 5.771, 5.771, 5.771, 5.771, 3.613, 3.613, 3.613, 3.613, 6.499, 7.227, 6.499, 6.499, 6.499, 6.499, 6.499, 7.409, 6.499, 7.227, 7.227, 7.227, 7.227, 6.499, 7.227, 6.499, }; /* font for chord names : 12 point Helvetica */ float helvetica_width[224] = { 3.335, 3.335, 4.259, 6.671, 6.671, 10.667, 8.003, 2.651, 3.995, 3.995, 4.667, 7.007, 3.335, 3.995, 3.335, 3.335, 6.671, 6.671, 6.671, 6.671, 6.671, 6.671, 6.671, 6.671, 6.671, 6.671, 3.335, 3.335, 7.007, 7.007, 7.007, 6.671, 12.179, 8.003, 8.003, 8.663, 8.663, 8.003, 7.331, 9.335, 8.663, 3.335, 5.999, 8.003, 6.671, 9.995, 8.663, 9.335, 8.003, 9.335, 8.663, 8.003, 7.331, 8.663, 8.003, 11.327, 8.003, 8.003, 7.331, 3.335, 3.335, 3.335, 5.627, 6.671, 2.663, 6.671, 6.671, 5.999, 6.671, 6.671, 3.335, 6.671, 6.671, 2.663, 2.663, 5.999, 2.663, 9.995, 6.671, 6.671, 6.671, 6.671, 3.995, 5.999, 3.335, 6.671, 5.999, 8.663, 5.999, 5.999, 5.999, 4.007, 3.119, 4.007, 7.007, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.995, 6.671, 6.671, 2.003, 6.671, 6.671, 6.671, 6.671, 2.291, 3.995, 6.671, 3.995, 3.995, 5.999, 5.999, 3.335, 6.671, 6.671, 6.671, 3.335, 3.335, 6.443, 4.199, 2.663, 3.995, 3.995, 6.671, 11.999, 11.999, 3.335, 7.331, 3.335, 3.995, 3.995, 3.995, 3.995, 3.995, 3.995, 3.995, 3.995, 3.335, 3.995, 3.995, 3.335, 3.995, 3.995, 3.995, 11.999, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 11.999, 3.335, 4.439, 3.335, 3.335, 3.335, 3.335, 6.671, 9.335, 11.999, 4.379, 3.335, 3.335, 3.335, 3.335, 3.335, 10.667, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 2.663, 7.331, 11.327, 7.331, 3.335, 3.335, 3.335, 3.335, }; int ISOencoding(char ch1, char ch2) /* converts the 2 characters following '\' into an ISO Latin 1 Font */ /* character code in the range 128-255 */ { int code; code = 0; switch (ch1) { case '`': switch(ch2) { case 'A': code = 0300; break; case 'E': code = 0310; break; case 'I': code = 0314; break; case 'O': code = 0322; break; case 'U': code = 0331; break; case 'a': code = 0340; break; case 'e': code = 0350; break; case 'i': code = 0354; break; case 'o': code = 0362; break; case 'u': code = 0371; break; default: break; }; break; case '\'': switch(ch2) { case 'A': code = 0301; break; case 'E': code = 0311; break; case 'I': code = 0315; break; case 'U': code = 0332; break; case 'Y': code = 0335; break; case 'a': code = 0341; break; case 'e': code = 0351; break; case 'i': code = 0355; break; case 'o': code = 0363; break; case 'u': code = 0372; break; case 'y': code = 0375; break; default: break; }; break; case '^': switch(ch2) { case 'A': code = 0302; break; case 'E': code = 0312; break; case 'I': code = 0316; break; case 'O': code = 0324; break; case 'U': code = 0333; break; case 'a': code = 0342; break; case 'e': code = 0352; break; case 'i': code = 0356; break; case 'o': code = 0364; break; case 'u': code = 0373; break; default: break; }; break; case '"': switch(ch2) { case 'A': code = 0304; break; case 'E': code = 0313; break; case 'I': code = 0317; break; case 'O': code = 0326; break; case 'U': code = 0334; break; case 'a': code = 0344; break; case 'e': code = 0353; break; case 'i': code = 0357; break; case 'o': code = 0366; break; case 'u': code = 0374; break; case 'y': code = 0377; break; default: break; }; break; case '~': switch(ch2) { case 'A': code = 0303; break; case 'N': code = 0321; break; case 'O': code = 0325; break; case 'a': code = 0343; break; case 'n': code = 0361; break; case 'o': code = 0365; break; default: break; }; break; case 'c': switch(ch2) { case 'C': code = 0307; break; case 'c': code = 0347; break; default: break; }; break; case 'o': switch(ch2) { case 'A': code = 0305; break; case 'a': code = 0345; break; default: break; }; break; case 'A': if (ch2=='A') { code = 0305; }; if (ch2=='E') { code = 0305; }; break; case 'a': if (ch2=='a') { code = 0345; }; if (ch2=='e') { code = 0346; }; break; case 's': if (ch2=='s') { code = 0337; }; break; default: break; }; return(code); } int getISO(char s[], int j, int* code) /* convert input into 8-bit character code */ /* s[] is input string, j is place in string */ /* returns new place in string and writes 8-bit value to code */ { int i; int count; i =j; if (s[i]=='\\') { /* look for 2 character code */ *code = ISOencoding(s[i+1], s[i+2]); if (*code != 0) { i = i+3; } else { /* look for octal code */ i = i+1; *code = 0; count = 0; while ((s[i]>='0')&&(s[i]<='7')&&(count<3)) { *code = ((*code)*8+(int)s[i]-'0')%256; count = count+1; i = i+1; }; if (*code == 0) { *code = '\\'; }; }; } else { *code = (int)s[i] & 0xFF; i = i + 1; }; return(i); } ISOdecode(char s[], char out[]) /* convert coded characters to straight 8-bit ascii */ { int i, j, len; int code; len = strlen(s); i = 0; j = 0; while (i 31) && (ch < 128)) { fprintf(f, "%c", s[i]); } else { fprintf(f, "\\%03o", (int)s[i]); }; i = i+1; break; }; }; } float stringwidth(char* str, float ptsize, int fontno) /* calculate width of string */ /* array timesbold_width must be set correctly before calling this routine */ { int i, len, ch; float width; int code; float baseptsize; float* charwidth; if (fontno == 3) { charwidth = &helvetica_width[0]; baseptsize = 12.0; } else { charwidth = ×bold_width[0]; baseptsize = 13.0; }; width = 0.0; len = strlen(str); i = 0; while (i < len) { i = getISO(str, i, &code); ch = code - 32; if ((ch >= 0) && (ch < 224)) { width = width + charwidth[ch]; } else { width = width + charwidth[0]; /* approximate with space */ }; }; return(width*ptsize/baseptsize); } setscaling(s) char* s; /* scaling value from string following -s in arglist */ { float f; f = -1.0; sscanf(s, "%f", &f); if (f < 0.001) { f = TUNE_SCALING; }; scale = f; } setmargins(s) char* s; /* set margin values from string following -M in arglist */ { int i, j; xmargin = XMARGIN; ymargin = YMARGIN; if (s != NULL) { i = -1; j = -1; sscanf(s, "%dx%d", &i, &j); if ((i != -1) && (j != -1)) { xmargin = i; ymargin = j; }; }; } setpagesize(s) char* s; /* set page size from string following -P in arglist */ { int i, j; pagelen = A4_PAGELEN - 2 * ymargin; pagewidth = A4_PAGEWIDTH - 2 * xmargin; if (s != NULL) { i = 0; j = 0; sscanf(s, "%dx%d", &i, &j); if (i == 0) { pagelen = A4_PAGELEN - 2 * ymargin; pagewidth = A4_PAGEWIDTH - 2 * xmargin; }; if (i == 1) { pagelen = US_LETTER_PAGELEN - 2 * ymargin; pagewidth = US_LETTER_PAGEWIDTH - 2 * xmargin; }; if ((i > 100) && (i > 2*xmargin) && (j > 100) && (j > 2*ymargin)) { pagewidth = i - 2 * xmargin; pagelen = j - 2 * ymargin; }; }; scaledlen = ((float)(pagelen))/scale; scaledwidth = ((float)(pagewidth))/scale; } set_space(afont, s) struct font* afont; char* s; { int count; float x; char units[40]; count = sscanf(s, "%f%s", &x, &units); if (count > 0) { if ((count >= 2) && (strncmp(units, "cm", 2) == 0)) { x = x*28.3; }; if ((count >= 2) && (strncmp(units, "in", 2) == 0)) { x = x*72.0 ; }; afont->space = (int)x; }; } init_font(afont, ptsize, spce, defnum, specialnum) /* initialize font structure with given values */ struct font* afont; int ptsize, spce, defnum, specialnum; { afont->pointsize = ptsize; afont->space = spce; afont->default_num = defnum; afont->special_num = specialnum; afont->name = NULL; afont->defined = 0; } void font_command(p, s) /* execute font-related %%commands */ /* called from event_specific */ char* p; char*s; { if (strcmp(p, "textfont") == 0) { define_font(&textfont, s); }; if (strcmp(p, "titlefont") == 0) { define_font(&titlefont, s); }; if (strcmp(p, "subtitlefont") == 0) { define_font(&subtitlefont, s); }; if (strcmp(p, "wordsfont") == 0) { define_font(&wordsfont, s); }; if (strcmp(p, "composerfont") == 0) { define_font(&composerfont, s); }; if (strcmp(p, "vocalfont") == 0) { define_font(&vocalfont, s); }; if (strcmp(p, "gchordfont") == 0) { define_font(&gchordfont, s); }; if (strcmp(p, "partsfont") == 0) { define_font(&partsfont, s); }; if (strcmp(p, "titlespace") == 0) { set_space(&titlefont, s); }; if (strcmp(p, "subtitlespace") == 0) { set_space(&subtitlefont, s); }; if (strcmp(p, "composerspace") == 0) { set_space(&composerfont, s); }; if (strcmp(p, "vocalspace") == 0) { set_space(&vocalfont, s); }; if (strcmp(p, "wordsspace") == 0) { set_space(&wordsfont, s); }; if (strcmp(p, "gchordspace") == 0) { set_space(&gchordfont, s); }; if (strcmp(p, "textspace") == 0) { set_space(&textfont, s); }; if (strcmp(p, "partsspace") == 0) { set_space(&textfont, s); }; } startpage() /* Encapsulated PostScript for a page header */ { fprintf(f, "%%%%Page: %d %d\n", pagecount, pagecount); fprintf(f, "%%%%BeginPageSetup\n"); fprintf(f, " gsave %d %d T\n", xmargin, ymargin+pagelen); fprintf(f, " 0.8 setlinewidth 0 setlinecap\n"); fprintf(f, " %.3f %.3f scale\n", scale, scale); fprintf(f, "%%%%EndPageSetup\n\n"); fontnum = 0; fontsize = 0; totlen = 0.0; oldplace = 0.0; descend = 0.0; } closepage() /* Encapsulated PostScript for page end */ { char pageno[10]; int pagebottom(); int setfont(); if (pagenumbering) { setfont(12, 3); pagebottom(); fprintf(f, "(%d) %.1f 0 M cshow\n", pagecount, scaledwidth/2.0); }; fprintf(f, "%%%%PageTrailer\n"); fprintf(f, "grestore\n"); fprintf(f, "showpage\n\n"); pagecount = pagecount + 1; } newpage() /* create PostScript for a new page of paper */ /* everything will now be printed on this fresh sheet */ /* ignore the command if the sheet is completely blank */ { if (totlen > 0.0) { closepage(); startpage(); }; } pagebottom() /* move to the bottom of the page */ { fprintf(f, "0 %.1f T\n", -(scaledlen - totlen)); totlen = scaledlen; descend = 0.0; } newblock(float height, float descender) /* The page is modelled as a series of vertical bands */ /* This routine finds room for a fresh band below the current one */ /* It also does a translation to set a new y=0 line */ /* Subsequent calls may draw up to height units above or */ /* descender units below the line y=0 */ { if (totlen+(descend+height+descender) > scaledlen - (float)(pagenumbering*12)) { newpage(); }; fprintf(f, "0 %.1f T\n", - descend - height); totlen = totlen + (descend + height); descend = descender; } staveline() /* draw 5 lines of a stave */ { fprintf(f, "%.1f staff\n", scaledwidth); } printclef(enum cleftype t, float x) /* draw a clef of the specified type */ { float xpos; switch (t) { case treble: fprintf(f, "%.1f tclef\n", x); break; case bass: fprintf(f, "%.1f bclef\n", x); break; case alto: fprintf(f, "%.1f cclef\n", x); break; case baritone: fprintf(f, "0 %d T %.1f cclef 0 %d T\n", 4*TONE_HT, x, -4*TONE_HT); break; case tenor: fprintf(f, "0 %d T %.1f cclef 0 %d T\n", 2*TONE_HT, x, -2*TONE_HT); break; case mezzo: fprintf(f, "0 %d T %.1f cclef 0 %d T\n", -2*TONE_HT, x, 2*TONE_HT); break; case soprano: fprintf(f, "0 %d T %.1f cclef 0 %d T\n", -4*TONE_HT, x, 4*TONE_HT); break; default: break; }; } void set_keysig(struct key* k, struct key* newval) { int i; for (i=0; i<7; i++) { k->map[i] = newval->map[i]; k->mult[i] = newval->mult[i]; }; k->sharps = newval->sharps; } float size_keysig(char oldmap[], char newmap[]) /* compute width taken up by drawing the key signature */ { int i, n; n = 0; for (i=0; i<7; i++) { if (((newmap[i] == '=')&&(oldmap[i] != '=')) || (newmap[i] == '^') || (newmap[i] == '_')) { n = n + 1; }; }; return((float)n * 5.0); } float size_timesig(struct fract* meter) /* compute width of the time signature */ { float len1, len2; char temp[20]; sprintf(temp, "%d", meter->num); len1 = stringwidth(temp, 16.0, 2); sprintf(temp, "%d", meter->denom); len2 = stringwidth(temp, 16.0, 2); if (len1 > len2) { return(len1); } else { return(len2); }; } draw_keysig(char oldmap[], char newmap[], int newmult[], float x, enum cleftype clef) /* draw key specified key signature at position x on line */ /* arrays oldmap[], newmap[], newmult[] are indexed with a=0 to g=7 */ /* sharp_pos[] and flat_pos[] give order of notes */ /* e.g. sharp_pos[0] = 8 means 1st sharp drawn is conventionally f */ /* which is in position 8 on the stave (bottom line is E = 0) */ { float xpos; static int sharp_pos[7] = { 8, 5, 9, 6, 3, 7, 4 }; static int flat_pos[7] = { 4, 7, 3, 6, 2, 5, 1}; int i; int offset, pos; int note; switch (clef) { case treble: offset = 0; break; case soprano: offset = 2; break; case mezzo: offset = 4; break; case alto: offset = 6; break; case tenor: offset = 8; break; case baritone: offset = 10; break; case bass: offset = 12; break; }; xpos = x; /* draw naturals to cancel out old accidentals */ for (i=0; i<7; i++) { note = (sharp_pos[i] + 4) % 7; if ((newmap[note] == '=')&&(oldmap[note] != '=')) { pos = (sharp_pos[i] + offset - 3) % 7 + 3; fprintf(f, " %.1f %d nt0", xpos, pos*TONE_HT); xpos = xpos + 5; }; }; /* draw sharps */ for (i=0; i<7; i++) { note = (sharp_pos[i] + 4) % 7; if (newmap[note] == '^') { pos = (sharp_pos[i] + offset - 3) % 7 + 3; if (newmult[note] == 2) { fprintf(f, " %.1f %d dsh0", xpos, pos*TONE_HT); } else { fprintf(f, " %.1f %d sh0", xpos, pos*TONE_HT); }; xpos = xpos + 5; }; }; /* draw flats */ for (i=0; i<7; i++) { note = (flat_pos[i] + 4) % 7; if (newmap[note] == '_') { pos = (flat_pos[i] + offset - 1)%7 + 1; if (newmult[note] == 2) { fprintf(f, " %.1f %d dft0", xpos, pos*TONE_HT); } else { fprintf(f, " %.1f %d ft0", xpos, pos*TONE_HT); }; xpos = xpos + 5; }; }; fprintf(f, "\n"); } draw_meter(struct fract* meter, float x) /* draw meter (time signature) at specified x value */ { fprintf(f, "%.1f (%d) (%d) tsig\n", x, meter->num, meter->denom); } float maxstrwidth(struct llist* strings, float ptsize, int fontno) { float width, max; char* str; max = 0.0; str = firstitem(strings); while (str != NULL) { width = stringwidth(str, ptsize, fontno); if (width > max) { max = width; }; str = nextitem(strings); }; return(max); } sizerest(struct rest* r, struct feature* ft) /* compute width of note element */ { float width; switch (r->len.denom) { case 1: ft->xleft = 2.5; ft->xright = 2.5; break; case 2: ft->xleft = 2.5; ft->xright = 2.5; break; case 4: ft->xleft = 3.0; ft->xright = 4.0; break; case 8: ft->xleft = 4.0; ft->xright = 3.0; break; case 16: ft->xleft = 5.0; ft->xright = 3.0; break; case 32: ft->xleft = 6.0; ft->xright = 5.0; break; case 64: ft->xleft = 7.0; ft->xright = 5.0; break; }; if (r->gchords != NULL) { width = maxstrwidth(r->gchords, (float)(gchordfont.pointsize), gchordfont.default_num); if (width > ft->xleft + ft->xright) { ft->xright = width - ft->xleft; }; }; } int acc_upsize(char ch) /* returns height above note of accent */ { int size; switch (ch) { case '=': size = NAT_UP; break; case '^': size = SH_UP; break; case '_': size = FLT_UP; break; default: size = 0; break; }; return(size); } int acc_downsize(char ch) /* returns depth below note of accent */ { int size; switch (ch) { case '=': size = NAT_DOWN; break; case '^': size = SH_DOWN; break; case '_': size = FLT_DOWN; break; default: size = 0; break; }; return(size); } setstemlen(struct note* n, int ingrace) { if (n->base == 1) { n->stemlength = 0.0; } else { if (ingrace) { if (n->beaming != single) { n->stemlength = GRACE_STEMLEN; /* default stem length */ /* value will be recalculated later by setbeams() */ } else { switch(n->base) { case 64: n->stemlength = GRACE_STEMLEN + 2*TAIL_SEP*0.7; break; case 32: n->stemlength = GRACE_STEMLEN + TAIL_SEP*0.7; break; default: n->stemlength = GRACE_STEMLEN; /* default stem length */ break; }; }; } else { if (n->beaming != single) { n->stemlength = STEMLEN; /* default stem length */ /* value will be recalculated later by setbeams() */ } else { switch(n->base) { case 64: n->stemlength = STEMLEN + 2*TAIL_SEP; break; case 32: n->stemlength = STEMLEN + TAIL_SEP; break; default: n->stemlength = STEMLEN; /* default stem length */ break; }; }; }; }; } sizenote(struct note* n, struct feature* f, int ingrace) /* compute width and height of note element */ { float width; char* decorators; int i; f->ydown = TONE_HT*(n->y - 2); f->yup = TONE_HT*(n->y + 2); if (ingrace) { f->xleft = GRACE_HALF_HEAD; f->xright = GRACE_HALF_HEAD; } else { f->xleft = HALF_HEAD; f->xright = HALF_HEAD; }; if (n->dots > 0) { f->xright = f->xright + (n->dots*DOT_SPACE); }; if ((n->stemup) && (n->base >= 8) && (n->beaming==single)) { if (f->xright < (HALF_HEAD + TAILWIDTH)) { f->xright = HALF_HEAD + TAILWIDTH; }; }; if (n->accidental != ' ') { f->xleft = f->xleft + ACC_OFFSET; if (n->stemup) { f->ydown = TONE_HT*(n->y) - (float)(acc_downsize(n->accidental)); } else { f->yup = TONE_HT*(n->y) + (float)(acc_upsize(n->accidental)); }; }; if (n->stemlength > 0.0) { if (n->stemup) { f->yup = TONE_HT*(n->y) + n->stemlength; } else { f->ydown = TONE_HT*(n->y) - n->stemlength; }; }; if (n->accents != NULL) { decorators = n->accents; for (i=0; iyup = f->yup + BIG_DEC_HT; break; case 'R': if (n->stemup) { f->ydown = f->ydown + BIG_DEC_HT; } else { f->yup = f->yup + BIG_DEC_HT; }; break; case 'M': case '.': if (n->stemup) { f->ydown = f->ydown + SMALL_DEC_HT; } else { f->yup = f->yup + SMALL_DEC_HT; }; break; default: /* this should never happen */ event_error("Un-recognized accent"); break; }; }; }; if (n->syllables != NULL) { width = maxstrwidth(n->syllables, (float)(vocalfont.pointsize), vocalfont.default_num); if (width > f->xleft + f->xright) { f->xright = width - f->xleft; }; }; if (n->gchords != NULL) { width = maxstrwidth(n->gchords, (float)(gchordfont.pointsize), gchordfont.default_num); if (width > f->xleft + f->xright) { f->xright = width - f->xleft; }; }; } sizechord(struct chord* ch, int ingrace) { if ((ch->ytop + ch->ybot < 8)||(ingrace)) { ch->stemup = 1; } else { ch->stemup = 0; }; ch->stemlength = STEMLEN; } setxy(float* x, float* y, struct note* n, struct feature* ft, float offset, float half_head) /* compute x,y co-ordinates above note for drawing a beam */ { if (n->stemup) { *x = ft->x + half_head; *y = (float)(TONE_HT*n->y) + n->stemlength - offset; } else { *x = ft->x - half_head; *y = (float)(TONE_HT*n->y) - n->stemlength + offset; }; } drawtuple(struct feature* beamset[], int beamctr, int tupleno) /* label a beam which is a tuplet of notes (e.g. triplet) */ { float x0, x1, y0, y1; struct note* n; int stemup; x0 = beamset[0]->x; n = beamset[0]->item; stemup = n->stemup; if (stemup) { y0 = (float)(TONE_HT*n->y) + n->stemlength; } else { y0 = (float)(TONE_HT*n->y) - n->stemlength; }; x1 = beamset[beamctr-1]->x; n = beamset[beamctr-1]->item; if (stemup) { y1 = (float)(TONE_HT*n->y) + n->stemlength; } else { y1 = (float)(TONE_HT*n->y) - n->stemlength; }; if (stemup) { fprintf(f, " %.1f %.1f (%d) bnum", (x0+x1)/2, (y0+y1)/2 + TUPLE_UP, tupleno); } else { fprintf(f, " %.1f %.1f (%d) bnum", (x0+x1)/2, (y0+y1)/2 + TUPLE_DOWN, tupleno); }; } drawbeam(struct feature* beamset[], int beamctr, int dograce) /* draw a beam spanning the notes in array beamset[] */ { int i, d; int donenotes; int start, stop; float x0, x1, y0, y1, offset; struct note* n; int stemup, beamdir; float half_head; if (beamctr==0) { event_error("Internal error: beam with 0 notes"); showtune(&thetune); return; exit(0); }; if (beamset[0]->type != NOTE) { event_error("Internal error: beam does not start with NOTE"); exit(0); }; fprintf(f, "\n"); n = beamset[0]->item; stemup = n->stemup; beamdir = 2*stemup - 1; donenotes = 1; d = 8; offset = 0.0; if (dograce) { half_head = GRACE_HALF_HEAD; } else { half_head = HALF_HEAD; }; while (donenotes) { donenotes = 0; start = -1; for (i=0; iitem; if (n->base >= d) { if (start == -1) { start = i; setxy(&x0, &y0, n, beamset[i], offset, half_head); }; stop = i; setxy(&x1, &y1, n, beamset[i], offset, half_head); }; if ((n->beaming == endbeam) || (n->base < d)) { if (start != -1) { /* draw unit */ if (start == stop) { if (start != 0) { /* half line in front of note */ n = beamset[start-1]->item; setxy(&x0, &y0, n, beamset[start-1], offset, half_head); x0 = x0 + (x1-x0)/2; y0 = y0 + (y1-y0)/2; } else { /* half line behind note */ n = beamset[start+1]->item; setxy(&x1, &y1, n, beamset[start+1], offset, half_head); x1 = x1 + (x0-x1)/2; y1 = y1 + (y0-y1)/2; }; if (dograce) { fprintf(f, "%.1f %.1f %.1f %.1f gbm2\n", x0, y0, x1, y1); } else { fprintf(f, "%.1f %.1f %.1f %.1f %.1f bm\n", x0, y0, x1, y1, (float)(beamdir * TAIL_WIDTH)); }; } else { if (dograce) { fprintf(f, "%.1f %.1f %.1f %.1f gbm2\n", x0, y0, x1, y1); } else { fprintf(f, "%.1f %.1f %.1f %.1f %.1f bm\n", x0, y0, x1, y1, (float)(beamdir * TAIL_WIDTH)); }; }; donenotes = 1; start = -1; }; }; }; d = d*2; offset = offset + TAIL_SEP; }; } sizevoice(struct voice* v, struct tune* t) /* compute width and height values for all elements in voice */ { struct feature* ft; struct note* anote; struct key* akey; enum cleftype* aclef; char* astring; struct fract* afract; struct rest* arest; struct note* lastnote; struct chord* thischord; enum tail_type chordbeaming; ingrace = 0; inchord = 0; chordhead = NULL; thischord = NULL; v->clef = t->clef; if (v->keysig == NULL) { event_error("Voice has no key signature"); }; ft = v->first; lastnote = NULL; while (ft != NULL) { ft->xleft = 0.0; ft->xright = 0.0; ft->ydown = 0.0; ft->yup = 0.0; switch (ft->type) { case SINGLE_BAR: ft->xleft = 0.8; ft->xright = 0.0; break; case DOUBLE_BAR: ft->xleft = 3.0; ft->xright = 0.0; break; case BAR_REP: ft->xleft = 1.0; ft->xright = 10.0; break; case REP_BAR: ft->xleft = 1.0; ft->xright = 10.0; break; case REP1: break; case REP2: break; case PLAY_ON_REP: break; case BAR1: break; case REP_BAR2: ft->xleft = 6.0; ft->xright = 5.0; break; case DOUBLE_REP: ft->xleft = 10.0; ft->xright = 10.0; break; case THICK_THIN: ft->xleft = 0.0; ft->xright = 6.0; break; case THIN_THICK: ft->xleft = 6.0; ft->xright = 0.0; break; case PART: astring = ft->item; case TEMPO: break; case TIME: afract = ft->item; if (afract == NULL) { afract = &v->meter; }; ft->xleft = 0; ft->xright = size_timesig(afract); break; case KEY: ft->xleft = 0; akey = ft->item; ft->xright = size_keysig(v->keysig->map, akey->map); set_keysig(v->keysig, akey); break; case REST: arest = ft->item; sizerest(arest, ft); break; case TUPLE: break; case NOTE: anote = ft->item; setstemlen(anote, ingrace); sizenote(anote, ft, ingrace); if (inchord) { if (chordcount == 0) { chordhead = ft; thischord->ytop = anote->y; thischord->ybot = anote->y; chordbeaming = anote->beaming; } else { if (anote->y > thischord->ytop) { thischord->ytop = anote->y; }; if (anote->y < thischord->ybot) { thischord->ybot = anote->y; }; if (ft->xleft > chordhead->xleft) { chordhead->xleft = ft->xleft; }; if (ft->xright > chordhead->xright) { chordhead->xright = ft->xright; }; if (ft->yup > chordhead->yup) { chordhead->yup = ft->yup; }; if (ft->ydown > chordhead->ydown) { chordhead->ydown = ft->ydown; }; ft->type = CHORDNOTE; }; chordcount = chordcount + 1; }; break; case NONOTE: break; case OLDTIE: break; case TEXT: break; case SLUR_ON: break; case SLUR_OFF: break; case TIE: break; case TITLE: break; case CHANNEL: break; case TRANSPOSE: break; case RTRANSPOSE: break; case GRACEON: ingrace = 1; break; case GRACEOFF: ingrace = 0; break; case SETGRACE: break; case SETC: break; case GCHORD: break; case GCHORDON: break; case GCHORDOFF: break; case VOICE: break; case CHORDON: inchord = 1; chordcount = 0; thischord = ft->item; break; case CHORDOFF: if (thischord != NULL) { anote = chordhead->item; thischord->beaming = chordbeaming; if (thischord->beaming == single) { sizechord(thischord, ingrace); }; }; inchord = 0; chordhead = NULL; thischord = NULL; break; case SLUR_TIE: break; case TNOTE: break; case LT: break; case GT: break; case DYNAMIC: break; case LINENUM: lineno = (int)(ft->item); break; case MUSICLINE: break; case MUSICSTOP: break; case WORDLINE: break; case WORDSTOP: break; case INSTRUCTION: break; case NOBEAM: break; case CLEF: aclef = ft->item; if (aclef == NULL) { aclef = &v->clef; }; ft->yup = (float)8*TONE_HT; ft->ydown = 0.0; switch (*aclef) { case treble: ft->yup = (float)TREBLE_UP; ft->ydown = (float)TREBLE_DOWN; ft->xright = TREBLE_RIGHT; ft->xleft = TREBLE_LEFT; break; case baritone: ft->yup = (float)12*TONE_HT; ft->xright = CCLEF_RIGHT; ft->xleft = CCLEF_LEFT; break; case tenor: ft->yup = (float)10*TONE_HT; ft->xright = CCLEF_RIGHT; ft->xleft = CCLEF_LEFT; break; case mezzo: ft->ydown = (float)2*TONE_HT; ft->xright = CCLEF_RIGHT; ft->xleft = CCLEF_LEFT; break; case soprano: ft->ydown = (float)4*TONE_HT; ft->xright = CCLEF_RIGHT; ft->xleft = CCLEF_LEFT; break; case bass: ft->xright = BASS_RIGHT; ft->xleft = BASS_LEFT; break; }; v->clef = *aclef; break; case PRINTLINE: break; case NEWPAGE: break; case LEFT_TEXT: break; case CENTRE_TEXT: break; case VSKIP: break; default: printf("unknown type %d\n", ft->type); break; }; ft = ft->next; }; } sizetune(struct tune* t) /* compute width and height values for all elements in the tune */ { struct voice* v; v = firstitem(&t->voices); while (v != NULL) { sizevoice(v, t); v = nextitem(&t->voices); }; } singlehead(float x, float y, int base, int dots) { int i; fprintf(f, "%.1f %.1f ", x, y); /* note head */ switch(base) { case 1: fprintf(f, "HD"); break; case 2: fprintf(f, "Hd"); break; default: fprintf(f, "hd"); break; }; for (i=1; i <= dots; i++) { fprintf(f, " %.1f 3.0 dt", (float)(HALF_HEAD+DOT_SPACE*i)); }; } drawhead(struct note* n, float x, struct feature* ft) /* draw just the head of a note */ { int i; char* decorators; float ytop, ybot; singlehead(x, (float)(TONE_HT*n->y), n->base, n->dots); switch (n->accidental) { case '=': fprintf(f, " %.1f nt", ACC_OFFSET); break; case '^': if (n->mult == 1) { fprintf(f, " %.1f sh", ACC_OFFSET); } else { fprintf(f, " %.1f dsh", ACC_OFFSET); }; break; case '_': if (n->mult == 1) { fprintf(f, " %.1f ft", ACC_OFFSET); } else { fprintf(f, " %.1f dft", ACC_OFFSET); }; break; default: break; }; i = 10; while (n->y >= i) { fprintf(f, " %d hl", i*TONE_HT); i = i + 2; }; i = -2; while (n->y <= i) { fprintf(f, " %d hl", i*TONE_HT); i = i - 2; }; if (n->accents != NULL) { decorators = n->accents; ytop = (n->y + 2) * TONE_HT; ybot = (n->y - 2) * TONE_HT; if (n->stemup) { ytop = ytop + n->stemlength; } else { ybot = ybot - n->stemlength; }; for (i=0; istemup) { fprintf(f, " %.1f stc", ybot+STC_OFF); ybot = ybot - SMALL_DEC_HT; } else { fprintf(f, " %.1f stc", ytop+STC_OFF); ytop = ytop + SMALL_DEC_HT; }; break; case 'R': if (n->stemup) { fprintf(f, " %.1f cpd", ybot+CPD_OFF); ybot = ybot - SMALL_DEC_HT; } else { fprintf(f, " %.1f cpu", ytop+CPU_OFF); ytop = ytop + SMALL_DEC_HT; }; break; case 'M': if (n->stemup) { fprintf(f, " %.1f emb", ybot+EMB_OFF); ybot = ybot - SMALL_DEC_HT; } else { fprintf(f, " %.1f emb", ytop+EMB_OFF); ytop = ytop + SMALL_DEC_HT; }; break; case 'H': fprintf(f, " %.1f hld", ytop+HLD_OFF); ytop = ytop + BIG_DEC_HT; break; case '~': fprintf(f, " %.1f grm", ytop+GRM_OFF); ytop = ytop + BIG_DEC_HT; break; case 'u': fprintf(f, " %.1f upb", ytop+UPB_OFF); ytop = ytop + BIG_DEC_HT; break; case 'v': fprintf(f, " %.1f dnb", ytop+DNB_OFF); ytop = ytop + BIG_DEC_HT; break; case 'T': fprintf(f, " %.1f trl", ytop+TRL_OFF); ytop = ytop + BIG_DEC_HT; break; default: break; }; }; }; fprintf(f, "\n"); } drawgracehead(struct note* n, float x, struct feature* ft, enum tail_type tail) /* draw the head of a grace note */ { int i; float y; y = (float)(TONE_HT*n->y); switch (tail) { case nostem: /* note head only */ fprintf(f, "%.1f %.1f gn", x, y); break; case single: /* note head with stem and tail */ fprintf(f, "%.1f %.1f %.1f gn1", x, y, n->stemlength); break; case midbeam: case startbeam: case endbeam: /* note head with stem and tail */ fprintf(f, "%.1f %.1f %.1f gnt", x, y, n->stemlength); break; }; switch (n->accidental) { case '=': fprintf(f, " %.1f %.1f gnt0", x-ACC_OFFSET, y); break; case '^': if (n->mult == 1) { fprintf(f, " %.1f %.1f gsh0", x-ACC_OFFSET, y); } else { fprintf(f, " %.1f %.1f gds0h", x-ACC_OFFSET, y); }; break; case '_': if (n->mult == 1) { fprintf(f, " %.1f %.1f gft0", x-ACC_OFFSET, y); } else { fprintf(f, " %.1f %.1f gdf0", x-ACC_OFFSET, y); }; break; default: break; }; i = 10; while (n->y >= i) { fprintf(f, " %.1f %.1f ghl", x, (float)i*TONE_HT); i = i + 2; }; i = -2; while (n->y <= i) { fprintf(f, " %.1f %.1f ghl", x, (float)i*TONE_HT); i = i - 2; }; fprintf(f, "\n"); } handlegracebeam(struct note *n, struct feature* ft) { switch (n->beaming) { case single: gracebeamctr = -1; break; case startbeam: gracebeamset[0] = ft; gracebeamctr = 1; break; case midbeam: case endbeam: if (gracebeamctr > 0) { gracebeamset[gracebeamctr] = ft; if (gracebeamctr < 32) { gracebeamctr = gracebeamctr + 1; } else { event_error("Too many notes under one gracebeam"); }; } else { event_error("Internal beaming error"); exit(1); }; break; }; } drawgracenote(struct note* n, float x, struct feature* ft, struct chord* thischord) /* draw an entire grace note */ { int i; handlegracebeam(n, ft); drawgracehead(n, x, ft, n->beaming); if (n->beaming == endbeam) { drawbeam(gracebeamset, gracebeamctr, 1); }; fprintf(f, "\n"); } singletail(int base, int stemup, float stemlength) { switch(base) { case 1: case 2: case 4: break; case 8: if (stemup) { fprintf(f, " %.1f f1u", stemlength); } else { fprintf(f, " %.1f f1d", stemlength); }; break; case 16: if (stemup) { fprintf(f, " %.1f f2u", stemlength); } else { fprintf(f, " %.1f f2d", stemlength); }; break; case 32: if (stemup) { fprintf(f, " %.1f f3u", stemlength); } else { fprintf(f, " %.1f f3d", stemlength); }; break; case 64: if (stemup) { fprintf(f, " %.1f f4u", stemlength); } else { fprintf(f, " %.1f f4d", stemlength); }; break; default: event_error("Note value too small"); break; }; } drawchordtail(struct chord* ch, float x) { if (ch->base > 1) { fprintf(f, "%.1f setx ", x); if (ch->stemup) { fprintf(f, "%.1f sety ", (float)(ch->ybot*TONE_HT)); fprintf(f, " %.1f su ", ch->stemlength + (float)((ch->ytop - ch->ybot)*TONE_HT)); } else { fprintf(f, "%.1f sety ", (float)(ch->ytop*TONE_HT)); fprintf(f, " %.1f sd ", ch->stemlength + (float)((ch->ytop - ch->ybot)*TONE_HT)); }; if (ch->beaming == single) { singletail(ch->base, ch->stemup, ch->stemlength+ (float)((ch->ytop - ch->ybot)*TONE_HT)); }; fprintf(f, "\n"); }; } showtext(struct llist* textitems, float x, float ystart, float ygap) /* This routine deals with lyrics, guitar chords and instructions */ /* print text item at specified point */ /* if a string starts with ':' this means it is a special symbol */ { float ygc; char* gc; gc = firstitem(textitems); ygc = ystart; while (gc != NULL) { if (*gc == ':') { switch (*(gc+1)) { case 'p': /* part label */ fprintf(f, " %.1f %.1f %.1f (", x, ygc, ygap); ISOfprintf(gc+2); fprintf(f, ") boxshow "); break; case 's': fprintf(f, " %.1f %.1f segno\n", x, ygc); break; case 'c': fprintf(f, " %.1f %.1f coda\n", x, ygc); break; default: break; }; } else { fprintf(f, " %.1f %.1f (", x, ygc); ISOfprintf(gc); fprintf(f, ") gc "); }; gc = nextitem(textitems); ygc = ygc - ygap; }; } define_font(struct font* thefont, char* s) /* set up a font structure with user-defined font and pointsize */ { char fontname[80]; int fontsize, params; params = sscanf(s, "%s %d", fontname, &fontsize); if (params >= 1) { if (strcmp(fontname, "-") != 0) { if (thefont->name != NULL) { free(thefont->name); }; thefont->name = addstring(fontname); thefont->defined = 0; }; if (params == 2) { thefont->pointsize = fontsize; }; }; } setfont(int size, int num) /* define font point size and style */ { if ((fontsize != size) || (fontnum != num)) { fprintf(f, "%d.0 F%d\n", size, num); fontsize = size; fontnum = num; }; } setfontstruct(struct font* thefont) /* set font according to font structure */ { if (thefont->name == NULL) { setfont(thefont->pointsize, thefont->default_num); } else { if (thefont->defined == 0) { /* define font */ fprintf(f, "/%s-ISO /%s setISOfont\n", thefont->name, thefont->name); fprintf(f, "/F%d { /%s-ISO exch selectfont} def\n", thefont->special_num, thefont->name); thefont->defined = 1; } setfont(thefont->pointsize, thefont->special_num); }; } notetext(struct note* n, int* tupleno, float x, struct feature* ft) /* print text associated with note */ { char* gc; float ygc; if (n->beaming == endbeam) { drawbeam(beamset, beamctr, 0); if (*tupleno != 0) { drawtuple(beamset, beamctr, *tupleno); *tupleno = 0; }; }; fprintf(f, "\n"); if (n->instructions != NULL) { setfontstruct(&partsfont); showtext(n->instructions, x - ft->xleft, yinstruct, (float)(partsfont.pointsize+partsfont.space)); }; if (n->gchords != NULL) { setfontstruct(&gchordfont); showtext(n->gchords, x - ft->xleft, ygchord, (float)(gchordfont.pointsize+gchordfont.space)); }; if (n->syllables != NULL) { setfontstruct(&vocalfont); showtext(n->syllables, x - ft->xleft, ywords, (float)(vocalfont.pointsize + vocalfont.space)); }; } handlebeam(struct note* n, struct feature* ft) { switch (n->beaming) { case single: beamctr = -1; break; case startbeam: beamset[0] = ft; beamctr = 1; rootstem = n->stemup; break; case midbeam: case endbeam: if (beamctr > 0) { beamset[beamctr] = ft; if (beamctr < 64) { beamctr = beamctr + 1; } else { event_error("Too many notes under one beam"); }; } else { event_error("Internal beaming error"); }; n->stemup = rootstem; break; }; } drawnote(struct note* n, float x, struct feature* ft, struct chord* thischord, int* tupleno) /* draw a note */ { int base; handlebeam(n, ft); drawhead(n, x, ft); if (thischord == NULL) { if (n->base > 1) { if (n->stemup) { fprintf(f, " %.1f su", n->stemlength); } else { fprintf(f, " %.1f sd", n->stemlength); }; }; }; if (n->beaming == single) { singletail(n->base, n->stemup, n->stemlength); }; notetext(n, tupleno, x, ft); fprintf(f, "\n"); } drawrest(struct rest* r, float x) /* draw a rest of specified duration */ { reduce(&r->len); switch (r->len.denom) { case 1: fprintf(f, "%.1f %d r1 ", x, 4*TONE_HT); break; case 2: fprintf(f, "%.1f %d r2 ", x, 4*TONE_HT); break; case 4: fprintf(f, "%.1f %d r4 ", x, 4*TONE_HT); break; case 8: fprintf(f, "%.1f %d r8 ", x, 4*TONE_HT); break; case 16: fprintf(f, "%.1f %d r16 ", x, 4*TONE_HT); break; case 32: fprintf(f, "%.1f %d r32 ", x, 4*TONE_HT); break; case 64: fprintf(f, "%.1f %d r64 ", x, 4*TONE_HT); break; default: break; }; fprintf(f, "\n"); if (r->instructions != NULL) { setfontstruct(&partsfont); showtext(r->instructions, x, yinstruct, (float)(partsfont.pointsize+partsfont.space)); }; if (r->gchords != NULL) { setfontstruct(&gchordfont); showtext(r->gchords, x, ygchord, (float)(gchordfont.pointsize+gchordfont.space)); }; } setbeams(struct feature* note[], struct chord* chording[], int m, float stemlen) /* choose the spatial postioning of beams and compute appropriate stem */ /* lengths for all notes within the beam */ { int i; float min[64]; float lift; struct note* anote; int stemup; float x1, y1, x2, y2, xi, yi; float max; /* printf("Doing setbeams m=%d\n", m); */ anote = note[0]->item; stemup = anote->stemup; /* calculate minimum feasible stem lengths */ /* bearing in mind space needed for the tails */ for (i=0; iitem; anote->stemup = stemup; switch (anote->base) { default: case 8: min[i] = TAIL_SEP; break; case 16: min[i] = 2*TAIL_SEP; break; case 32: min[i] = 3*TAIL_SEP; break; case 64: min[i] = 4*TAIL_SEP; break; }; if (anote->accidental == ' ') { min[i] = min[i] + TONE_HT; } else { if (stemup) { min[i] = min[i] + (float)acc_upsize(anote->accidental); } else { min[i] = min[i] + (float)acc_downsize(anote->accidental); }; }; min[i] = min[i] + TONE_HT; /* require at least this much clearance */ if (stemup) { if (chording[i] == NULL) { min[i] = TONE_HT*anote->y + min[i]; /* avoid collision with previous note */ if ((i > 0) && (min[i-1] - TONE_HT < min[i])) { min[i-1] = min[i]; }; } else { min[i] = TONE_HT*chording[i]->ytop + min[i]; }; } else { if (chording[i] == NULL) { min[i] = TONE_HT*anote->y - min[i]; /* avoid collision with previous note */ if (i > 0) { anote = note[i-1]->item; if (anote->y*TONE_HT < min[i]) { min[i] = anote->y*TONE_HT; }; }; } else { min[i] = TONE_HT*chording[i]->ybot - min[i]; }; }; }; /* work out clearance from a straight line between 1st and last note */ x1 = note[0]->x; anote = note[0]->item; y1 = anote->y*TONE_HT; x2 = note[m-1]->x; anote = note[m-1]->item; y2 = anote->y*TONE_HT; if (x1 == x2) { printf("Internal error: x1 = x2 = %.1f\n", x1); x2 = x2 + 1; }; lift = 0.0; max = 0.0; if (stemup) { for (i=0; ix; yi = y1 + (y2-y1)*(xi-x1)/(x2-x1); if (min[i] - yi > lift) { lift = min[i] - yi; }; if (min[i] - yi < max) { max = min[i] - yi; }; }; if (lift - max < stemlen) { lift = stemlen - max; }; } else { for (i=0; ix; yi = y1 + (y2-y1)*(xi-x1)/(x2-x1); if (min[i] - yi < lift) { lift = min[i] - yi; }; if (min[i] - yi > max) { max = min[i] - yi; }; }; if (max - lift < stemlen) { lift = max - stemlen; }; }; /* raise the line just enough to clear notes */ for (i=0; iitem; xi = note[i]->x; if (stemup) { anote->stemlength = y1 + (y2-y1)*(xi-x1)/(x2-x1) + lift - TONE_HT*anote->y; } else { anote->stemlength = -(y1 + (y2-y1)*(xi-x1)/(x2-x1) + lift - TONE_HT*anote->y); }; }; /* now transfer results to chords */ for (i=0; iitem; chording[i]->stemup = anote->stemup; if (chording[i]->stemup) { chording[i]->stemlength = anote->stemlength; } else { chording[i]->stemlength = anote->stemlength - (float)((chording[i]->ytop - chording[i]->ybot)*TONE_HT); }; }; }; }; beamline(struct feature* ft) /* compute positioning of beams for one stave line of music */ { struct feature* p; struct note* n; struct feature* beamnote[64]; struct chord* chording[64]; struct chord* lastchord; struct feature* gracebeamnote[64]; struct chord* gracechording[64]; struct chord* gracelastchord; int i, j; int ingrace; p = ft; ingrace = 0; lastchord = NULL; gracelastchord = NULL; while ((p != NULL) && (p->type != PRINTLINE)) { switch (p->type) { case NOTE: if (ingrace) { n = p->item; if (n == NULL) { event_error("Missing NOTE!!!!"); exit(0); }; if (n->beaming == startbeam) { j = 0; }; if (n->beaming != single) { gracebeamnote[j] = p; gracechording[j] = gracelastchord; j = j + 1; }; if ((n->beaming == endbeam)||(j==10)) { setbeams(gracebeamnote, gracechording, j, GRACE_STEMLEN); j = 0; }; } else { /* non-grace notes*/ n = p->item; if (n == NULL) { event_error("Missing NOTE!!!!"); exit(0); }; if (n->beaming == startbeam) { i = 0; }; if (n->beaming != single) { beamnote[i] = p; chording[i] = lastchord; i = i + 1; }; if ((n->beaming == endbeam)||(i==64)) { setbeams(beamnote, chording, i, STEMLEN); i = 0; }; }; break; case CHORDON: if (ingrace) { gracelastchord = p->item; } else { lastchord = p->item; }; break; case CHORDOFF: if (ingrace) { gracelastchord = NULL; } else { lastchord = NULL; }; break; case GRACEON: ingrace = 1; break; case GRACEOFF: ingrace = 0; gracelastchord = NULL; break; default: break; }; p = p->next; }; } measureline(struct feature* ft, float* height, float* descender) /* calculate vertical space (height/descender) requirement of current line */ { struct feature* p; struct note* n; struct rest* r; float max, min; float up, down; int i; int gotgchords, gcount; int gotinstructions, icount; int gotwords, wcount; int gotends; char* gc; char* decorators; p = ft; min = 0.0; max = 8*TONE_HT; gotgchords = 0; gotwords = 0; gotinstructions = 0; gotends = 0; while ((p != NULL) && (p->type != PRINTLINE)) { switch (p->type) { case NOTE: n = p->item; if (n == NULL) { event_error("Missing NOTE!!!!"); exit(0); } else { if (n->gchords != NULL) { gcount = 0; gc = firstitem(n->gchords); while (gc != NULL) { gcount = gcount + 1; gc = nextitem(n->gchords); }; if (gcount > gotgchords) { gotgchords = gcount; }; }; if (n->instructions != NULL) { icount = 0; gc = firstitem(n->instructions); while (gc != NULL) { icount = icount + 1; gc = nextitem(n->instructions); }; if (icount > gotwords) { gotinstructions = icount; }; }; if (n->syllables != NULL) { wcount = 0; gc = firstitem(n->syllables); while (gc != NULL) { wcount = wcount + 1; gc = nextitem(n->syllables); }; if (wcount > gotwords) { gotwords = wcount; }; }; /* recalculate note size in case of new stemlen */ sizenote(n, p, ingrace); if (p->yup > max) { max = p->yup; }; if (p->ydown < min) { min = p->ydown; }; }; break; case REST: r = p->item; if (r == NULL) { event_error("Missing REST!!!!"); exit(0); } else { if (r->gchords != NULL) { gcount = 0; gc = firstitem(r->gchords); while (gc != NULL) { gcount = gcount + 1; gc = nextitem(r->gchords); }; if (gcount > gotgchords) { gotgchords = gcount; }; }; if (r->instructions != NULL) { icount = 0; gc = firstitem(r->instructions); while (gc != NULL) { icount = icount + 1; gc = nextitem(r->instructions); }; if (icount > gotwords) { gotinstructions = icount; }; }; }; break; case REP1: case REP2: case PLAY_ON_REP: case BAR1: case REP_BAR2: gotends = 1; break; case CLEF: if (p->yup > max) { max = p->yup; }; if (-(p->ydown) < min) { min = -(p->ydown); }; break; default: break; }; p = p->next; }; if (gotgchords > 0) { max = max + (gchordfont.pointsize + gchordfont.space)*gotgchords; ygchord = max - (gchordfont.pointsize + gchordfont.space); }; if (gotinstructions > 0) { max = max + INSTRUCT_HT*gotinstructions; yinstruct = max - INSTRUCT_HT; }; if (gotends) { max = max + END_HT; yend = max - END_HT; }; if (gotwords > 0) { ywords = min - (vocalfont.pointsize + vocalfont.space); min = min - (vocalfont.pointsize + vocalfont.space)*gotwords; }; *height = VERT_GAP + max; *descender = -min; } printtext(place, s, afont) enum placetype place; char* s; struct font* afont; /* print text a line of text in specified font at specified place */ { newblock((float)(afont->pointsize + afont->space), 0.0); setfontstruct(afont); fprintf(f, "("); ISOfprintf(s); switch (place) { case left: fprintf(f, ") 0 0 M show\n"); break; case right: fprintf(f, ") %.1f 0 M lshow\n", (float) scaledwidth); break; case centre: fprintf(f, ") %.1f 0 M cshow\n", scaledwidth/2.0); break; }; } vskip(gap) float gap; /* skip over specified amount of vertical space */ { newblock(gap, 0.0); } centretext(s) char* s; /* print text centred in the middle of the page */ /* used by %%centre command */ { printtext(centre, s, &textfont); } lefttext(s) char* s; /* print text up against the left hand margin */ /* used by %%text command */ { printtext(left, s, &textfont); } draw_tempo(float x, float y, struct atempo* t) { int base, dots; char msg[20]; if (t != NULL) { /* fprintf(f, "gsave 0.7 0.7 scale\n"); */ setfont(12, 3); dots = count_dots(&base, t->basenote.num, t->basenote.denom); singlehead(x, y, base, dots); if (base >= 4) { fprintf(f, " %.1f su", STEMLEN); }; singletail(base, 1, STEMLEN); fprintf(f, " %.1f %.1f M (= %d) show\n", x + 20.0, y, t->count); /* fprintf(f, "grestore\n"); */ }; } voicedivider() { newblock((float)VERT_GAP, 0.0); fprintf(f, " %.1f %.1f M %.1f %.1f L\n", scaledwidth/4.0, -descend, scaledwidth*3/4.0, -descend); newblock((float)VERT_GAP, 0.0); } resetvoice(struct tune* t, struct voice * v) /* sets up voice with initial attributes as defined in abc tune header */ { v->place = v->first; v->clef = t->clef; if (v->keysig == NULL) { v->keysig = newkey(t->keysig->name, t->keysig->sharps, t->keysig->map, t->keysig->mult); } else { free(v->keysig->name); v->keysig->name = addstring(t->keysig->name); set_keysig(v->keysig, t->keysig); /* v->keysig->sharps = t->keysig->sharps; */ }; v->meter.num = t->meter.num; v->meter.denom = t->meter.denom; } resettune(struct tune* t) { struct voice* v; v = firstitem(&t->voices); while (v != NULL) { resetvoice(t, v); v = nextitem(&t->voices); }; } open_output_file(filename) char* filename; /* open output file and write the abc2ps library to it */ { f = fopen(filename, "w"); if (f == NULL) { printf("Could not open file!!\n"); exit(0); }; printf("writing file %s\n", filename); printlib(f, filename); fontsize = 0; fontnum = 0; init_font(&textfont, TEXT_HT, 1, 1, 5); init_font(&titlefont, TITLE1_HT, 0, 0, 6); init_font(&subtitlefont, TITLE2_HT, 0, 0, 7); init_font(&wordsfont, WORDS_HT, 1, 0, 8); init_font(&composerfont, COMP_HT, 1, 1, 9); init_font(&vocalfont, LYRIC_HT, 1, 2, 10); init_font(&gchordfont, CHORDNAME_HT, 1, 3, 11); init_font(&partsfont, INSTRUCT_HT, 1, 3, 12); startpage(); } close_output_file() /* complete last page and close file */ { closepage(); fclose(f); } int endrep(inend, end_string, x1, x2) int inend; char* end_string; float x1, x2; /* draws either 1st or 2nd ending marker */ { if (inend != 0) { fprintf(f, "%.1f %.1f %.1f (%s) endy1\n", yend, x1, x2, end_string); }; return(0); } void drawslurtie(struct slurtie* s) { float x0, y0, x1, y1; struct feature* ft; struct note* n; struct rest* r; ft = s->begin; if (ft == NULL) { event_error("Slur or tie missing start note"); return; }; x0 = ft->x; if (ft->type == NOTE) { n = ft->item; y0 = (float)(n->y * TONE_HT); } else { if (ft->type == REST) { r = ft->item; y0 = (float)(4 * TONE_HT); } else { printf("NOT A NOTE/REST (%d)in slur/tie\n" ,ft->type); return; }; }; ft = s->end; if (ft == NULL) { event_error("Slur or tie missing end note"); return; }; x1 = ft->x; if (ft->type == NOTE) { n = ft->item; y1 = (float)(n->y * TONE_HT); } else { if (ft->type == REST) { r = ft->item; y1 = (float)(4 * TONE_HT); } else { printf("NOT A NOTE/REST (%d)in slur/tie\n" ,ft->type); return; }; }; if (n->stemup) { fprintf(f, " %.1f %.1f %.1f %.1f slurdown\n", x0, y0, x1, y1); } else { fprintf(f, " %.1f %.1f %.1f %.1f slurup\n", x0, y0, x1, y1); }; } underbar(struct feature* ft) /* debugging routine */ /* shows width of a graphical element */ { fprintf(f, " %.1f -10 moveto %.1f -10 lineto stroke\n", ft->x - ft->xleft, ft->x + ft->xright); } int printvoiceline(struct voice* v) /* draws one line of music from specified voice */ { struct feature* ft; struct note* anote; struct key* akey; char* astring; struct fract* afract; struct rest* arest; struct tuple* atuple; float x; int sharps; struct chord* thischord; int chordcount; float xchord; enum cleftype* aclef; int printedline; float xend; int inend; char endstr[80]; int ingrace; float height, descender; /* skip over line number so we can check for end of tune */ while ((v->place != NULL) && ((v->place->type == LINENUM) || (v->place->type == NEWPAGE) || (v->place->type == LEFT_TEXT) || (v->place->type == CENTRE_TEXT) || (v->place->type == VSKIP))) { if (v->place->type == LINENUM) { lineno = (int)(v->place->item); }; if (v->place->type == NEWPAGE) { newpage(); }; if (v->place->type == LEFT_TEXT) { printtext(left, v->place->item, &textfont); }; if (v->place->type == CENTRE_TEXT) { printtext(centre, v->place->item, &textfont); }; if (v->place->type == VSKIP) { vskip((float)((int)v->place->item)); }; v->place = v->place->next; }; if (v->place == NULL) { return(0); }; inend = 0; ingrace = 0; chordcount = 0; v->tuple_pending = 0; x = 20; thischord = NULL; if (v->keysig == NULL) { event_error("Voice has no key signature"); } else { sharps = v->keysig->sharps; }; /* draw stave */ ft = v->place; /* spaceline(ft, 0.0); */ beamline(ft); measureline(ft, &height, &descender); newblock(height, descender); staveline(); fprintf(f, "0 0 M 0 24 L\n"); x = 0.0; /* now draw the line */ printedline = 0; while ((ft != NULL) && (!printedline)) { /* printf("type = %d\n", ft->type); */ switch (ft->type) { case SINGLE_BAR: fprintf(f, "%.1f bar\n", ft->x); break; case DOUBLE_BAR: fprintf(f, "%.1f dbar\n", ft->x); inend = endrep(inend, endstr, xend, ft->x); break; case BAR_REP: fprintf(f, "%.1f fbar1 %.1f rdots\n", ft->x, ft->x+10); inend = endrep(inend, endstr, xend, ft->x); break; case REP_BAR: fprintf(f, "%.1f rdots %.1f fbar2\n", ft->x, ft->x+10); inend = endrep(inend, endstr, xend, ft->x); break; case REP1: inend = endrep(inend, endstr, xend, ft->x - ft->xleft); inend = 1; strcpy(endstr, "1"); xend = ft->x + ft->xright; break; case REP2: inend = endrep(inend, endstr, xend, ft->x - ft->xleft); inend = 2; strcpy(endstr, "2"); xend = ft->x + ft->xright; break; case PLAY_ON_REP: inend = endrep(inend, endstr, xend, ft->x - ft->xleft); inend = 1; strcpy(endstr, ft->item); xend = ft->x + ft->xright; break; case BAR1: fprintf(f, "%.1f bar\n", ft->x); inend = endrep(inend, endstr, xend, ft->x - ft->xleft); inend = 1; strcpy(endstr, "1"); xend = ft->x + ft->xright; break; case REP_BAR2: fprintf(f, "%.1f rdots %.1f fbar2\n", ft->x, ft->x+10); inend = endrep(inend, endstr, xend, ft->x - ft->xleft); inend = 2; strcpy(endstr, "2"); xend = ft->x + ft->xright; break; case DOUBLE_REP: fprintf(f, "%.1f fbar1 %.1f rdots %.1f fbar2 %.1f rdots\n", ft->x, ft->x+10, ft->x+2, ft->x-8); inend = endrep(inend, endstr, xend, ft->x); break; case THICK_THIN: fprintf(f, "%.1f fbar1\n", ft->x); inend = endrep(inend, endstr, xend, ft->x); break; case THIN_THICK: fprintf(f, "%.1f fbar2\n", ft->x); inend = endrep(inend, endstr, xend, ft->x); break; case PART: astring = ft->item; break; case TEMPO: break; case TIME: afract = ft->item; if (afract==NULL) { if (v->line == midline) { draw_meter(&v->meter, ft->x); }; } else { setfract(&v->meter, afract->num, afract->denom); if (v->line == midline) { draw_meter(afract, ft->x); }; }; break; case KEY: akey = ft->item; if (v->line == midline) { draw_keysig(v->keysig->map, akey->map, akey->mult, ft->x, v->clef); }; set_keysig(v->keysig, akey); break; case REST: arest = ft->item; drawrest(arest, ft->x); break; case TUPLE: atuple = ft->item; v->tuple_pending = atuple->n; v->tuplenotes = atuple->r; break; case NOTE: anote = ft->item; if (thischord == NULL) { if (ingrace) { drawgracenote(anote, ft->x, ft, thischord); } else { drawnote(anote, ft->x, ft, thischord, &v->tuple_pending); }; } else { xchord = ft->x; /* make sure all chord heads are aligned */ if (ingrace) { handlegracebeam(anote, ft); drawgracehead(anote, ft->x, ft, nostem); } else { handlebeam(anote, ft); drawhead(anote, ft->x, ft); if (chordcount == 0) { notetext(anote, &v->tuple_pending, ft->x, ft); }; chordcount = chordcount + 1; }; }; break; case CHORDNOTE: anote = ft->item; if (ingrace) { drawgracehead(anote, xchord, ft, nostem); } else { drawhead(anote, xchord, ft); }; break; case NONOTE: break; case OLDTIE: break; case TEXT: break; case SLUR_ON: drawslurtie(ft->item); break; case SLUR_OFF: break; case TIE: drawslurtie(ft->item); break; case TITLE: break; case CHANNEL: break; case TRANSPOSE: break; case RTRANSPOSE: break; case GRACEON: ingrace = 1; break; case GRACEOFF: ingrace = 0; break; case SETGRACE: break; case SETC: break; case GCHORD: break; case GCHORDON: break; case GCHORDOFF: break; case VOICE: break; case CHORDON: thischord = ft->item; chordcount = 0; drawchordtail(thischord, ft->next->x); break; case CHORDOFF: thischord = NULL; break; case SLUR_TIE: break; case TNOTE: break; case LT: break; case GT: break; case DYNAMIC: break; case LINENUM: lineno = (int)(ft->item); break; case MUSICLINE: v->line = midline; break; case PRINTLINE: inend = endrep(inend, endstr, xend, scaledwidth); printedline = 1; v->line = newline; break; case MUSICSTOP: break; case WORDLINE: break; case WORDSTOP: break; case INSTRUCTION: break; case NOBEAM: break; case CLEF: aclef = ft->item; if (aclef != NULL) { v->clef = *aclef; }; if (v->line == midline) { printclef(v->clef, ft->x); }; break; case NEWPAGE: event_warning("newpage in music line ignored"); break; case LEFT_TEXT: event_warning("text in music line ignored"); break; case CENTRE_TEXT: event_warning("centred text in music line ignored"); break; case VSKIP: event_warning("%%vskip in music line ignored"); break; default: printf("unknown type: %d\n", (int)ft->type); break; }; ft = ft->next; }; v->place = ft; return(1); } printtune(t) struct tune* t; /* draws the PostScript for an entire abc tune */ { char* atitle; char* wordline; int notesdone; int titleno; struct voice* thisvoice; int doneline; float firstvline, lastvline; struct voice* firstvoice; resettune(t); sizetune(t); resettune(t); if (separate_voices) { monospace(t); } else { spacevoices(t); }; if (debugging) { showtune(t); }; resettune(t); notesdone = 0; atitle = firstitem(&t->title); titleno = 1; while (atitle != NULL) { if (titleno == 1) { printtext(centre, atitle, &titlefont); } else { printtext(centre, atitle, &subtitlefont); }; titleno = titleno + 1; atitle = nextitem(&t->title); }; draw_tempo(10.0, 0.0, t->tempo); if (t->composer != NULL) { printtext(right, t->composer, &composerfont); }; if (t->origin != NULL) { printtext(right, t->origin, &composerfont); }; if (t->parts != NULL) { printtext(right, t->parts, &composerfont); }; donemeter = 0; /* musicsetup(); */ thisvoice = firstitem(&t->voices); while (thisvoice != NULL) { thisvoice->clef = t->clef; setfract(&thisvoice->meter, t->meter.num, t->meter.denom); thisvoice->place = thisvoice->first; thisvoice = nextitem(&t->voices); }; if (separate_voices) { thisvoice = firstitem(&t->voices); while (thisvoice != NULL) { doneline = 1; while (doneline==1) { doneline = printvoiceline(thisvoice); }; thisvoice = nextitem(&t->voices); if (thisvoice != NULL) { voicedivider(); }; }; } else { doneline = 1; while(doneline) { thisvoice = firstitem(&t->voices); firstvoice = thisvoice; doneline = 0; while (thisvoice != NULL) { doneline = (printvoiceline(thisvoice) || doneline); if (thisvoice == firstvoice) { firstvline = totlen; } else { lastvline = totlen; if (lastvline > firstvline) { fprintf(f, "0 24 M 0 %.1f L\n", lastvline - firstvline); }; firstvline = totlen; }; thisvoice = nextitem(&t->voices); }; }; }; /* musicrestore(); */ wordline = firstitem(&t->words); while (wordline != NULL) { printtext(left, wordline, &wordsfont); wordline = nextitem(&t->words); }; }