--- /dev/null
+
+/* Copyright (c) 1995, 1996, 1997, 1998, 2000, 2001, 2002, 2005 by Arkkra Enterprises */
+/* All rights reserved */
+
+/* utility functions for creating MIDI output from Mup input. These functions
+ * are split out into this file to keep midi.c from being so huge */
+
+#ifdef __WATCOMC__
+#include <io.h>
+#endif
+#include "defines.h"
+#include "structs.h"
+#include "globals.h"
+
+
+static struct GRPSYL *create_prev_grp P((struct MAINLL *mll_p, int staffno,
+ int v));
+static struct GRPSYL *create_meas_space P((struct MAINLL *mll_p));
+static void fix_spacechord P((struct MAINLL *chmll_p, struct CHORD *ch_p));
+static void splitspace P((struct GRPSYL *gs_p, RATIONAL duration));
+static void splicechord P((struct GRPSYL *gs_p, struct CHORD *ch_p));
+static void guitar_grpsyl_transpose P((struct GRPSYL *gs_p));
+static RATIONAL find_acc_end_time P((RATIONAL begin_time, struct GRPSYL *gs_p,
+ int n));
+static void propogate_accidental P((struct NOTE *note_p, RATIONAL begin_time,
+ RATIONAL end_time, struct GRPSYL *gs_p));
+static void mv_skipped_midi P((struct STUFF *stuff_p, int staffno,
+ struct MAINLL *topstaff_mll_p));
+\f
+
+/* seek back to where header size is in file, and fill in the correct size,
+ * now that we know what it is. */
+
+void
+fix_track_size(mfile, track_start, track_size)
+
+int mfile; /* file descriptor of MIDI file */
+long track_start; /* offset in file where size needs to be put */
+long track_size; /* track length in bytes */
+
+{
+ unsigned char buff[4];
+
+
+ debug(512, "fix_track_size");
+
+ /* go to where track size is stored in file */
+ (void) lseek(mfile, track_start + 4, SEEK_SET);
+
+ /* convert to 4-byte number with correct byte ordering regardless
+ * of machine byte ordering */
+ buff[0] = (track_size >> 24) & 0xff;
+ buff[1] = (track_size >> 16) & 0xff;
+ buff[2] = (track_size >> 8) & 0xff;
+ buff[3] = track_size & 0xff;
+ (void) write(mfile, buff, 4);
+
+ /* go back to end of file in case there are more track to write */
+ (void) lseek(mfile, 0L, SEEK_END);
+}
+\f
+
+/* given an octave mark string, return number of octaves to tranpose (could
+ * be negative if transposing down) */
+
+int
+parse_octave(string, place, fname, lineno)
+
+char *string; /* typically "8va" */
+int place; /* PL_ABOVE or PL_BELOW */
+char *fname; /* file name for errors */
+int lineno;
+
+
+{
+ int font, size;
+ int octave_value = 0;
+ int code; /* ASCII of character in string */
+
+
+ font = string[0];
+ size = string[1];
+ string += 2;
+ code = next_str_char(&string, &font, &size);
+ if (isdigit(code)) {
+ octave_value = code - '0';
+ code = next_str_char(&string, &font, &size);
+ /* might be a second digit. If user is crazy enough to use
+ * an octave number greater than 2 digits, ignore the rest */
+ if (isdigit(code)) {
+ octave_value = (octave_value * 10) + (code - '0');
+ }
+ }
+
+ /* must be either a non-zero multiple of 8, or things like 15, 22,
+ * etc if some musical mathematician adds 7 instead of 8 */
+ if (octave_value < 8 || ((octave_value - 8) % 7 != 0 &&
+ (octave_value - 8) % 8 != 0)) {
+ l_ufatal(fname, lineno, "invalid octave mark string");
+ }
+ if (octave_value % 8 == 0) {
+ octave_value /= 8;
+ } else {
+ octave_value = 1 + (octave_value - 8) % 7;
+ }
+
+ return(place == PL_BELOW ? -octave_value : octave_value);
+}
+\f
+
+/* determine "clocks per metronome tick." It not entirely clear to me
+ * how that is supposed to work, but... if the time signature denominator is
+ * 4, we'll use 24. If 8, use 12, etc. Only go down as far as 3, since that's
+ * not divisible by 2. Really, in fast tempo triple time, the denominator
+ * isn't the beat, but this will do for now. */
+
+int
+clocks(num)
+
+int num;
+
+{
+ switch(num) {
+ case 1:
+ return(96);
+ case 2:
+ return(48);
+ case 4:
+ return(24);
+ case 8:
+ return(12);
+ case 16:
+ return(6);
+ default:
+ return(3);
+ }
+}
+\f
+
+/* given a string, if it contains a word followed by an =, followed by a word,
+ * any of which may be separated by white space, return, via pointers,
+ * a pointer to the beginning of the word, the length of the word, and a pointer
+ * to the first non-white-space character after the =, and return YES.
+ * Otherwise, return NO. A "word" is a sequence of non-white-space chars. */
+
+int
+getkeyword(string, key_p, leng_p, arg_p_p)
+
+char *string; /* check this string */
+char **key_p; /* return pointer to keyword via this */
+int *leng_p; /* return length of keyword via this */
+char **arg_p_p; /* return pointer to argument after = via this */
+
+{
+ char *tok;
+
+
+ /* skip leading white space */
+ for (*key_p = string; **key_p == ' ' || **key_p == '\t'; (*key_p)++) {
+ ;
+ }
+
+ /* go till hit white space or equals sign */
+ for (tok = *key_p; *tok != '\0'; tok++) {
+ if (*tok == ' ' || *tok == '\t' || *tok == '=') {
+ break;
+ }
+ }
+
+ /* fill in length of key */
+ *leng_p = tok - *key_p;
+
+ if (*leng_p == 0) {
+ return(NO);
+ }
+
+ /* find first non-white beyond the = */
+ for ( ; *tok != '\0'; tok++) {
+ if (*tok == '=') {
+ for (tok++; *tok != '\0'; tok++) {
+ if (*tok != ' ' && *tok != '\t') {
+ *arg_p_p = tok;
+ return(YES);
+ }
+ }
+ }
+ }
+ return(NO);
+}
+\f
+
+/* given a user-specified key, see if it matches the given command name. Return
+ * YES if it does, NO if it doesn't. User only has to specify the first 3 or
+ * more characters of the command, because that's enough to make it unique,
+ * and saves them from typing longer names. */
+
+int
+matches(key, leng, cmd)
+
+char *key; /* user specified key to be checked */
+int leng; /* length of key */
+char *cmd; /* check if key matches this command */
+
+{
+ if (leng < 3) {
+ return(NO);
+ }
+ return(strncmp(key, cmd, leng) == 0 ? YES : NO);
+}
+\f
+
+/* given an ASCII hex digit, return its value 0-15 */
+
+int
+hexdig(ch)
+
+int ch;
+
+{
+ if (ch >= '0' && ch <= '9') {
+ return(ch - '0');
+ }
+ else if (ch >= 'a' && ch <= 'f') {
+ return(ch - 'a' + 10);
+ }
+ else if (ch >= 'A' && ch <= 'F') {
+ return(ch - 'A' + 10);
+ }
+ pfatal("bad hex digit");
+ /*NOTREACHED*/
+ return(0);
+}
+\f
+
+/* given a string, output it to midi file, prefixed by its length. */
+/* return number of bytes written */
+
+UINT32B
+midi_wrstring(mfile, str, internalform)
+
+int mfile; /* MIDI file */
+char *str; /* string to write to file */
+int internalform; /* YES if str is in Mup format, NO if just ASCII,
+ * C-style null-terminated string to be copied */
+
+{
+ char *buff; /* for all-ASCII version of str */
+ UINT32B bytes; /* number of bytes in length value */
+ int length; /* of string */
+
+
+ /* get plain ascii version of string. Write out length of
+ * string, then plain string itself */
+ if (internalform == YES) {
+ buff = ascii_str(str, NO, YES, TM_NONE);
+ length = strlen(buff);
+ bytes = wr_varlength(mfile, (UINT32B) length);
+ bytes += write(mfile, buff, (unsigned) length);
+ }
+ else {
+ length = strlen(str);
+ bytes = wr_varlength(mfile, (UINT32B) length);
+ bytes += write(mfile, str, (unsigned) length);
+ }
+
+ /* return number of bytes written */
+ return(bytes);
+}
+\f
+
+/* given a number, write to MIDI file in MIDI variable length format.
+ * Return number of bytes written. */
+
+UINT32B
+wr_varlength(mfile, num)
+
+UINT32B num;
+
+{
+ unsigned char buff[4];
+ int i;
+ int shift;
+
+
+ /* Because only 7 bits of each MIDI byte can be used,
+ * there is only support for numbers up to 28 bits long. */
+ if ((num & 0xf0000000) != 0) {
+ ufatal("midi value too large");
+ }
+
+ /* convert value to the MIDI variable-length number, which
+ * uses the lower 7 bits of each byte as parts of the number, and
+ * the high order bit as a flag to say which is the last byte of
+ * the (potentially) multi-byte number */
+ for (i = 0, shift = 21; shift >= 7; shift -= 7) {
+ if ( (num >> shift) || (i > 0)) {
+ buff[i++] = 0x80 | ((num >> shift) & 0x7f);
+ }
+ }
+ buff[i] = num & 0x7f;
+ (void) write(mfile, buff, (unsigned) (i + 1));
+ return (UINT32B) (i+1);
+}
+\f
+
+/* do key signature. Return number of bytes written */
+
+UINT32B
+midi_keysig(mfile, sharps, is_minor)
+
+int mfile;
+int sharps;
+int is_minor; /* YES if minor */
+
+{
+ UINT32B bytes;
+ unsigned char buff[8];
+
+
+ bytes = write_delta(mfile);
+ buff[0] = 0xff;
+ buff[1] = 0x59;
+ buff[2] = 0x02;
+ buff[3] = (char) sharps;
+ buff[4] = (is_minor == YES ? 1 : 0);
+ (void) write(mfile, buff, 5);
+
+ return(bytes + 5);
+}
+\f
+
+/* write out the timesig in Score SSV. Return number of bytes written */
+
+UINT32B
+midi_timesig(mfile)
+
+int mfile;
+{
+ UINT32B bytes;
+ unsigned char buff[8];
+
+
+ /* With additive time signatures, it is possible to get an effective
+ * time signature that won't fit in 7 bits. In that case, we don't
+ * do any time signature, since we can't represent it. */
+ if (Score.timenum > 127) {
+ return(0);
+ }
+
+ bytes = write_delta(mfile);
+ buff[0] = 0xff;
+ buff[1] = 0x58;
+ buff[2] = 0x04;
+ buff[3] = (char) Score.timenum;
+ buff[4] = (unsigned char) drmo(Score.timeden);
+ buff[5] = clocks(Score.timeden);
+ buff[6] = 0x8;
+ bytes += write(mfile, buff, 7);
+ return(bytes);
+}
+\f
+
+/* find group before given group. If none before it in current measure,
+ * back up in main list to find corresponding group list, and use final group
+ * in that list. If no group exists, create one. */
+
+struct GRPSYL *
+grp_before(gs_p, mll_p, staffno, v)
+
+struct GRPSYL *gs_p; /* find group before this one */
+struct MAINLL *mll_p; /* the list containing gs_p is attached to main list here */
+int staffno;
+int v; /* voice */
+
+{
+ int found_bar = NO;
+
+
+ if (gs_p->prev != (struct GRPSYL *) 0) {
+ /* oh good. There's another group before this one in the
+ * current measure, so just return it */
+ return(gs_p->prev);
+ }
+
+ /* have to go back to previous measure, if any. Start searching
+ * backwards in main list. */
+ for (mll_p = mll_p->prev; mll_p != (struct MAINLL *) 0;
+ mll_p = mll_p->prev) {
+ switch (mll_p->str) {
+ case S_STAFF:
+ if (found_bar == NO) {
+ /* still in current measure */
+ break;
+ }
+
+ if (mll_p->u.staff_p->staffno == staffno) {
+ /* we found the previous measure */
+ if (mll_p->u.staff_p->groups_p[v]
+ != (struct GRPSYL *) 0) {
+ /* find and return last group */
+ for (gs_p=mll_p->u.staff_p->groups_p[v];
+ gs_p->next !=
+ (struct GRPSYL *) 0;
+ gs_p = gs_p->next) {
+ ;
+ }
+ return(gs_p);
+ }
+ else {
+ /* this voice wasn't present before.
+ * Will have to create a measure */
+ return(create_meas_space(mll_p));
+ }
+ }
+ else if (mll_p->u.staff_p->staffno < staffno) {
+ /* corresponding staff does not exist in this
+ * measure. The only time this should happen is
+ * if user changed the number of staffs.
+ * So create staff */
+ return(create_prev_grp(mll_p, staffno, v));
+ }
+ break;
+
+ case S_BAR:
+ found_bar = YES;
+ break;
+
+ default:
+ /* ignore other things */
+ break;
+ }
+ }
+
+ /* Fell off the top of the list. This used to be possible,
+ * and we called create_prev_grp() to create a measure.
+ * But the measure really needs to be created much earlier--
+ * before makechords() is run--in order for squeezing to work right.
+ * So that's what we do now. So we should never get here. */
+ pfatal("fell off top of list in grp_before()");
+ /* NOTREACHED */
+ return(create_prev_grp(mll_p, staffno, v));
+}
+\f
+
+/* create a new STAFF struct and insert in main list, with grpcont of
+ * space and fulltime of the measure. Return pointer to the GRPSYL of
+ * appropriate voice of the STAFF that was created. */
+
+static struct GRPSYL *
+create_prev_grp(mll_p, staffno, v)
+
+struct MAINLL *mll_p; /* insert here */
+int staffno;
+int v;
+
+{
+ struct MAINLL *new_p; /* new STAFF */
+ int i;
+
+
+ new_p = newMAINLLstruct(S_STAFF, -1);
+ new_p->u.staff_p->staffno = (short) staffno;
+ insertMAINLL(new_p, mll_p);
+ for (i = 0; i < MAXVOICES; i++) {
+ new_p->u.staff_p->groups_p[i]
+ = create_meas_space(mll_p);
+ }
+
+ /* if added to beginning of list, have to add bar as well */
+ if (mll_p == (struct MAINLL *) 0) {
+ struct MAINLL *mbar_p;
+
+ mbar_p = newMAINLLstruct(S_BAR, -1);
+ insertMAINLL(mbar_p, new_p);
+ }
+
+ return(new_p->u.staff_p->groups_p[v]);
+}
+\f
+
+/* create a measure space as long as that of the reference measure (or of 4/4
+ * if no reference) and return it */
+
+static struct GRPSYL *
+create_meas_space(mll_p)
+
+struct MAINLL *mll_p; /* use this for reference to get measure length */
+
+{
+ struct GRPSYL *gs_p; /* new grpsyl */
+ struct GRPSYL *egs_p; /* existing grpsyl */
+
+
+ gs_p = newGRPSYL(GS_GROUP);
+ gs_p->grpcont = GC_SPACE;
+
+ /* figure out how much full time to give the group. If mll_p is not
+ * null, we are adding a staff to an existing measure, so use
+ * length of its first voice. Count up the length of existing measure */
+ gs_p->fulltime = Zero;
+ if (mll_p != (struct MAINLL *) 0 && mll_p->str == S_STAFF) {
+ for (egs_p = mll_p->u.staff_p->groups_p[0];
+ egs_p != (struct GRPSYL *) 0;
+ egs_p = egs_p->next) {
+
+ gs_p->fulltime = radd(gs_p->fulltime, egs_p->fulltime);
+ }
+ }
+ else {
+ /* at beginning of list, use default of 1/1 (the reduced
+ * form of 4/4) */
+ gs_p->fulltime.n = gs_p->fulltime.d = 1;
+ }
+
+ return(gs_p);
+}
+\f
+
+/* add a rest of the specified fulltime duration after the specified group.
+ * Since this is just for midi purposes, don't worry about filling in all
+ * the fields. */
+
+void
+add_rest(gs_p, fulltime)
+
+struct GRPSYL *gs_p; /* add rest after this group */
+RATIONAL fulltime; /* make it this long */
+
+{
+ struct GRPSYL *newgs_p;
+
+
+ if (gs_p == (struct GRPSYL *) 0) {
+ pfatal("null group passed to add_rest");
+ }
+
+ newgs_p = newGRPSYL(GS_GROUP);
+ newgs_p->grpcont = GC_REST;
+ rred (&fulltime);
+ newgs_p->fulltime = fulltime;
+ newgs_p->next = gs_p->next;
+ newgs_p->prev = gs_p;
+ gs_p->next = newgs_p;
+ if (newgs_p->next != (struct GRPSYL *) 0) {
+ newgs_p->next->prev = newgs_p;
+ }
+}
+\f
+
+/* when all voices have space, that should be squeezed to zero time.
+ * Go through main list. For each CHHEAD found,
+ * go down the list of chords. For each chord, see if
+ * if it is an all-space chord. If so, call fix_spacechord() to
+ * handle it.
+ */
+
+void
+midi_squeeze()
+
+{
+ struct MAINLL *mll_p; /* walk through main list */
+ struct CHORD *ch_p; /* walk through list of chords */
+
+
+ debug(256, "midi_squeeze");
+
+ initstructs();
+ for (mll_p = Mainllhc_p; mll_p != (struct MAINLL *) 0;
+ mll_p = mll_p->next) {
+
+ /* skip everything except CHHEADs and SSV updates */
+ if (mll_p->str != S_CHHEAD) {
+ if (mll_p->str == S_SSV) {
+ asgnssv(mll_p->u.ssv_p);
+ }
+ continue;
+ }
+
+ /* do each chord */
+ for (ch_p = mll_p->u.chhead_p->ch_p;
+ ch_p != (struct CHORD *) 0;
+ ch_p = ch_p->ch_p) {
+
+ if (ch_p->width == 0.0) {
+ /* found one to squeeze, do it */
+ fix_spacechord(mll_p, ch_p);
+ }
+ }
+ }
+}
+\f
+
+/* given an all-space chord to crunch, split up any groups that
+ * spill into this chord or extend beyond it. Then set the grpvalue of each
+ * group in the chord to GV_ZERO.
+ */
+
+static void
+fix_spacechord(chmll_p, ch_p)
+
+struct MAINLL *chmll_p; /* chord is hanging off this CHHEAD */
+struct CHORD *ch_p; /* zero-width chord */
+
+{
+ struct MAINLL *mll_p; /* walk through STAFFs in main list */
+ struct GRPSYL *gs_p; /* walk through groups in chord */
+ struct GRPSYL *group_p; /* head of list of grpsyls in measure */
+ RATIONAL minspacetime; /* time of shortest space in chord */
+ RATIONAL chordstart; /* where in measure chord begins */
+ RATIONAL chordend; /* where the space ends */
+ RATIONAL new_chordend; /* tentative new value for chordend */
+ RATIONAL grpstart; /* where in measure grpsyl begins */
+ int v; /* voice index */
+
+
+ /* first find the smallest duration in the chord. Can't use
+ * ch_p->duration here because grace notes have been adjusted
+ * by now to take some time */
+ minspacetime = ch_p->gs_p->fulltime;
+ for (gs_p = ch_p->gs_p->gs_p; gs_p != (struct GRPSYL *) 0;
+ gs_p = gs_p->gs_p) {
+
+ /* skip lyrics */
+ if (gs_p->grpsyl == GS_SYLLABLE) {
+ continue;
+ }
+
+ /* skip things on tab staff--we use the associated tabnotes
+ * staff instead */
+ if (is_tab_staff(gs_p->staffno) == YES) {
+ continue;
+ }
+
+ /* double check */
+ if (gs_p->grpcont != GC_SPACE) {
+ pfatal("non-space in zero-width chord");
+ }
+
+ /* find minimum time value of space */
+ if (LT(gs_p->fulltime, minspacetime)) {
+ minspacetime = gs_p->fulltime;
+ }
+ }
+
+ /* find the start of the chord. Can't use ch_p->startime because
+ * we may have squeezed out time earlier in the measure and need
+ * to compensate for that. So have to count up the time before
+ * the group at the top of the chord. */
+ for (chordstart = Zero, gs_p = ch_p->gs_p->prev;
+ gs_p != (struct GRPSYL *) 0;
+ gs_p = gs_p->prev) {
+ chordstart = radd(chordstart, gs_p->fulltime);
+ }
+
+ /* if spaces are overlapped in strange ways between different
+ * voices, the minspacetime we found above may actually be too long.
+ * If the time between this chord and the next is less than the
+ * minspacetime found so far, make minspacetime the time till the
+ * next chord */
+ if (ch_p->ch_p != (struct CHORD *) 0) {
+ if (LT(rsub(ch_p->ch_p->starttime, chordstart), minspacetime)) {
+ minspacetime = rsub(ch_p->ch_p->starttime, chordstart);
+ }
+ }
+
+ /* That still isn't completely adequate to find where the space actually
+ * ends, because if there were lots of grace notes moved back into a
+ * space, and a very short space in another voice, they could overlap.
+ * So go through the list of voices, seeing where the real end is.
+ * This code is unfortunately very similar to the code below it,
+ * yet different enough to make it hard to make it
+ * into a common function. */
+ chordend = radd(chordstart, minspacetime);
+ for (mll_p = chmll_p->next; mll_p != (struct MAINLL *) 0;
+ mll_p = mll_p->next) {
+
+ /* CHHEAD is followed immediately by STAFFS, so when we
+ * hit something other than STAFF, we are done */
+ if (mll_p->str != S_STAFF) {
+ break;
+ }
+
+ /* do each voice */
+ for (v = 0; v < MAXVOICES; v++ ) {
+ /* Check if we have a special case where the actual
+ * space is shorter than we thought. We unfortunately
+ * cannot use hasspace() here, because it ignores grace
+ * notes, and by now, grace notes have some time.
+ * Find the chord that begins the space in question. */
+ grpstart = Zero;
+
+ if ((gs_p = mll_p->u.staff_p->groups_p[v])
+ == (struct GRPSYL *) 0) {
+ /* no voice here, so that's all space.
+ * That's good: we're done with this voice */
+ continue;
+ }
+
+ /* grace notes at the beginning of the measure
+ * have effectively been moved timewise
+ * into previous measure, so discount them. */
+ for ( ; gs_p->grpvalue == GV_ZERO
+ && gs_p->grpcont == GC_NOTES;
+ gs_p = gs_p->next) {
+ ;
+ }
+
+ for ( ; gs_p != (struct GRPSYL *) 0;
+ gs_p = gs_p->next) {
+
+ /* see if this group start corresponds
+ * with start of chord, or spills into
+ * the chord or is the last chord. If
+ * so, that's the one we want. */
+ if (EQ(grpstart, chordstart) ||
+ GT(radd(grpstart,
+ gs_p->fulltime),
+ chordstart) ||
+ gs_p->next ==
+ (struct GRPSYL *) 0) {
+ /* found appropriate group */
+ break;
+ }
+ else {
+ /* accummulate time so far */
+ grpstart = radd(grpstart, gs_p->fulltime);
+ }
+ }
+
+ if (gs_p == (struct GRPSYL *) 0) {
+ pfatal("failed to find space group");
+ }
+
+ if (gs_p->grpcont != GC_SPACE) {
+ /* things overlapped so much after the
+ * grace note adjustments and such
+ * that this isn't really a
+ * crunch-able chord after all. */
+ return;
+ }
+
+ /* need to adjust amount of space we
+ * can really crunch. Find where this chord
+ * ends, and add in the time of any immediately
+ * following space groups. */
+ new_chordend = radd(grpstart, gs_p->fulltime);
+ for (gs_p = gs_p->next; gs_p != (struct GRPSYL *) 0;
+ gs_p = gs_p->next) {
+
+ if (gs_p->grpcont == GC_SPACE) {
+ new_chordend = radd(new_chordend,
+ gs_p->fulltime);
+ }
+ else {
+ break;
+ }
+ }
+
+ /* if the newly calculated end is sooner than
+ * what we had before, then adjust accordingly. */
+ if (LT(new_chordend, chordend)) {
+ chordend = new_chordend;
+ }
+ }
+ }
+
+ /* recalculate the minspace time after any adjustment */
+ minspacetime = rsub(chordend, chordstart);
+
+
+ /* go down each voice of each staff.
+ * For each, find the space group associated with the chord.
+ * If it has the same starttime as the chord and has minspacetime
+ * duration, it's easy: we just mark it GV_ZERO. Otherwise, if
+ * it starts earlier, we have to split off a group in the front
+ * first, and if it lasts longer than the end of the chord, we
+ * have to split off a group at the end first.
+ */
+ for (mll_p = chmll_p->next; mll_p != (struct MAINLL *) 0;
+ mll_p = mll_p->next) {
+ /* CHHEAD is followed immediately by STAFFS, so when we
+ * hit something other than STAFF, we are done */
+ if (mll_p->str != S_STAFF) {
+ break;
+ }
+
+ /* do each voice */
+ for (v = 0; v < MAXVOICES; v++ ) {
+
+ /* get shorter name for list of grpsyls */
+ group_p = mll_p->u.staff_p->groups_p[v];
+
+ if (group_p == (struct GRPSYL *) 0) {
+ continue;
+ }
+
+ /* go through groups, add up time till we find the
+ * group we're looking for */
+ grpstart = Zero;
+
+ /* grace notes at the beginning of the measure
+ * have effectively been moved timewise
+ * into previous measure, so discount them. */
+ for (gs_p = group_p; gs_p->grpvalue == GV_ZERO
+ && gs_p->grpcont == GC_NOTES;
+ gs_p = gs_p->next) {
+ ;
+ }
+
+ for ( ; gs_p != (struct GRPSYL *) 0;
+ gs_p = gs_p->next) {
+
+ /* see if this group start corresponds with
+ * start of chord */
+ if (EQ(grpstart, chordstart)) {
+ /* found appropriate group */
+ break;
+ }
+
+ else if (GT(radd(grpstart,gs_p->fulltime),
+ chordstart)) {
+ /* This group spills into space
+ * to be crunched.
+ * Split off beginning of group. */
+ splitspace(gs_p,
+ rsub(chordstart, grpstart));
+
+ /* point to added group */
+ gs_p = gs_p->next;
+
+ /* splice added group into chord */
+ splicechord(gs_p, ch_p);
+
+ /* found appropriate group */
+ break;
+ }
+ else {
+ /* haven't gotten to the group yet.
+ * Add on the time taken by this
+ * group in preparation for next
+ * trip around the loop */
+ grpstart = radd(grpstart, gs_p->fulltime);
+ /* if last group in measure, this has
+ * to be the appropriate one */
+ if (gs_p->next == (struct GRPSYL *) 0) {
+ break;
+ }
+ }
+ }
+
+ if (gs_p == (struct GRPSYL *) 0) {
+ pfatal("failed to find space group");
+ }
+
+ /* if group extended beyond end of
+ * chord, split the group and splice added group
+ * into chord */
+ if (GT(gs_p->fulltime, minspacetime)) {
+ splitspace(gs_p, minspacetime);
+ splicechord(gs_p->next, ch_p->ch_p);
+ }
+
+ /* mark as taking no time */
+ gs_p->grpvalue = GV_ZERO;
+ }
+ }
+}
+\f
+
+/* split a space grpsyl into two. The original group becomes the first
+ * group, having the specified duration. A new group is added after it,
+ * having the remainder of the time taken by the original group. */
+
+static void
+splitspace(gs_p, duration)
+
+struct GRPSYL *gs_p; /* split this group */
+RATIONAL duration; /* make the first group of split this long */
+
+{
+ struct GRPSYL *newgs_p; /* added group */
+
+
+ /* bug insurance */
+ if (gs_p == (struct GRPSYL *) 0 ||
+ (gs_p->grpcont != GC_SPACE &&
+ svpath(gs_p->staffno, VISIBLE)->visible == YES) ) {
+ pfatal("bad group passed to splitspace");
+ }
+
+ /* split into 2 groups, one taking duration, and the
+ * other taking the remainder */
+ newgs_p = newGRPSYL(GS_GROUP);
+ copy_attributes(newgs_p, gs_p);
+ newgs_p->grpcont = GC_SPACE;
+ newgs_p->fulltime = rsub(gs_p->fulltime, duration);
+ gs_p->fulltime = duration;
+
+ /* link new one into list */
+ newgs_p->next = gs_p->next;
+ newgs_p->prev = gs_p;
+ gs_p->next = newgs_p;
+ if (newgs_p->next != (struct GRPSYL *) 0) {
+ newgs_p->next->prev = newgs_p;
+ }
+}
+\f
+
+/* splice a grpsyl into a chord */
+
+static void
+splicechord(gs_p, ch_p)
+
+struct GRPSYL *gs_p; /* splice in this group */
+struct CHORD *ch_p; /* splice into this chord */
+
+{
+ struct GRPSYL *nxtgs_p; /* next group in chord list */
+ struct GRPSYL **ins_p_p; /* where to insert new grpsyl */
+
+
+ if (ch_p == (struct CHORD *) 0) {
+ /* this could happen if user gave measure of space, and
+ * then following measure started with a grace note. The
+ * grace note got moved into the space, but there was no
+ * chhead created for it. It should be safe to just
+ * not link it into a chhead, so return */
+ return;
+ }
+
+ if (gs_p == (struct GRPSYL *) 0) {
+ pfatal("null pointer in splicechord");
+ }
+
+ /* Figure out where to insert. */
+ for (ins_p_p = &(ch_p->gs_p); (*ins_p_p) != (struct GRPSYL *) 0;
+ ins_p_p = &((*ins_p_p)->gs_p)) {
+
+ /* gets inserted here if next group is for a higher staffno or
+ * for a higher voice number of the same staffno */
+ nxtgs_p = *ins_p_p;
+ if (nxtgs_p == (struct GRPSYL *) 0) {
+ /* goes at end of list */
+ break;
+ }
+ if (nxtgs_p->staffno > gs_p->staffno ||
+ (nxtgs_p->staffno == gs_p->staffno &&
+ nxtgs_p->vno > gs_p->vno)) {
+ break;
+ }
+ }
+
+ if (ins_p_p == (struct GRPSYL **) 0) {
+ pfatal("couldn't find where to insert new grpsyl into chord");
+ }
+
+ /* insert it */
+ gs_p->gs_p = *ins_p_p;
+ *ins_p_p = gs_p;
+}
+\f
+
+/* if user specifies the default guitar tab staff, then we want to transpose
+ * everything down an octave, because a standard guitar sounds an octave
+ * lower than it is written. */
+
+void
+guitar_transpose()
+
+{
+ struct MAINLL *mll_p;
+
+
+ /* go through the main list looking for things to transpose */
+ initstructs();
+ for (mll_p = Mainllhc_p; mll_p != (struct MAINLL *) 0; mll_p = mll_p->next) {
+ if (mll_p->str == S_STAFF) {
+ /* if there is a staff below this and that staff is a
+ * guitar tab staff, then we need to transpose.
+ * Index into Staff by this staff's number, because
+ * staff numbers start at 1, but Staff index starts at 0
+ */
+ if (mll_p->u.staff_p->staffno < Score.staffs &&
+ Staff[mll_p->u.staff_p->staffno]
+ .strinfo == Guitar) {
+ guitar_grpsyl_transpose(mll_p->u.staff_p->groups_p[0]);
+ }
+ }
+ else if (mll_p->str == S_SSV) {
+ asgnssv(mll_p->u.ssv_p);
+ }
+ }
+}
+\f
+
+/* given a group, transpose everything down by an octave */
+
+static void
+guitar_grpsyl_transpose(gs_p)
+
+struct GRPSYL *gs_p;
+
+{
+ register int n;
+
+ for( ; gs_p != (struct GRPSYL *) 0; gs_p = gs_p->next) {
+ for (n = 0; n < gs_p->nnotes; n++) {
+ if (gs_p->notelist[n].octave > 0) {
+ (gs_p->notelist[n].octave)--;
+ }
+ else {
+ pfatal("guitar transposition results in note below octave 0");
+ }
+ }
+ }
+}
+\f
+
+/* If there is an accidental on a note on one voice, it should really apply
+ * the other voice on that same staff too. So propogate these accidentals
+ * to the other voice if necessary.
+ */
+
+void
+other_voice_accidentals(staff_p)
+
+struct STAFF *staff_p;
+
+{
+ struct GRPSYL *gs_p;
+ int v; /* voice */
+ int n; /* notelist index */
+ RATIONAL begin_time, end_time;
+
+ /* if there is only one voice on the staff, or if either
+ * voice is a measure space or rest or multirest, nothing to do. Since
+ * we're doing MIDI, we know a "measure" GRPSYL
+ * will be rest or space, not mrpt, so can just check that. */
+ if ( (staff_p->groups_p[0]->is_meas == YES) ||
+ (staff_p->groups_p[1] == (struct GRPSYL *) 0) ||
+ (staff_p->groups_p[1]->is_meas == YES) ) {
+ return;
+ }
+
+ /* do each voice */
+ for (v = 0; v < MAXVOICES; v++) {
+
+ /* do each note of each chord */
+ begin_time = Zero;
+ for (gs_p = staff_p->groups_p[v]; gs_p != (struct GRPSYL *) 0;
+ gs_p = gs_p->next) {
+
+ for (n = 0; n < gs_p->nnotes; n++) {
+
+ /* if this note doesn't have an accidental
+ * nothing more to do on it */
+ if (gs_p->notelist[n].accidental == '\0') {
+ continue;
+ }
+
+ /* This note does have an accidental.
+ * Go forward and see how long it lasts.
+ * It will last until there is another
+ * accidental on the same note letter/octave
+ * or until end of measure.
+ */
+ end_time = find_acc_end_time(begin_time, gs_p, n);
+
+ /* now check the other voice.
+ * If the same letter/octave note appears
+ * on that voice on or after the begin time but
+ * before the end time, and that note
+ * does not already have an accidental,
+ * give it the same accidental as
+ * was found on the other voice.
+ */
+ propogate_accidental( &(gs_p->notelist[n]),
+ begin_time, end_time,
+ staff_p->groups_p[v == 0 ? 1 : 0]);
+ }
+
+ /* accumulate time so far in measure */
+ begin_time = radd(begin_time, gs_p->fulltime);
+ }
+ }
+}
+
+/* Given an GRPSYL and a note n in its notelist, and a begin_time, return
+ * the end_time which is either the end of the measure or the next instance
+ * of the given note which has an accidental on it. */
+
+static RATIONAL
+find_acc_end_time(begin_time, gs_p, n)
+
+RATIONAL begin_time;
+struct GRPSYL *gs_p;
+int n;
+
+{
+ RATIONAL end_time;
+ struct GRPSYL *end_gs_p;
+ int n2;
+
+
+ end_time = begin_time;
+ end_gs_p = gs_p;
+ for ( ; ; ) {
+ /* Add up time that accidental lasts */
+ end_time = radd(end_time, end_gs_p->fulltime);
+
+ if ((end_gs_p = end_gs_p->next) == (struct GRPSYL *) 0) {
+ /* Hit end of measure */
+ return(end_time);
+ }
+
+ /* See if this group has the same note as had the accidental,
+ * and if so, whether it has a new accidental. */
+ for (n2 = 0; n2 < end_gs_p->nnotes; n2++) {
+ if ((gs_p->notelist[n].letter
+ == end_gs_p->notelist[n2].letter) &&
+ (gs_p->notelist[n].octave
+ == end_gs_p->notelist[n2].octave)) {
+
+ /* does have the same note. check accidental */
+ if (end_gs_p->notelist[n2].accidental != '\0') {
+ /* a new accidental, so this cancels
+ * the one we had */
+ return(end_time);
+ }
+ break;
+ }
+ }
+ }
+}
+\f
+
+/* Check the groups in list pointed to by gs_p.
+ * If the letter/octave of the given note_p appears on or after the begin time
+ * but before the end time, and that note does not already have an accidental,
+ * give it the same accidental as was found on note_p. */
+
+static void
+propogate_accidental(note_p, begin_time, end_time, gs_p)
+
+struct NOTE *note_p; /* look for note matching this one */
+RATIONAL begin_time; /* look between the begin_time and end_time */
+RATIONAL end_time;
+struct GRPSYL *gs_p; /* look in this list and adjust if needed */
+
+{
+ RATIONAL accumulated_time;
+ int n;
+
+
+ for (accumulated_time = Zero;
+ gs_p != (struct GRPSYL *) 0;
+ accumulated_time = radd(accumulated_time,
+ gs_p->fulltime), gs_p = gs_p->next) {
+
+ if (LT(accumulated_time, begin_time)) {
+ /* haven't gotten to begin yet */
+ continue;
+ }
+
+ if (GE(accumulated_time, end_time)) {
+ /* reached end time */
+ return;
+ }
+
+ /* See if this group contains the note of interest */
+ for (n = 0; n < gs_p->nnotes; n++) {
+ if ( (gs_p->notelist[n].letter == note_p->letter) &&
+ (gs_p->notelist[n].octave == note_p->octave) ) {
+ /* if note already has an accidental,
+ * the one from the other voice doesn't
+ * count. Otherwise propogate the
+ * accidental from the other voice */
+ if (gs_p->notelist[n].accidental == '\0') {
+ gs_p->notelist[n].accidental
+ = note_p->accidental;
+ }
+
+ /* Found the note and fixed it if needed,
+ * so our job here is done */
+ return;
+ }
+ }
+ }
+}
+\f
+
+/* If doing MIDI with the -x option, we move all the midi STUFFs in the
+ * skipped leading measures to the "extra" empty measure that exists for MIDI.
+ * This ensure that any MIDI parameters, instruments, etc are correct
+ * for the place in the song where we are actually starting.
+ */
+
+void
+mv_midi_items(mll_p, topstaff_mll_p)
+
+struct MAINLL *mll_p; /* points to a STAFF that may have MIDI items to move */
+struct MAINLL *topstaff_mll_p; /* points to STAFF where "all" MIDI items will go */
+
+{
+ struct STUFF *stuff_p; /* stuff currently being processed */
+ struct STUFF *next; /* we may move the current STUFF to another
+ * list, so need to save its "next" */
+ char *key; /* midi directive keyword */
+ int leng; /* length of key */
+ char *arg; /* arg after the = */
+ int staffno;
+
+
+ staffno = mll_p->u.staff_p->staffno;
+ for (stuff_p = mll_p->u.staff_p->stuff_p; stuff_p != 0; stuff_p = next) {
+ /* keep track of next, in case we delete the current */
+ next = stuff_p->next;
+
+ if (next == stuff_p) {
+ pfatal("loop detected in MIDI list");
+ }
+
+ if (stuff_p->stuff_type == ST_MIDI) {
+ if (getkeyword(stuff_p->string+2, &key, &leng, &arg)
+ == NO) {
+ /* invalid, so ignore it. */
+ continue;
+ }
+
+ /* Some MIDI things, like cue point should just be
+ * ignored if we are skipping over the music,
+ * so just check for and move things we care about.
+ */
+ if (matches(key, leng, "program") == YES ||
+ matches(key, leng, "tempo") == YES ||
+ matches(key, leng, "onvelocity") == YES ||
+ matches(key, leng, "channel") == YES ||
+ matches(key, leng, "parameter") == YES ||
+ matches(key, leng, "offvelocity") == YES ||
+ matches(key, leng, "name") == YES ||
+ matches(key, leng, "instrument") == YES ||
+ matches(key, leng, "hex") == YES ||
+ matches(key, leng, "port") == YES ||
+ matches(key, leng, "chanpressure") == YES) {
+ /* Found something to move; move it */
+ mv_skipped_midi(stuff_p, staffno, topstaff_mll_p);
+ }
+ }
+ }
+}
+\f
+
+/* With -x option and MIDI, this moves a MIDI STUFF from where it was in
+ * a skipped measure to the special "empty" measure at the beginning of the
+ * piece. In general, for any given MIDI thing, only the last one matters,
+ * so we only need to keep the last one.
+ */
+
+static void
+mv_skipped_midi(stuff_p, staffno, topstaff_mll_p)
+
+struct STUFF *stuff_p; /* Midi STUFF to be moved */
+int staffno; /* Which staff it is for */
+struct MAINLL *topstaff_mll_p; /* The STAFF where midi "all" STUFF go.
+ * Other STAFFs, if any, will surround it. */
+
+{
+ int leng; /* length of MIDI keyword */
+ char *oldtext; /* text of STUFF that already
+ * exists in the list */
+ char *newtext; /* text of STUFF to be added */
+ struct MAINLL *mll_p; /* for finding STAFF to link to */
+ struct STAFF *staff_p = 0; /* the STAFF to link to */
+ struct STUFF **stuff_p_p; /* for finding where to add
+ * the new stuff_p into list */
+ static char *alphabet = "abcdefghijklmnopqrstuvwxyz";
+
+ /* Find the correct STAFF to attach to. We all passed a pointer
+ * to the STAFF for midi "all" items. For non-"all" items,
+ * we search in the main list for the correct STAFF.
+ */
+ if (stuff_p->all == YES || staffno == topstaff_mll_p->u.staff_p->staffno) {
+ staff_p = topstaff_mll_p->u.staff_p;
+ }
+ else if (staffno < topstaff_mll_p->u.staff_p->staffno) {
+ /* Search backward for correct staff from the "all" staff.
+ * This case probably won't happen often--only if some
+ * staffs at the top are currently invisible (so they are
+ * not the current "top visible staff").
+ */
+ for (mll_p = topstaff_mll_p; mll_p->str == S_STAFF;
+ mll_p = mll_p->prev) {
+ if (mll_p->u.staff_p->staffno == staffno) {
+ staff_p = mll_p->u.staff_p;
+ break;
+ }
+ }
+ }
+ else {
+ /* Search forwards for correct staff from the "all" staff. */
+ for (mll_p = topstaff_mll_p; mll_p->str == S_STAFF;
+ mll_p = mll_p->next) {
+ if (mll_p->u.staff_p->staffno == staffno) {
+ staff_p = mll_p->u.staff_p;
+ break;
+ }
+ }
+ }
+ if (staff_p == 0) {
+ /* User must have reduced the number of staffs during the
+ * skipped part, so this one is irrelevant. */
+ return;
+ }
+
+ /* Add to end of list. When going through the list,
+ * see if there is already an item of that type (for param
+ * have to check the specific param matches too), and if so delete
+ * the earlier one, because this new one overrides it.
+ */
+ newtext = stuff_p->string + 2;
+ for (stuff_p_p = &(staff_p->stuff_p); *stuff_p_p != 0;
+ stuff_p_p = &((*stuff_p_p)->next) ) {
+
+ /* If for different place,
+ * this one doesn't count for matching */
+ if ( (*stuff_p_p)->place != stuff_p->place) {
+ continue;
+ }
+
+ oldtext = (*stuff_p_p)->string + 2;
+
+ /* Since hex is arbitrary data, we always keep it */
+ if (matches(oldtext, strlen(oldtext), "hex")) {
+ continue;
+ }
+
+ /* Names can be abbreviated,
+ * so match up to whichever is shorter */
+ leng = MIN(strspn(oldtext, alphabet), strspn(newtext, alphabet));
+ if (matches(newtext, leng, oldtext)== YES) {
+ /* If it's something other than param, we can get
+ * rid of the existing one */
+ if (strncmp(newtext, "par", 3) != 0) {
+ *stuff_p_p = (*stuff_p_p)->next;
+ }
+ else {
+ /* Compare parameter numbers and if they match,
+ * delete the existing one.
+ */
+ char *new_eq_p, *old_eq_p; /* loc of '=' */
+ int oldparm, oldval;
+ int newparm, newval;
+
+ new_eq_p = strchr(newtext, '=');
+ old_eq_p = strchr(oldtext, '=');
+ if (new_eq_p != 0 && old_eq_p != 0) {
+ if (get_param(new_eq_p + 1,
+ stuff_p->inputfile,
+ stuff_p->inputlineno,
+ &newparm, &newval)
+ == YES &&
+ get_param(old_eq_p + 1,
+ (*stuff_p_p)->inputfile,
+ (*stuff_p_p)->inputlineno,
+ &oldparm, &oldval)
+ == YES &&
+ oldparm == newparm) {
+ *stuff_p_p = (*stuff_p_p)->next;
+ }
+ }
+ }
+
+ if (*stuff_p_p == 0) {
+ /* If deleted last item in the existing list,
+ * jump out, so we won't try to deference
+ * this null pointer. */
+ break;
+ }
+ }
+ }
+
+ /* Just do everything at beat zero. The beat at which user specified
+ * might not even exist in the current time signature, and basically
+ * we just want to do everything ASAP anyway. We know there is never
+ * a 'til' on midi, so no need to deal with that. We could end up
+ * with a lot to do at count zero, potentially enough to overwhelm
+ * the limited MIDI bandwidth and delay the first actual note.
+ * But that potential is always there, and by discarding things
+ * that were overwritten later, which we did above, unless there
+ * are an awfully lot of parameters on every possible channel,
+ * it's probably only going to take less than 0.1 second,
+ * so it doesn't seem worth the bother to try to do something
+ * fancy to calculate how much time we need for this stuff
+ * and delay the actual music by that much. */
+ stuff_p->start.count = 0.0;
+ stuff_p->start.steps = 0.0;
+
+ /* link onto end of list */
+ *stuff_p_p = stuff_p;
+ stuff_p->next = 0;
+}
+\f
+
+/* Given the argument to a MIDI "parameter" command, extract and return
+ * (via pointers) the two numbers (the parameter number and its value).
+ * If successful, returns YES, else prints a warning and returns NO.
+ * If NO is returned, the pointed to return values are not fill in.
+ */
+
+int
+get_param(arg, inputfile, inputlineno, parmnum_p, parmval_p)
+
+char *arg; /* the argument part after "parameter=" */
+char *inputfile; /* for error messages */
+int inputlineno; /* for error messages */
+int *parmnum_p; /* parameter number is returned here */
+int *parmval_p; /* parameter value is returned here */
+
+{
+ int parmnum; /* parameter number */
+ int parmval; /* parameter value */
+ char *parm_p; /* pointer to current place in string */
+
+
+ /* extract first number */
+ parmnum = strtol(arg, &parm_p, 10);
+ if (parm_p == arg) {
+ l_warning(inputfile, inputlineno,
+ "parameter requires two values");
+ return(NO);
+ }
+ /* skip white space, if any */
+ while (*parm_p == ' ' || *parm_p == '\t') {
+ parm_p++;
+ }
+ /* next we better have a comma */
+ if (*parm_p != ','){
+ l_warning(inputfile, inputlineno,
+ "parameter is missing required comma");
+ return(NO);
+ }
+ /* extract the second number */
+ arg = parm_p + 1;
+ parmval = strtol(arg, &parm_p, 10);
+ if (parm_p == arg) {
+ l_warning(inputfile, inputlineno,
+ "parameter is missing second value");
+ return(NO);
+ }
+ /* verify both numbers are within range */
+ if ((l_rangecheck(parmnum, 0, 127, "parameter number",
+ inputfile, inputlineno) == YES) &&
+ (l_rangecheck(parmval, 0, 127, "parameter value",
+ inputfile, inputlineno) == YES)) {
+ *parmnum_p = parmnum;
+ *parmval_p = parmval;
+ return(YES);
+ }
+ return(NO);
+}