char version[12] = "2018-02-23"; /* Copyright (C) 2014-18 R. D. Tennent School of Computing, * Queen's University, rdt@cs.queensu.ca * * 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., 51 Franklin Street, * Fifth Floor, Boston, MA 02110-1301, USA. */ /* autosp - preprocessor to generate note-spacing commands for MusiXTeX scores */ /* Usage: autosp [-v | --version | -h | --help] * autosp [-d | --dotted] [-l | --log] infile[.aspc] [outfile[.tex]] * * Options * --dotted (-d) suppresses extra spacing for dotted beam notes. * --log (-l) generate a log file * * * * Key functions: * * process_command, which processes commands * \instrumentnumber * \nbinstrument * \setstaffs * \startpiece * \TransformNotes * \def\vnotes * \def\atnextbar * \anotes etc. * \bar * \endpiece etc. * * analyze_notes, which determines pointers notes[i] and * characters terminator[i] for each staff; '$' is used * as the terminator for the last staff. * * generate_notes, which repeatedly uses spacing_note (i) to * determine spacings[i] of the next spacing note in * each staff; the current spacing is then the smallest * of these. If necessary, the current notes command * is terminated by terminate_notes() and a new notes * command initiated by initialize_notes(). The current global * skip and note commands for each staff are then copied to * outstrings[i] using output_notes (i). When necessary, * vspacing[i] is set to generate subsequent skips. * Appoggiaturas and xtuplets are treated separately. * * spacing_note (i), which searches for the next spacing note * in the i'th staff, unless a "virtual note" (skip) is * needed. * * output_notes (i), which copies a global_skip string and * outstrings[i] to output, using filter_output (i) to discard * commas and global and large skips, and insert collective-coding * prefixes and closing braces. * * terminate_notes, which copies outstrings[i] to output, * in the order determined by TransformNotes2. * * Spacing values are expressed as sums of binary multiples of * SMALL_NOTE (by default, 1/256th); for example, half-note * spacing = SP(2) = SMALL_NOTE/2. Special small or large * values are used for appoggiaturas and bar-rests. * */ # include "utils.h" #ifdef HAVE_CONFIG_H /* for TeXLive */ #include #endif # ifdef KPATHSEA # include # else # include # endif # ifdef HAVE_STDBOOL_H # include # else # ifndef HAVE__BOOL # define _Bool signed char # endif # define bool _Bool # define true 1 # define false 0 # endif # define PRIVATE static # define SHORT_LEN 256 # define MAX_STAFFS 9 # define SMALL_NOTE 512 # define SP(note) (SMALL_NOTE/note) /* note = { 256 | 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 } */ # define MAX_SPACING 2*SP(1) # define APPOGG_SPACING SP(64)+SP(256) /* not a legitimate spacing */ # define notespp "\\vnotes2.95\\elemskip" # define Notespp "\\vnotes3.95\\elemskip" # define NOtespp "\\vnotes4.95\\elemskip" # define NOTespp "\\vnotes6.95\\elemskip" # define NOTEsp "\\vnotes9.52\\elemskip" # define APPOGG_NOTES "\\vnotes1.45\\elemskip" PRIVATE bool debug = false; PRIVATE char infilename[SHORT_LEN]; PRIVATE char *infilename_n = infilename; PRIVATE char outfilename[SHORT_LEN]; PRIVATE char *outfilename_n = outfilename; PRIVATE char logfilename[SHORT_LEN]; PRIVATE char *logfilename_n = logfilename; PRIVATE FILE *infile, *outfile, *logfile; PRIVATE char line[LINE_LEN]; /* line of input */ PRIVATE int ninstr = 1; /* number of instruments */ PRIVATE int staffs[MAX_STAFFS] = {0,1}; /* number of staffs for ith instrument*/ PRIVATE int nstaffs = 1; /* number of staffs */ PRIVATE int nastaffs = 1; /* number of active staffs; */ PRIVATE char terminator[MAX_STAFFS]; /* one of '&' "|', '$' */ PRIVATE bool active[MAX_STAFFS]; /* is staff i active? */ PRIVATE char *notes[MAX_STAFFS]; /* note segment for ith staff */ PRIVATE char *current[MAX_STAFFS]; PRIVATE int spacings[MAX_STAFFS]; /* spacing for ith staff */ PRIVATE int spacing = MAX_SPACING; /* spacing for current notes */ PRIVATE int old_spacing = MAX_SPACING; PRIVATE int vspacing[MAX_STAFFS]; /* virtual-note (skip) spacing */ PRIVATE bool vspacing_active[MAX_STAFFS]; /* virtual-note spacing active? */ /* used to preclude unnecessary pre-accidental skips */ PRIVATE bool nonvirtual_notes; /* used to preclude output of *only* virtual notes */ PRIVATE int cspacing[MAX_STAFFS]; /* nominal collective-note spacing */ PRIVATE char collective[MAX_STAFFS][SHORT_LEN]; /* prefixes for collective note sequences */ PRIVATE bool first_collective[MAX_STAFFS]; PRIVATE int restbars = 0; PRIVATE char deferred_bar[SHORT_LEN]; /* deferred \bar (or \endpiece etc.) */ PRIVATE bool Changeclefs = false; /* output \Changeclefs after \def\atnextbar */ PRIVATE int beaming[MAX_STAFFS]; /* spacing for beamed notes */ PRIVATE int new_beaming; PRIVATE int semiauto_beam_notes[MAX_STAFFS]; /* semi-automatic beam notes */ /* save-restore state for a staff; used in process_xtuplet */ PRIVATE int beamingi; PRIVATE char *currenti; PRIVATE int cspacingi; PRIVATE int vspacingi; PRIVATE char collectivei[SHORT_LEN]; PRIVATE bool first_collectivei; PRIVATE int xtuplet[MAX_STAFFS]; /* x for xtuplet in staff i */ PRIVATE bool appoggiatura; PRIVATE bool dottedbeamnotes = false; /* dotted beam notes ignored? */ PRIVATE bool bar_rest[MAX_STAFFS]; PRIVATE char outstrings[MAX_STAFFS][LINE_LEN]; /* accumulate commands to be output */ PRIVATE char *n_outstrings[MAX_STAFFS]; PRIVATE int global_skip; /* = 1, 2, 3, or 4 for (non-standard) commands \QQsk \HQsk \TQsk \Qsk */ /* = 5 for five commas and double-flat accidental spacing */ /* = 6 for six commas */ PRIVATE char global_skip_str[7][16] = { "","\\qqsk","\\hqsk","\\tqsk","\\qsk","\\qsk\\qqsk","\\qsk\\hqsk" }; /* all based on \elemskip, not \noteskip */ PRIVATE char TransformNotes2[SHORT_LEN] = {'\0'}; /* 2nd argument of \TransformNotes */ PRIVATE bool TransformNotesDefined = false; PRIVATE void usage () { printf ("Usage: autosp [-v | --version | -h | --help]\n"); printf (" autosp [-d | --dotted] [-l | --log] infile[.aspc] [outfile[.tex]]\n"); printf (" autosp [-d | --dotted] [-l | --log] infile.tex outfile[.tex]\n"); } PRIVATE char *ps(int spacing) /* convert spacing value to note value(s) */ { char *s = malloc(SHORT_LEN); char *s_n; if (s == NULL) error ("Not enough memory"); s_n = s; *s = '\0'; if (spacing == MAX_SPACING) sprintf(s, "MAX_SPACING"); else if (spacing == 0) sprintf(s, "0"); else { int n = 1; while (true) { if (spacing >= SMALL_NOTE/n) { s_n = s_n + sprintf (s_n, "SP(%d)", n); spacing = spacing - SMALL_NOTE/n; if (spacing > 0) s_n = s_n + sprintf (s_n, "+"); else break; } n = 2*n; } } return s; } PRIVATE void note_segment (char *s) { char *t; t = strpbrk (s, "|&$"); if (t == NULL) t = s + strlen (s); while (s < t) { fputc (*s, logfile); s++; } fputc ('\n', logfile); } PRIVATE void status (int i) { if (active[i]) { fprintf (logfile, "notes[%d]=", i); note_segment (notes[i]); fprintf (logfile, "current[%d]=", i); note_segment (current[i]); fprintf (logfile, "spacings[%d]=%s\n", i, ps(spacings[i])); fprintf (logfile, "vspacing[%d]=%s\n", i, ps(vspacing[i])); fprintf (logfile, "outstrings[%d]=%s\n", i, outstrings[i]); if (xtuplet[i] > 1) fprintf (logfile, "xtuplet[%d]=%d\n", i, xtuplet[i]); } } PRIVATE void status_spacing (void) { fprintf (logfile, "spacing=%s\n", ps(spacing)); fprintf (logfile, "old_spacing=%s\n", ps(old_spacing)); } PRIVATE void status_all (void) { int i; for (i=1; i <= nstaffs; i++) if (active[i]) status (i); status_spacing (); fprintf (logfile, "nastaffs=%d\n", nastaffs); if (dottedbeamnotes) fprintf (logfile, "dottedbeamnotes == true\n"); } PRIVATE void status_collective (int i) { if (active[i]) { fprintf (logfile, "cspacing[%d]=%s\n", i, ps(cspacing[i])); fprintf (logfile, "collective[%d]=%s\n", i, collective[i]); fprintf (logfile, "first_collective[%d]=%d\n", i, first_collective[i]); } } PRIVATE void status_beam (int i) { if (active[i]) { fprintf (logfile, "beaming[%d]=%s\n", i, ps(beaming[i])); } } PRIVATE void analyze_notes (char **ln) /* divide material from *ln to \en into notes[i] segments and initialize terminator[i] etc. */ { int i; char *s; char *t; int newlines = 0; s = *ln+1; /* skip "/" */ while (isalpha(*s)) {s++;} /* skip rest of the initial command */ while (true) { /* look for \en */ t = strstr(s, "\\en"); if (t != NULL) break; /* replace EOL by a blank and append another line of input */ { char new_line[LINE_LEN]; char *nl; if (fgets (new_line, LINE_LEN, infile) == NULL) error ("Unexpected EOF."); nl = new_line; while (*nl == ' ') nl++; /* avoid spaces */ t = strpbrk (s, "\n%"); if (t == NULL) error ("Missing EOL."); if (*t == '\n') { *t = ' '; t++;} *t = '\0'; if (append (line, &t, nl, LINE_LEN) >= LINE_LEN) error ("Line too long."); newlines++; } } /* t -> "\en" */ if ( prefix("\\enotes", t) ) *ln = t+7; else *ln = t+3; *t = '$'; /* replace '\' by '$' (a distinctive terminating character) */ for (i=1; i <= nstaffs; i++) { char *tt; while (*s == ' ') s++; /* skip spaces */ if (active[i]) { notes[i] = s; current[i] = s; } tt = strpbrk (s, "|&$"); if (tt == NULL) error ("can't parse note-spacing command."); s = tt; terminator[i] = *s; if (*s != '$') s++; } lineno = lineno + newlines; /* initialize: */ spacing = MAX_SPACING; old_spacing = MAX_SPACING; appoggiatura = false; global_skip = 0; for (i=1; i <= nstaffs; i++) { if (active[i]) { n_outstrings[i] = outstrings[i]; *n_outstrings[i] = '\0'; vspacing[i] = 0; vspacing_active[i] = false; collective[i][0] = '\0'; cspacing[i] = MAX_SPACING; first_collective[i] = false; xtuplet[i] = 1; } } if (debug) { fprintf (logfile, "\nAfter analyze_notes:\n"); status_all (); } } PRIVATE void checkc (char *s, char c) { if (*s != c) { printf ("Error on line %d: Expected %c but found %d:\n%s\n", lineno, c, *s, line); exit (EXIT_FAILURE); } } PRIVATE void checkn (char *s) { if (strpbrk (s, "0123456789") != s) { printf ("Error on line %d: Expected digit but found %c:\n%s\n", lineno, *s, line); exit (EXIT_FAILURE); } } PRIVATE void filter_output (int i) { /* discard \sk \bsk \Qsk \TQsk \HQsk \QQsk and \Cpause */ char *s = notes[i]; while (s < current[i]) { char *t; t = strpbrk (s+1, "\\&|$"); if (t == NULL || t > current[i]) t = current[i]; if (!prefix ("\\sk", s) && !prefix ("\\bsk", s) && !prefix ("\\Cpause", s) && !prefix ("\\Qsk", s) && !prefix ("\\HQsk", s) && !prefix ("\\TQsk", s) && !prefix ("\\QQsk", s) && !prefix ("\\Triolet", s) && !prefix ("\\Xtuplet", s) ) { while (s < t) { while (*s == ',') s++; /* global skips */ *n_outstrings[i] = *s; n_outstrings[i]++; s++; } *(n_outstrings[i]) = '\0'; } s = t; } notes[i] = s; } PRIVATE void output_notes (int i) /* append from notes[i] up to current[i] to outstrings[i] */ { if (debug) { fprintf (logfile, "\nEntering output_notes:\n"); status (i); } if (debug) { fprintf (logfile, "\nAppending %s for global_skip=%i\n", global_skip_str[global_skip], global_skip); } append (outstrings[i], &(n_outstrings[i]), global_skip_str[global_skip], LINE_LEN); if (vspacing[i] > 0) { if (nonvirtual_notes) append (outstrings[i], &(n_outstrings[i]), "\\sk", LINE_LEN); vspacing[i] = vspacing[i] - spacing; } else if (collective[i][0]) { if (debug) { fprintf (logfile, "\nBefore outputting collective note:\n"); status (i); status_collective (i); } if (!first_collective[i]) append (outstrings[i], &(n_outstrings[i]), collective[i], LINE_LEN); first_collective[i] = false; filter_output (i); append (outstrings[i], &(n_outstrings[i]), "}", LINE_LEN); if (*notes[i] == '}') { collective[i][0] = '\0'; cspacing[i] = 0; notes[i]++; } if (*notes[i] == '}') /* close of {\tinynotesize..{}}? */ { append (outstrings[i], &(n_outstrings[i]), "}", LINE_LEN); notes[i]++; } if (debug) { fprintf (logfile, "\nAfter outputting collective note:\n"); status (i); status_collective (i); } } else filter_output (i); if (debug) { fprintf (logfile, "\nAfter output_notes:\n"); status (i); status_spacing (); } } PRIVATE void pseudo_output_notes (int i) { /* process notes like output_notes but without outputting */ if (vspacing[i] > 0) vspacing[i] = vspacing[i] - spacing; else if (collective[i][0]) { first_collective[i] = false; if (*current[i] == '}') { collective[i][0] = '\0'; current[i]++; } } } PRIVATE void update_global_skip (int n) /* global skips may be used in more than one staff */ { if (n>6) { warning ("Too many commas."); n = 6; } if (global_skip < n) global_skip = n; } PRIVATE int collective_note (int i) { char *s = current[i]; int spacing = cspacing[i]; if (debug) { fprintf (logfile, "\nEntering collective_note:\n"); status (i); status_collective (i); } while (true) /* search for alphabetic or numeric note (or asterisk) */ { int n = 0; while (*s == ',') { n++; s++; } /* global skips */ update_global_skip (n); /* commas will be discarded by filter_output (i) */ if (*s == '.' && new_beaming == 0 && !dottedbeamnotes) spacing = (int)(spacing * 1.50); else if ( (*s == '^' || *s == '_' || *s == '=' || *s == '>') && !vspacing_active[i] /* is additional spacing needed? */ ) /* leave space for normal accidentals */ { update_global_skip (3) ; if (debug) { fprintf (logfile, "\nLeave space for accidental\n"); status (i); status_beam (i); status_collective (i); } } else if ( (*s == '<') /* double-flat */ && !vspacing_active[i] /* is additional spacing needed? */ ) { update_global_skip (5); if (debug) { fprintf (logfile, "\nLeave space for double-flat\n"); status (i); status_beam (i); status_collective (i); } } else if (isalnum (*s) || *s == '*') { s++; while (*s == '\'' || *s == '`' || *s == '!') /* transposition characters */ s++; current[i] = s; if (debug) { fprintf (logfile, "\nAfter collective_note:\n"); status (i); status_collective (i); } vspacing_active[i] = false; return spacing; } s++; } } PRIVATE void beam_initiation (char *s, int i) { if ( prefix ("\\ibbbbbb", s) || prefix ("\\Ibbbbbb", s) || prefix ("\\nbbbbbb", s) ) beaming[i] = SP(256); else if ( prefix ("\\ibbbbb", s) || prefix ("\\Ibbbbb", s) || prefix ("\\nbbbbb", s) ) beaming[i] = SP(128); else if ( prefix ("\\ibbbb", s) || prefix ("\\Ibbbb", s) || prefix ("\\nbbbb", s) ) beaming[i] = SP(64); else if ( prefix ("\\ibbb", s) || prefix ("\\Ibbb", s) || prefix ("\\nbbb", s) ) beaming[i] = SP(32); else if ( prefix ("\\ibb", s) || prefix ("\\Ibb", s) || prefix ("\\nbb", s) ) beaming[i] = SP(16); else if ( prefix ("\\ib", s) || prefix ("\\Ib", s) ) beaming[i] = SP(8); if (debug) { fprintf (logfile, "\nAfter beam_initiation:\n"); status (i); status_beam (i); } } PRIVATE void beam_termination (char *s, int i) { if (debug) { fprintf (logfile, "\nEntering beam_termination:\n"); status (i); status_beam (i); } if ( prefix ("\\tbbbbbb", s) ) { new_beaming = SP(128); if (beaming[i] > SP(256)) beaming[i] = SP(256); } else if ( prefix ("\\tbbbbb", s) ) { new_beaming = SP(64); if (beaming[i] > SP(128)) beaming[i] = SP(128); } else if ( prefix ("\\tbbbb", s) ) { new_beaming = SP(32); if (beaming[i] > SP(64)) beaming[i] = SP(64); } else if ( prefix ("\\tbbb", s) ) { new_beaming = SP(16); if (beaming[i] > SP(32)) beaming[i] = SP(32); } else if ( prefix ("\\tbb", s) ) { new_beaming = SP(8); if (beaming[i] > SP(16)) beaming[i] = SP(16); } else if ( prefix ("\\tb", s) ) { new_beaming = 0; } if (debug) { fprintf (logfile, "\nAfter beam_termination:\n"); status (i); status_beam (i); } } PRIVATE char *skip_balanced_text (char *s) { char *t; do { t = strpbrk (s, "{}"); if (t == NULL) error ("Expected '}'"); if (*t == '{') t = skip_balanced_text (t+1); } while (*t != '}'); return t+1; } PRIVATE char *skip_arg (char *s) { while (*s == ' ') s++; if (*s == '{') { s++; skip_balanced_text (s); } else s++; return s; } PRIVATE int spacing_note (int i) /* Search from current[i] for the next spacing note in the ith staff. * Returns the spacing value and updates current[i]. */ { int spacing = MAX_SPACING; char *s, *t; bool tinynotesize = false; bool doubledotted = false; bool dotted = false; new_beaming = 0; if (!active[i]) return spacing; if (vspacing[i] > 0) { /* virtual note (i.e., a skip) */ return vspacing[i]; } if (collective[i][0]) { nonvirtual_notes = true; return collective_note (i); } if (semiauto_beam_notes[i] > 0) { if (debug) fprintf (logfile, "\nsemiauto_beam_notes reduced\n"); semiauto_beam_notes[i]--; current[i]++; if (debug) status (i); nonvirtual_notes = true; return beaming[i]; } s = strpbrk (current[i], "\\&|$"); if (*s != '\\') { return spacing; } if (debug) { fprintf (logfile, "\nIn spacing_note:\n"); status (i); } while (true) { if ( prefix ("\\wh", s) || prefix ("\\breve", s) || prefix ("\\pause", s) || prefix ("\\wq", s) ) { spacing = SP(1); break; } if ( prefix ("\\ha", s) || (prefix ("\\hl", s) && !prefix ( "\\hloff", s) ) || prefix ("\\hu", s) || prefix ("\\hp", s) || prefix ("\\hpause", s) ) { spacing = SP(2); break;} if ( prefix ("\\qa", s) || prefix ("\\ql", s) || prefix ("\\qu", s) || prefix ("\\qp", s) ) { spacing = SP(4); break; } if ( ( prefix ("\\ca", s) || prefix ("\\cl", s) || prefix ("\\cu", s) ) && !prefix ("\\caesura", s ) ) { spacing = SP(8); break; } if ( prefix ("\\ds", s ) && !prefix ("\\dsh", s) ) { spacing = SP(8); break; } if ( prefix ("\\cccc", s) ) { spacing = SP(64); break; } if ( prefix ("\\ccc", s) ) { spacing = SP(32); break; } if ( prefix ("\\cc", s) && !prefix ("\\ccn", s) && !prefix ("\\cchar", s) ) { spacing = SP(16); break; } if ( prefix ("\\qs", s) && !prefix ("\\qsk", s) && !prefix ("\\qspace", s) ) { spacing = SP(16); break; } if ( prefix ("\\hs", s) && !prefix ("\\hsk", s) && !prefix ("\\hsp", s) ) { spacing = SP(32); break;} if ( prefix ("\\qqs", s ) && !prefix ("\\qqsk", s) ) { spacing = SP(64); break; } if ( prefix ("\\qb", s)) /* beam note */ { spacing = beaming[i]; if (new_beaming != 0) /* set by preceding \tb... */ { beaming[i] = new_beaming; new_beaming = 0; } if (debug) { fprintf (logfile, "\nBeam note:\n"); status (i); status_beam (i); } break; } if ( prefix ("\\Cpause", s) ) { /* bar-centered rest */ bar_rest[i] = true; spacing = MAX_SPACING; break; } if (prefix("\\tqqq", s) || prefix("\\nqqq", s) ) { if (beaming[i] > SP(32)) beaming[i] = SP(32); spacing = beaming[i]; beaming[i] = SP(16); new_beaming = 0; if (debug) { fprintf (logfile, "\nAfter beam completion:\n"); status (i); status_beam (i); } break; } if (prefix("\\tqq", s) || prefix("\\nqq", s) ) { if (beaming[i] > SP(16)) beaming[i] = SP(16); spacing = beaming[i]; beaming[i] = SP(8); new_beaming = 0; if (debug) { fprintf (logfile, "\nAfter beam completion:\n"); status (i); status_beam (i); } break; } if ( prefix("\\tq", s) && !prefix("\\tqsk", s) ) { spacing = beaming[i]; new_beaming = 0; if (debug) { fprintf (logfile, "\nAfter beam completion:\n"); status (i); status_beam (i); } break; } /* non-spacing commands: */ if ( prefix ("\\ib", s) || prefix ("\\Ib", s) || prefix ("\\nb", s) ) beam_initiation (s, i); else if ( prefix("\\tb", s) ) beam_termination (s, i); else if ( prefix("\\ztq", s) ) /* non-spacing beam termination */ new_beaming = 0; else if ( prefix("\\xtuplet", s) || prefix("\\xxtuplet", s)|| prefix("\\Xtuplet", s) ) { char *t = s+1; while (!isdigit(*t)) t++; xtuplet[i] = atoi(t); } else if ( prefix("\\triolet", s) || prefix("\\Triolet", s) || prefix("\\uptrio", s) || prefix("\\downtrio", s) || prefix("\\uptuplet", s) || prefix("\\downtuplet", s) ) { xtuplet[i] = 3; } else if ( prefix("\\zchar", s) || prefix("\\lchar", s) || prefix("\\cchar", s) || prefix("\\zcn", s) || prefix("\\lcn", s) || prefix("\\ccn", s) ) { /* need to skip two arguments, possibly with embedded commands */ char *t; t = strpbrk (s+1, " {"); if (t == NULL) error ("Argument expected"); t = skip_arg (t); t = skip_arg (t); } else if ( prefix("\\tinynotesize", s) ) { tinynotesize = true; appoggiatura = true; } else if (prefix("\\ppt", s) || prefix("\\pppt", s) ) { doubledotted = true; /* triple-dotted spaced as double-dotted */ } else if (prefix("\\pt", s) && !prefix("\\ptr", s)) { dotted = true; } else if ( prefix ("\\Dqbb", s) ) { semiauto_beam_notes[i] = 1; beaming[i] = SP(16); current[i] = current[i] + 8; nonvirtual_notes = true; return beaming[i]; } else if ( prefix ("\\Dqb", s) ) { if (debug) fprintf (logfile, "\nFound \\Dqb\n"); semiauto_beam_notes[i] = 1; beaming[i] = SP(8); current[i] = current[i] + 7; nonvirtual_notes = true; if (debug) status (i); return beaming[i]; } else if ( prefix ("\\Tqbb", s) ) { semiauto_beam_notes[i] = 2; beaming[i] = SP(16); current[i] = current[i] + 8; nonvirtual_notes = true; return beaming[i]; } else if ( prefix ("\\Tqb", s) ) { semiauto_beam_notes[i] = 2; beaming[i] = SP(8); current[i] = current[i] + 7; nonvirtual_notes = true; return beaming[i]; } else if ( prefix ("\\Qqbb", s) ) { semiauto_beam_notes[i] = 3; beaming[i] = SP(16); current[i] = current[i] + 8; nonvirtual_notes = true; return beaming[i]; } else if ( prefix ("\\Qqb", s) ) { semiauto_beam_notes[i] = 3; beaming[i] = SP(8); current[i] = current[i] + 7; nonvirtual_notes = true; return beaming[i]; } else if (prefix ("\\rlap", s) ) /* skip the argument, as it must be regarded as non-spacing */ { char *t; t = strpbrk (s+1, " {\\"); if (t == NULL) error ("Argument expected"); if (*t == '\\') s = t+1; else s = skip_arg(t); } else if (prefix ("\\Hsk", s) ) update_global_skip (5); else if (prefix ("\\Qsk", s) ) update_global_skip (4); else if (prefix ("\\TQsk", s) ) update_global_skip (3); else if (prefix ("\\HQsk", s) ) update_global_skip (2); else if (prefix ("\\QQsk", s) ) update_global_skip (1); /* Command is non-spacing. */ /* Skip ahead to the next command. */ s = strpbrk (s+1, "\\&|$"); if ( *s != '\\') { current[i] = s; if (debug) { fprintf (logfile, "\nAfter spacing_note:\n"); status (i); } return spacing; } } /* end of while (true) loop */ if (spacing < MAX_SPACING) nonvirtual_notes = true; if (prefix ("\\whpp", s) || prefix ("\\hupp", s) || prefix ("\\hlpp", s) || prefix ("\\happ", s) || prefix ("\\hspp", s) || prefix ("\\hppp", s) || prefix ("\\hpausepp", s) || prefix ("\\qupp", s) || prefix ("\\qlpp", s) || prefix ("\\qapp", s) || prefix ("\\qspp", s) || prefix ("\\qppp", s) || prefix ("\\qqspp", s) || prefix ("\\cupp", s) || prefix ("\\clpp", s) || prefix ("\\ccupp", s) || prefix ("\\cclpp", s) || prefix ("\\cccupp", s) || prefix ("\\ccclpp", s) || prefix ("\\qbpp", s) || prefix ("\\dspp", s) || doubledotted ) { spacing = (int)(spacing * 1.75); doubledotted = false;} else if (prefix ("\\whp", s) || prefix ("\\hup", s) || prefix ("\\hlp", s) || prefix ("\\hap", s) || prefix ("\\hsp", s) || prefix ("\\hpp", s) || prefix ("\\hpausep", s) || prefix ("\\qup", s) || prefix ("\\qlp", s) || prefix ("\\qap", s) || prefix ("\\qsp", s) || prefix ("\\qqsp", s) || prefix ("\\qpp", s) || prefix ("\\cup", s) || prefix ("\\clp", s) || prefix ("\\ccup", s) || prefix ("\\cclp", s) || prefix ("\\cccup", s) || prefix ("\\ccclp", s) || prefix ("\\qbp", s) || prefix ("\\dsp", s) || dotted ) { spacing = (int)(spacing * 1.5); dotted = false; } t = strpbrk (s+1, "{\\&|$"); /* collective coding? */ if (*t == '{') /* {...} */ { /* Save prefix in collective[i]. * It will be output for every note in the collective */ char *ss = s; char *tt = collective[i]; while (ss <= t) { *tt = *ss; tt++, ss++; } *tt = '\0'; current[i] = t+1; if (tinynotesize) spacing = APPOGG_SPACING; cspacing[i] = spacing; first_collective[i] = true; return collective_note (i); } if (tinynotesize) spacing = APPOGG_SPACING; current[i] = strpbrk (s+1, "\\&|$"); if (debug) { fprintf (logfile, "\nAfter spacing_note:\n"); status (i); } vspacing_active[i] = false; return spacing; } PRIVATE void output_rests (void) { /* outputs a multi-bar rest and the deferred_bar command */ int i; fprintf ( outfile, "\\NOTes\\sk\\en%%\n" ); fprintf (outfile, "\\def\\atnextbar{\\znotes"); if (restbars == 1) fprintf ( outfile, "\\centerpause" ); else fprintf ( outfile, "\\centerHpause{%d}", restbars ); fprintf (outfile, "\\en}%%\n"); if (Changeclefs) /* \Changeclefs has to be output after \def\atnextbar... */ { fprintf ( outfile, "\\Changeclefs%%\n"); Changeclefs = false; } if (restbars > 1) fprintf ( outfile, "\\advance\\barno%d%%\n", restbars-1 ); restbars = 0; for (i=1; i <= nstaffs; i++) bar_rest[i] = false; fprintf ( outfile, "%s%%\n", deferred_bar); deferred_bar[0] = '\0'; } void initialize_notes () { int i; if (debug) { fprintf (logfile, "\nEntering initialize_notes\n"); status_all (); } if ( nastaffs == 1 && spacing != MAX_SPACING && restbars > 0) output_rests (); if (spacing == MAX_SPACING) fprintf (outfile, "\\znotes"); else if (spacing == SP(1)+SP(2) || spacing == SP(1)+SP(2)+SP(4)) fprintf (outfile, NOTEsp); else if (spacing == SP(1)) fprintf (outfile, "\\NOTEs"); else if (spacing == SP(2)+SP(4)+SP(8)) fprintf (outfile, "%s", NOTespp); else if (spacing == SP(2)+SP(4) ) fprintf (outfile, "\\NOTesp"); else if (spacing == SP(2)) fprintf (outfile, "\\NOTes"); else if (spacing == SP(4)+SP(8) ) fprintf (outfile, "\\NOtesp"); else if (spacing == SP(4)+SP(8)+SP(16) ) fprintf (outfile, "%s", NOtespp); else if (spacing == SP(4)) fprintf (outfile, "\\NOtes"); else if (spacing == SP(8)+SP(16) ) fprintf (outfile, "\\Notesp"); else if (spacing == SP(8)+SP(16)+SP(32)) fprintf (outfile, "%s", Notespp); else if (spacing == SP(8)) fprintf (outfile, "\\Notes"); else if (spacing == SP(16)+SP(32)+SP(64)) fprintf (outfile, notespp); else if (spacing == SP(16)+SP(32) ) fprintf (outfile, "\\notesp"); else if (spacing == SP(16) || spacing == SP(32)+SP(64) || spacing == SP(32)+SP(64)+SP(128)) fprintf (outfile, "\\notes"); else if (spacing == SP(32) || spacing == SP(64)+SP(128) || spacing == SP(64)+SP(128)+SP(256)) fprintf (outfile, "\\nnotes"); else if (spacing == SP(64) ) fprintf (outfile, "\\nnnotes"); else if (spacing == APPOGG_SPACING) fprintf (outfile, "%s", APPOGG_NOTES); else { printf ("Error on line %d: spacing %s not recognized.\n", lineno, ps(spacing)); exit (EXIT_FAILURE); } if (debug) { fprintf (logfile, "\noutputting \\Notes command for spacing=%s.\n", ps(spacing)); status_all(); } for (i=1; i <= nstaffs; i++) { if (active[i]) { n_outstrings[i] = outstrings[i]; *n_outstrings[i] = '\0'; } } } PRIVATE void terminate_notes () { int i; char *s, *t; if (debug) { fprintf (logfile, "\nEntering terminate_notes:\n"); status_all (); } t = TransformNotes2; while (true) { s = strchr (t, '#'); if (s == NULL) break; while (t < s) /* output any initial \transpose etc. */ { putc (*t, outfile); t++; } t++; /* skip # */ i = atoi (t) -1; t++; if (spacing == MAX_SPACING) { /* output any commands left in notes[i] */ filter_output (i); } fprintf (outfile, "%s", outstrings[i]); if (debug) fprintf (logfile, "\noutputting %s from outstrings[%d].\n", outstrings[i], i); outstrings[i][0] = '\0'; n_outstrings[i] = outstrings[i]; if (spacing < MAX_SPACING && spacing > 2 * old_spacing) { /* add extra space *before* much longer notes */ fprintf (outfile, "\\hqsk"); if (debug) fprintf (logfile, "\nExtra half-notehead space before longer notes.\n"); } if (*t != '\0') { putc (*t, outfile); t++; } /* terminator */ } if (spacing == MAX_SPACING && old_spacing < SP(8) ) { /* add extra space before \en */ fprintf (outfile, "\\hqsk"); if (debug) fprintf (logfile, "\nExtra half-notehead space before \\en.\n"); } fprintf (outfile, "\\en"); if (debug) { fprintf (logfile, "\noutputting \\en\n"); fprintf (logfile, "\nAfter terminate_notes:\n"); status_all (); } } PRIVATE void process_appogg (void) { int i; if (debug) { fprintf (logfile, "\nEntering process_appogg:\n"); status_all(); } if (old_spacing < MAX_SPACING) { char *s, *t; if (debug) fprintf (logfile, "Terminate current notes command:\n"); t = TransformNotes2; while (true) { s = strchr (t, '#'); if (s == NULL) break; while (t < s) /* output any initial \transpose etc. */ { putc (*t, outfile); t++; } t++; /* skip # */ i = atoi (t) - 1; t++; fprintf (outfile, "%s", outstrings[i]); if (debug) fprintf (logfile, "\noutputting %s from outstrings[%d].\n", outstrings[i], i); outstrings[i][0] = '\0'; n_outstrings[i] = outstrings[i]; if (*t != '\0') { putc (*t, outfile); t++; } /* terminator */ } fprintf (outfile, "\\en"); if (debug) { fprintf (logfile, "\\en"); fprintf (logfile, "\nAfter termination of notes command:\n"); status_all (); } } if (debug) fprintf (logfile, "\nCreate a new notes command for the appoggiatura:\n"); spacing = APPOGG_SPACING; initialize_notes (); spacing = MAX_SPACING; for (i=1; i <= nstaffs; i++) if (active[i]) { if (spacings[i] == APPOGG_SPACING ) { output_notes (i); spacings[i] = spacing_note (i); } if (spacings[i] < spacing) spacing = spacings[i]; } appoggiatura = false; nonvirtual_notes = true; old_spacing = APPOGG_SPACING; if (debug) { fprintf (logfile, "\nAfter process_appogg:\n"); status_all (); } return; } PRIVATE void save_state (int i) /* used in process_xtuplet */ { beamingi = beaming[i]; currenti = current[i]; cspacingi = cspacing[i]; vspacingi = vspacing[i]; first_collectivei = first_collective[i]; collectivei[0] = '\0'; append (collectivei, NULL, collective[i], SHORT_LEN); } PRIVATE void restore_state (int i) /* used in process_xtuplet */ { beaming[i] = beamingi; current[i] = currenti; cspacing[i] = cspacingi; vspacing[i] = vspacingi; first_collective[i] = first_collectivei; collective[i][0] = '\0'; append (collective[i], NULL, collectivei, SHORT_LEN); } PRIVATE void process_xtuplet (void) { int i, xi=0; int xspacing = MAX_SPACING; /* xtuplet total spacing */ int normalized_xspacing; /* (xspacing / xtuplet) * (xtuplet - 1); */ int xsp; double multnoteskip; if (debug) { fprintf (logfile, "\nEntering process_xtuplet:\n"); for (i=1; i <= nstaffs; i++) fprintf (logfile, "i=%d xtuplet[i]=%d\n", i, xtuplet[i]); } if (old_spacing < MAX_SPACING) { char *s, *t; if (debug) fprintf (logfile, "Terminate current notes command:\n"); t = TransformNotes2; while (true) { s = strchr (t, '#'); if (s == NULL) break; while (t < s) /* output any initial \transpose etc. */ { putc (*t, outfile); t++; } t++; /* skip # */ i = atoi (t) - 1; t++; fprintf (outfile, "%s", outstrings[i]); if (debug) fprintf (logfile, "\noutputting %s from outstrings[%d].\n", outstrings[i], i); outstrings[i][0] = '\0'; n_outstrings[i] = outstrings[i]; if (*t != '\0') { putc (*t, outfile); t++; } /* terminator */ } fprintf (outfile, "\\en"); if (debug) { fprintf (logfile, "\\en"); fprintf (logfile, "\nAfter termination of notes command:\n"); status_all (); } } if (debug) { fprintf (logfile, "\nDetermine xtuplet duration:\n"); fflush (logfile); } for (i=1; i <= nstaffs; i++) if (xtuplet[i] > 1) { xi = i; save_state (xi); pseudo_output_notes (xi); xspacing = spacings[xi]; while (xspacing % xtuplet[xi] != 0) { xspacing += spacing_note (xi); pseudo_output_notes (xi); if (xspacing >= MAX_SPACING) error ("Can't determine xtuplet duration."); } restore_state (xi); break; } if (debug) fprintf (logfile, "\nxspacing=%s\n", ps(xspacing)); if (debug) fprintf (logfile, "xi=%d xtuplet[xi]=%d\n", xi, xtuplet[xi]); normalized_xspacing = (xspacing / xtuplet[xi]) * (xtuplet[xi] - 1); if (debug) { fprintf (logfile, "normalized_xspacing=%s\n", ps(normalized_xspacing)); } spacing = xspacing / xtuplet[xi]; if (debug) { fprintf (logfile, "\nDetermine minimal spacing over all active staffs:\n"); } for (i=1; i <= nstaffs; i++) if (active[i] && xtuplet[i] == 1) { save_state (i); pseudo_output_notes (i); xsp = spacings[i]; if (spacings[i] < spacing) spacing = spacings[i]; while (xsp < normalized_xspacing) { int spi = spacing_note (i); pseudo_output_notes (i); xsp += spi; if (spi < spacing) { spacing = spi; if (debug) fprintf (logfile, "for i=%d spi=%s\n", i, ps(spi)); } } restore_state (i); } if (debug) fprintf (logfile, "\nxspacing=%s\n", ps(spacing)); if (debug) fprintf (logfile, "\nCreate a new notes command for the xtuplet:\n"); initialize_notes (); if (debug) fprintf (logfile, "\nProcess non-xtuplet staffs:\n"); for (i=1; i <= nstaffs; i++) vspacing[i] = 0; xsp = 0; while (true) { for (i=1; i <= nstaffs; i++) if (active[i] && xtuplet[i] == 1) output_notes (i); xsp += spacing; for (i=1; i <= nstaffs; i++) { /* virtual notes needed? */ if (active[i] && xtuplet[i] == 1 && spacings[i] != 0 && spacings[i] != spacing && vspacing[i] == 0) { vspacing[i] = spacings[i] - spacing; } } if (xsp >= normalized_xspacing) break; global_skip = 0; for (i=1; i <= nstaffs; i++) if (active[i] && xtuplet[i] == 1) spacings[i] = spacing_note(i); } if (debug) { fprintf (logfile, "\nAfter processing non-xtuplet staffs:\n"); status_all (); } if (debug) { fprintf (logfile, "Generate \\multnoteskip factors.\n"); fprintf (logfile, "xi=%d xtuplet[xi]=%d\n", xi, xtuplet[xi]); } multnoteskip = (double) (xtuplet[xi] - 1) / xtuplet[xi]; for (i=1; i <= nstaffs; i++) if (xtuplet[i] > 1) { n_outstrings[i] += sprintf (n_outstrings[i], "\\multnoteskip{%5.3f}", multnoteskip); if (debug) { fprintf (logfile, "\noutstrings[%d]=", i); note_segment (outstrings[i]); } } if (debug) fprintf (logfile, "\nRe-process xtuplet staffs:\n"); for (i=1; i <= nstaffs; i++) if (xtuplet[i] > 1) { xsp = 0; while (true) { output_notes (i); xsp += spacing; if (spacings[i] != spacing && vspacing[i] == 0) vspacing[i] = spacings[i] - spacing; if (xsp >= xspacing) break; global_skip = 0; spacings[i] = spacing_note (i); } } /* Restore normal \noteskip in the xtuplet staffs. */ if (debug) { fprintf (logfile, "Restore \\multnoteskip factors.\n"); fprintf (logfile, "xi=%d xtuplet[xi]=%d\n", xi, xtuplet[xi]); } multnoteskip = (double) xtuplet[xi] / (xtuplet[xi]-1); for (i=1; i <= nstaffs; i++) if (xtuplet[i] > 1) { n_outstrings[i] += sprintf (n_outstrings[i], "\\multnoteskip{%5.3f}", multnoteskip); xtuplet[i] = 1; } nonvirtual_notes = true; if (debug) { fprintf (logfile, "\nAfter process_xtuplet:\n"); status_all (); } return; } PRIVATE void generate_notes () { int i; bool xtuplet_flag; while (true) { old_spacing = spacing; spacing = MAX_SPACING; global_skip = 0; nonvirtual_notes = false; if (debug) { fprintf (logfile, "\nIn generate_notes:\n"); status_all (); } for (i=1; i <= nstaffs; i++) if (active[i]) { spacings[i] = spacing_note (i); if (spacings[i] < spacing) spacing = spacings[i]; } if (appoggiatura) { process_appogg (); } xtuplet_flag = false; for (i=1; i <= nstaffs; i++) if (xtuplet[i] > 1) xtuplet_flag = true; if (xtuplet_flag) { process_xtuplet (); continue; } if (spacing != old_spacing || spacing == MAX_SPACING) { if (old_spacing < MAX_SPACING) terminate_notes (); if (spacing == MAX_SPACING || nonvirtual_notes == false) { if (debug) { fprintf (logfile, "\nAfter generate_notes:\n"); status_all (); } return; } if (old_spacing < MAX_SPACING) putc ('\n', outfile); initialize_notes (); } for (i=1; i <= nstaffs; i++) /* append current notes to outstrings */ if (active[i]) output_notes (i); for (i=1; i <= nstaffs; i++) { /* virtual notes needed? */ if (active[i] && spacings[i] != MAX_SPACING && spacings[i] != spacing && vspacing[i] == 0 ) { vspacing[i] = spacings[i]; vspacing_active[i] = true; vspacing[i] = vspacing[i] - spacing; if (debug) { fprintf (logfile, "\nAfter vspacing initialization:\n"); status (i); } } } } } PRIVATE void process_command (char **ln) { char *s, *t; if (debug) { fprintf (logfile, "\nProcessing command:%s\n", *ln); fflush (logfile); } if ( prefix("\\instrumentnumber", *ln) ) { s = strpbrk (*ln, "123456789"); if ( s == NULL ) error ("\\instrumentnumber command unreadable."); ninstr = atoi (s); while (*ln <= s) { putc (**ln, outfile); (*ln)++;} } if ( prefix("\\def\\nbinstrument", *ln) ) { s = strpbrk (*ln, "123456789"); if ( s == NULL ) error ("\\def\\nbinstrument command unreadable."); ninstr = atoi (s); while (*ln <= s) { putc (**ln, outfile); (*ln)++;} } else if ( prefix("\\setstaffs", *ln) ) { /* for now, just record in staffs[i] */ int n, p; s = strpbrk (*ln, "123456789"); if ( s == NULL ) error ("\\setstaffs command unreadable."); n = (int)(*s) - (int)('0'); /* instrument number */ s = strpbrk (s+1, "123456789"); if ( s == NULL ) error ("\\setstaffs command unreadable."); p = (int)(*s) - (int)('0'); /* number of staffs */ staffs[n] = p; while (*ln <= s) { putc (**ln, outfile); (*ln)++;} } else if ( prefix("\\startpiece", *ln) ) { if (!TransformNotesDefined) /* create default TransformNotes2: */ { int i, j; t = TransformNotes2; nstaffs = 1; sprintf (t, "#%1i", nstaffs+1); t = t+2; for (j=2; j <= staffs[1]; j++) { nstaffs++; sprintf (t, "|#%1i", nstaffs+1); t = t+3; active[nstaffs] = true; } for (i=2; i <= ninstr; i++) { nstaffs++; sprintf (t, "&#%1i", nstaffs+1); t = t+3; for (j=2; j <= staffs[i]; j++) { nstaffs++; sprintf (t, "|#%1i", nstaffs+1); t = t+3; active[nstaffs] = true; } } nastaffs = nstaffs; if (debug) fprintf (logfile, "default TransformNotes2=%s\n", TransformNotes2); } if (nstaffs == 1) fprintf (outfile, "\\nostartrule\n"); if (debug) { int j; fprintf (logfile, "ninstr=%d nstaffs=%d nastaffs=%d\n", ninstr, nstaffs, nastaffs); for (j=1; j <= nstaffs; j++) fprintf (logfile, "active[%d]=%d\n", j, active[j]); } fprintf (outfile, "\\startpiece"); t = strpbrk (*ln+1, "\\%\n"); *ln = t; } else if ( prefix("\\startextract", *ln) ) { if (!TransformNotesDefined) /* create default TransformNotes2: */ { int i, j; t = TransformNotes2; nstaffs = 1; sprintf (t, "#%1i", nstaffs+1); t = t+2; for (j=2; j <= staffs[1]; j++) { nstaffs++; sprintf (t, "|#%1i", nstaffs+1); t = t+3; active[nstaffs] = true; } for (i=2; i <= ninstr; i++) { nstaffs++; sprintf (t, "&#%1i", nstaffs+1); t = t+3; for (j=2; j <= staffs[i]; j++) { nstaffs++; sprintf (t, "|#%1i", nstaffs+1); t = t+3; active[nstaffs] = true; } } nastaffs = nstaffs; if (debug) fprintf (logfile, "default TransformNotes2=%s\n", TransformNotes2); } if (nstaffs == 1) fprintf (outfile, "\\nostartrule\n"); fprintf (outfile, "\\startextract"); t = strpbrk (*ln+1, "\\%\n"); *ln = t; } else if ( prefix("\\TransformNotes", *ln) ) { /* determine ninstr, nstaffs, nastaffs, staffs[i], and active[i] */ int i; s = *ln + 16; /* first parameter */ ninstr = 0; nstaffs = 0; while (true) { ninstr++; nstaffs++; staffs[ninstr] = 1; checkc (s, '#'); s++; checkn (s); s++; while (*s == '|') { staffs[ninstr]++; nstaffs++; s++; checkc (s, '#'); s++; checkn (s); s++; } if (*s != '&') break; s++; } checkc (s, '}'); s++; s = strchr (s, '{'); if (s == NULL) error ("Can't parse \\TransformNotes"); s++; /* determine TransformNotes2: */ t = TransformNotes2; do { *t = *s; t++; s++; } while (*s != '}'); *t = '\0'; /* terminate TransformNotes2 */ TransformNotesDefined = true; if (debug) fprintf (logfile, "defined TransformNotes2=%s\n", TransformNotes2); /* determine active staffs: */ for (i=1; i <= nstaffs; i++) active[i] = false; nastaffs = 0; t = strpbrk (TransformNotes2, "#}"); /* may have \transpose etc. before # */ if (t == NULL) error ("Can't parse second argument of \\TransformNotes"); while (*t == '#') { if (sscanf (t, "#%d", &i) != 1) error ("sscanf for argument number fails"); active[i-1] = true; /* parameters start at 2 */ nastaffs++; t = strpbrk (t+1, "#}"); if (t == NULL) break; } if (debug) { int j; fprintf (logfile, "ninstr=%d nstaffs=%d nastaffs=%d\n", ninstr, nstaffs, nastaffs); for (j=1; j <= nstaffs; j++) fprintf (logfile, "active[%d]=%d\n", j, active[j]); } /* output \TransformNotes... as a comment: */ putc ('%', outfile); while (*ln <= s) { putc (**ln, outfile); (*ln)++; } } else if (prefix("\\def\\atnextbar{\\znotes", *ln)) { /* whole-bar or multi-bar rest? */ int i; bool centerpause = false; *ln = *ln + 15; /* skip "\def\atnextbar{" */ analyze_notes(ln); for (i=1; i <= nstaffs; i++) { if (active[i]) { char *t; char *s; t = strpbrk (notes[i], "&|$"); if (t == NULL) t = notes[i] + strlen (notes[i]); s = strstr (notes[i], "\\centerpause"); if (s != NULL && s < t && nastaffs == 1) { bar_rest[i] = true; centerpause = true; } break; } } if (!centerpause) /* generate transformed \znotes ... \en */ { fprintf ( outfile, "\\def\\atnextbar{\\znotes"); t = TransformNotes2; while (true) { /* output TransformNotes2 prefix (\transpose etc.): */ s = strchr (t, '#'); if (s == NULL) break; while (t < s) { putc (*t, outfile); t++; } t++; /* skip '#' */ /* output notes: */ i = atoi (t) - 1; t++; s = notes[i]; while (*s != '&' && *s != '|' && *s != '$') { putc (*s, outfile); s++; } if (*t != '\0') { putc (*t, outfile); t++; } /* output terminator */ } fprintf (outfile, "\\en}%%\n"); } t = strchr (*ln, '}'); if (t == NULL) error ("Can't find }."); *ln = t+1; t = strpbrk (*ln, "%\\"); if (t == NULL ) t = *ln + strlen(*ln); *ln = t; } else if ( prefix("\\def", *ln) ) /* copy to output */ { t = *ln + strlen(*ln); while (*ln < t) { fputc (**ln, outfile); (*ln)++; } } else if ( prefix("\\anotes", *ln) ) { if (debug) { fprintf (logfile, "\nProcessing %s", *ln); fprintf (logfile, "lineno=%d\n", lineno); fflush (logfile); } analyze_notes (ln); if (debug) { fprintf (logfile, "\nBefore generate_notes *ln=%s\n", *ln); fflush (logfile); } generate_notes (); if (debug) { fprintf (logfile, "\nAfter generate_notes *ln=%s\n", *ln); fflush (logfile); } t = strpbrk (*ln, "%\\\n"); if (t == NULL) t = *ln + strlen (*ln); if (*t == '\n') putc ('%', outfile); *ln = t; } else if ( prefix("\\nnnotes", *ln) || prefix("\\nnnotes", *ln) || prefix("\\nnotes", *ln) || prefix("\\notes", *ln) || prefix("\\Notes", *ln) || prefix("\\NOtes", *ln) || prefix("\\NOTes", *ln) || prefix("\\NOTEs", *ln) || prefix("\\znotes", *ln) ) { char *s, *t; int nstaff; if (debug) { fprintf (logfile, "\nProcessing %s", *ln); fprintf (logfile, "lineno=%d\n", lineno); fflush (logfile); } t = *ln+1; while ( isalpha (*t) ) t++; s = *ln; while (s < t) { putc (*s, outfile); s++; } analyze_notes (ln); t = TransformNotes2; while (true) { /* output TransformNotes2 prefix (\transpose etc.): */ s = strchr (t, '#'); if (s == NULL) break; while (t < s) { putc (*t, outfile); t++; } t++; /* skip '#' */ /* output notes: */ nstaff = atoi (t) - 1; t++; s = notes[nstaff]; while (*s != '&' && *s != '|' && *s != '$') { putc (*s, outfile); s++; } if (*t != '\0') { putc (*t, outfile); t++; } /* output terminator */ } fprintf (outfile, "\\en\n"); t = strpbrk (*ln, "%\\\n"); if (t == NULL) t = *ln + strlen (*ln); if (*t == '\n') putc ('%', outfile); *ln = t; } else if (prefix("\\vnotes", *ln) ) { char *s, *t; int nstaff; if (debug) { fprintf (logfile, "\nProcessing %s", *ln); fprintf (logfile, "lineno=%d\n", lineno); fflush (logfile); } s = *ln; t = strchr (*ln+1, '\\'); /* find \elemskip */ *ln = t; t = t + 9; while (s < t) { putc (*s, outfile); s++; } analyze_notes (ln); t = TransformNotes2; while (true) { /* output TransformNotes2 prefix (\transpose etc.): */ s = strchr (t, '#'); if (s == NULL) break; while (t < s) { putc (*t, outfile); t++; } t++; /* skip '#' */ /* output notes: */ nstaff = atoi (t) - 1; t++; s = notes[nstaff]; while (*s != '&' && *s != '|' && *s != '$') { putc (*s, outfile); s++; } if (*t != '\0') { putc (*t, outfile); t++; } /* output terminator */ } fprintf (outfile, "\\en\n"); t = strpbrk (*ln, "%\\\n"); if (t == NULL) t = *ln + strlen (*ln); if (*t == '\n') putc ('%', outfile); *ln = t; } else if ( prefix ("\\bar", *ln) && !prefix ("\\barno", *ln)) { int i; char *s, *t; bool atnextbar = false; for (i=1; i <= nstaffs; i++) if (active[i] && bar_rest[i]) { atnextbar = true; break; } if (nastaffs == 1 && atnextbar) { restbars++; sprintf (deferred_bar, "\\bar"); if (debug) fprintf (logfile, "\nrestbars increased to %d\n", restbars); for (i=1; i<= nstaffs; i++) bar_rest[i] = false; *ln = *ln+4; t = strpbrk (*ln, "\\\n"); if (t == NULL) t = *ln + strlen (*ln); if (*t == '\n') putc ('%', outfile); *ln = t; } else { if (atnextbar) { fprintf (outfile, "\\def\\atnextbar{\\znotes"); t = TransformNotes2; while (true) { s = strchr (t, '#'); if (s == NULL) break; while (t < s) /* output any initial \transpose etc. */ { putc (*t, outfile); t++; } t++; /* skip # */ i = atoi (t) -1; t++; if (active[i]) { if (bar_rest[i]) fprintf (outfile, "\\centerpause"); bar_rest[i] = false; } if (*t != '\0') { putc (*t, outfile); t++; } /* terminator */ } fprintf (outfile, "\\en}%%\n"); } if (Changeclefs) /* \Changeclefs has to be output after \def\atnextbar... */ { fprintf ( outfile, "\\Changeclefs%%\n"); Changeclefs = false; } t = strpbrk (*ln+1, "%\\\n"); while (*ln < t) { fputc (**ln, outfile); (*ln)++; } if (*t == '\n') putc ('%', outfile); *ln = t; } if (debug) { fprintf (logfile, "\nAfter \\bar processing, *ln=%s\n", *ln); fflush (logfile); } } else if ( prefix ("\\endpiece", *ln) || prefix ("\\Endpiece", *ln) || prefix ("\\endextract", *ln) || prefix ("\\stoppiece", *ln) || prefix ("\\raggedstoppiece", *ln) || prefix ("\\rightrepeat", *ln) || prefix ("\\leftrepeat", *ln) || prefix ("\\alaligne", *ln) || prefix ("\\alapage", *ln) || prefix ("\\changecontext", *ln) || prefix ("\\Changecontext", *ln) || prefix ("\\zchangecontext", *ln) || prefix ("\\zalaligne", *ln) || prefix ("\\zalapage", *ln) ) { int i; bool atnextbar = false; for (i=1; i <= nstaffs; i++) if (active[i] && bar_rest[i]) { atnextbar = true; break; } if (nastaffs == 1 && atnextbar) { restbars++; sprintf (deferred_bar, "%s", *ln); output_rests (); t = strpbrk (*ln+1, "%\\\n"); if (*t == '\n') putc ('%', outfile); *ln = t; } else if (atnextbar) { fprintf (outfile, "\\def\\atnextbar{\\znotes"); for (i=1; i <= nstaffs; i++) { if (active[i]) { if (bar_rest[i]) fprintf (outfile, "\\centerpause"); bar_rest[i] = false; } if ( terminator[i] != '$') putc (terminator[i], outfile); } fprintf (outfile, "\\en}%%\n"); if (Changeclefs) /* \Changeclefs has to be output after \def\atnextbar... */ { fprintf ( outfile, "\\Changeclefs%%\n"); Changeclefs = false; } t = strpbrk (*ln+1, "%\\\n"); while (*ln < t) { fputc (**ln, outfile); (*ln)++; } if (*t == '\n') putc ('%', outfile); *ln = t; } else { if (nastaffs == 1 && restbars > 0) output_rests (); t = strpbrk (*ln+1, "%\\\n"); while (*ln < t) { fputc (**ln, outfile); (*ln)++; } if (*t == '\n') putc ('%', outfile); *ln = t; } if (debug) { fprintf (logfile, "\nAfter \\bar processing, *ln=%s\n", *ln); fflush (logfile); } } else if ( prefix ("\\znotes", *ln) ) { if (nastaffs == 1 && restbars > 0) output_rests (); fputs (*ln, outfile); *ln = *ln + strlen(*ln); } else if ( prefix ("\\Changeclefs", *ln) ) { /* defer till after \def\atnextbar ... */ Changeclefs = true; *ln = *ln + strlen(*ln); } else if ( prefix ("\\documentclass", *ln) ) { if ( suffix( ".tex", outfilename) ) { char newoutfilename[SHORT_LEN]; char *newoutfilename_n = newoutfilename; append (newoutfilename, &newoutfilename_n, infilename, sizeof (newoutfilename)); newoutfilename_n -= 4; *newoutfilename_n = '\0'; append (newoutfilename, &newoutfilename_n, "ltx", sizeof (newoutfilename)); if (rename ( outfilename, newoutfilename) > 0) { printf ("Can't rename %s as %s\n", outfilename, newoutfilename); exit (EXIT_FAILURE); } printf ("\\documentclass detected; now writing to %s.", newoutfilename); printf ("\n\n"); } fputs (*ln, outfile); *ln = *ln + strlen(*ln); } else if ( prefix ("\\end ", *ln) || prefix ("\\end%", *ln) || prefix ("\\end{document}", *ln) ) { fprintf (outfile, "%s", *ln); exit(0); } else /* everything else */ { fputs (*ln, outfile); *ln = *ln + strlen(*ln); } } PRIVATE void process_line () { char *ln; ln = &line[0]; while ( *ln != '\0') { while (*ln == ' ') { ln++; fputc (' ', outfile); } if (*ln == '%') { fprintf (outfile, "%s", ln); return; } process_command (&ln); } } PRIVATE void process_score () /* process .tex file */ { int c; int i; lineno = 0; for (i=1; i < MAX_STAFFS; i++) { staffs[i] = 1; active[i] = true; bar_rest[i] = false; } spacing = MAX_SPACING; restbars = 0; c = getc (infile); while ( c != EOF ) { ungetc (c, infile); if (fgets(line, LINE_LEN, infile) == NULL) error ("Unexpected EOF."); lineno++; process_line (); c = getc (infile); } /* c == EOF */ } int main (int argc, char *argv[]) { int c; char today[12]; time_t mytime; # define NOPTS 5 struct option longopts[NOPTS] = { { "help", 0, NULL, 'h'}, { "version", 0, NULL, 'v'}, { "dotted", 0, NULL, 'd'}, { "log", 0, NULL, 'l'}, { NULL, 0, NULL, 0} }; time (&mytime); strftime (today, 11, "%Y-%m-%d", localtime (&mytime) ); printf ("This is autosp, version %s.\n", version); printf ("Copyright (C) 2014-18 R. D. Tennent\n" ); printf ("School of Computing, Queen's University, rdt@cs.queensu.ca\n" ); printf ("License GNU GPL version 2 or later .\n" ); printf ("There is NO WARRANTY, to the extent permitted by law.\n\n" ); c = getopt_long (argc, argv, "hvdl", longopts, NULL); while (c != -1) { switch (c) { case 'h': usage (); printf ("Please report bugs to rdt@cs.queensu.ca.\n" ); exit (0); case 'v': exit (0); case 'd': dottedbeamnotes = true; break; case 'l': debug = true; break; case '?': exit (EXIT_FAILURE); default: printf ("Function getopt returned character code 0%o.\n", (unsigned int) c); exit (EXIT_FAILURE); } c = getopt_long (argc, argv, "hvd", longopts, NULL); } infilename[0] = '\0'; infilename_n = infilename; if (optind < argc) { append (infilename, &infilename_n, argv[optind], sizeof (infilename)); if (!suffix (".aspc", infilename)) append (infilename, &infilename_n, ".aspc", sizeof (infilename)); } else { usage(); exit (EXIT_FAILURE); } infile = fopen (infilename, "r"); if (infile == NULL ) { printf ("Can't open %s\n", infilename); exit (EXIT_FAILURE); } printf ("Reading from %s.", infilename); optind++; outfilename[0] = '\0'; outfilename_n = outfilename; if (optind < argc) /* user-provided outfilename */ { append (outfilename, &outfilename_n, argv[optind], sizeof (outfilename)); if (!suffix (".tex", outfilename)) append (outfilename, &outfilename_n, ".tex", sizeof (outfilename)); } else if (suffix (".aspc", infilename) ) { /* output to infile.tex */ append (outfilename, &outfilename_n, infilename, sizeof (outfilename)); outfilename_n -= 4; *outfilename_n = '\0'; append (outfilename, &outfilename_n, "tex", sizeof (outfilename)); } if (*outfilename == '\0') { printf ("\nPlease provide a filename for the output.\n\n"); exit (EXIT_FAILURE); } else { outfile = fopen (outfilename, "wb"); if (outfile == NULL) { printf ("Can't open %s\n", outfilename); exit (EXIT_FAILURE); } printf (" Writing to %s.", outfilename); } logfilename[0] = '\0'; logfilename_n = logfilename; append (logfilename, &logfilename_n, infilename, sizeof (logfilename)); if (suffix (".aspc", infilename) ) logfilename_n -= 4; else logfilename_n -= 3; *logfilename_n = '\0'; append (logfilename, &logfilename_n, "alog", sizeof (logfilename)); if (debug) { logfile = fopen (logfilename, "wb"); if (logfile == NULL) { printf ("Can't open %s\n", logfilename); exit (EXIT_FAILURE); } printf (" Log file %s.", logfilename); } printf ("\n\n"); fprintf (outfile, "%% Generated by autosp (%s).\n", version); process_score (); return 0; }