/* exportabc.cpp * Functions for exporting what Denemo's working on to an ABC file (adapted * somewhat from exportmudela.c) * * for Denemo, a gtk+ frontend to GNU Lilypond * (c) 2001-2005 Eric Galluzzo */ #include #include #include "twoints.h" #include "utils.h" #include #include #include #include #include #include /** * Determine key signature from denemo value * */ static void determinebasekey (gint number, gchar ** basekey) { switch (number) { case -7: *basekey = "Cb"; break; case -6: *basekey = "Gb"; break; case -5: *basekey = "Db"; break; case -4: *basekey = "Ab"; break; case -3: *basekey = "Eb"; break; case -2: *basekey = "Bb"; break; case -1: *basekey = "F"; break; case 0: *basekey = "C"; break; case 1: *basekey = "G"; break; case 2: *basekey = "D"; break; case 3: *basekey = "A"; break; case 4: *basekey = "E"; break; case 5: *basekey = "B"; break; case 6: *basekey = "F#"; break; case 7: *basekey = "C#"; break; case 8: *basekey = "G#"; break; case 9: *basekey = "D#"; break; case 10: *basekey = "A#"; break; default: *basekey = _("%{error. defaulting to%}C"); break; } } /** * Determine clef from denemo value * */ static void determineclef (gint type, gchar ** clefname, gint * octaveshift) { switch (type) { case DENEMO_TREBLE_CLEF: *clefname = "treble"; *octaveshift = 0; break; case DENEMO_BASS_CLEF: *clefname = "bass"; *octaveshift = 2; break; case DENEMO_ALTO_CLEF: *clefname = "alto"; *octaveshift = 1; break; case DENEMO_G_8_CLEF: *clefname = "treble8"; *octaveshift = 1; break; case DENEMO_TENOR_CLEF: *clefname = "tenor"; /* FIXME: highly nonstandard */ *octaveshift = 1; /* FIXME: ditto */ break; case DENEMO_SOPRANO_CLEF: *clefname = "soprano"; /* FIXME: highly nonstandard */ *octaveshift = 0; /* FIXME: ditto */ break; default: *clefname = _("%{error. defaulting to%}treble"); *octaveshift = 0; break; } } /** * determine the timesig denominator * */ static gint determinedefaultlength (struct twoints *timesig) { /* For 1/x and 2/x time signatures, return x*2; for all others, return x. */ if (timesig->a < 3) return timesig->b << 1; else return timesig->b; } /** * Determine the length as it should be written to the ABC file as a fraction. * defaultlength is a denominator (e.g. "8"), the current default length * (e.g. "L:1/8"). duration and numdots are the Denemo duration and number of * dots, and the ABC length is returned in length. */ static void determinelength (gint duration, gint numdots, gint defaultlength, struct twoints *length) { /* ABC duration is: * numdots + 1 * defaultlength * (2 - 1) * ----------------------------------- * numdots + duration * 2 <-- = 1 << (numdots + duration) * * simplified as far as possible (defaultlength is always a multiple of 2). */ //gint i; gint num = 1; /* so far */ gint denom = 1 << (numdots + duration); while (denom > 1 && defaultlength > 1) { denom >>= 1; defaultlength >>= 1; } while (defaultlength > 1) { num <<= 1; defaultlength >>= 1; } num *= ((1 << (numdots + 1)) - 1); length->a = num; length->b = denom; } /** * Print out the given length (numerator/denominator), omitting 1's. */ static void printlength (FILE * fp, struct twoints *length) { if (length->a > 1) fprintf (fp, "%d", length->a); if (length->b > 1) { fprintf (fp, "/"); if (length->b > 2) fprintf (fp, "%d", length->b); } } /** * Print out the given chord, given the current default length. */ static void printchord (FILE * fp, chord * chordptr, gint octaveshift, gint defaultlength) { gint i; struct twoints length; determinelength (chordptr->baseduration, chordptr->numdots, defaultlength, &length); if (chordptr->tones) /* it's a note or chord, not a rest */ { /* If there's only one note, it's a note, not a chord. */ gboolean ischord = chordptr->tones->next != NULL; GList *curnotenode, *tmp; note *curnote; gchar notename; gint octave; /* Print out the decorations and beginning of the slur (if any). */ if (chordptr->slur_begin_p) fprintf (fp, "("); for (tmp = chordptr->ornamentlist; tmp; tmp = tmp->next) { if (*(enum ornament *) tmp->data == (enum ornament) STACCATO) fprintf (fp, "."); if (*(enum ornament *) tmp->data == (enum ornament) D_ACCENT) fprintf (fp, "k"); /* FIXME: nonstandard */ if (*(enum ornament *) tmp->data == (enum ornament) FERMATA) fprintf (fp, "H"); /* FIXME: nonstandard */ if (*(enum ornament *) tmp->data == (enum ornament) TENUTO) fprintf (fp, "M"); /* FIXME: nonstandard */ if (*(enum ornament *) tmp->data == (enum ornament) TRILL) fprintf (fp, "T"); /* FIXME: somewhat nonstandard */ /* FIXME: I believe turns are not supported in ABC at the moment. */ if (*(enum ornament *) tmp->data == (enum ornament) MORDENT) fprintf (fp, "~"); if (*(enum ornament *) tmp->data == (enum ornament) STACCATISSIMO) fprintf (fp, "K"); /* FIXME: nonstandard */ } if (ischord) fprintf (fp, "["); for (curnotenode = chordptr->tones; curnotenode != NULL; curnotenode = curnotenode->next) { curnote = (note *) curnotenode->data; notename = mid_c_offsettoname (curnote->mid_c_offset); octave = mid_c_offsettooctave (curnote->mid_c_offset) + octaveshift - 2; /* Print accidentals. */ if (curnote->showaccidental) { if (curnote->enshift > 0) { for (i = 0; i < curnote->enshift; i++) fprintf (fp, "^"); } else if (curnote->enshift < 0) { for (i = 0; i > curnote->enshift; i--) fprintf (fp, "_"); } else { fprintf (fp, "="); } } /* Print the note name. */ if (octave < 0) { octave += 1; notename = toupper (notename); } fprintf (fp, "%c", notename); /* Print the correct number of "'" or "," marks. */ if (octave > 0) { for (i = 0; i < octave; i++) fprintf (fp, "'"); } else { for (i = 0; i > octave; i--) fprintf (fp, ","); } printlength (fp, &length); } if (ischord) fprintf (fp, "]"); if (chordptr->slur_end_p) fprintf (fp, ")"); if (chordptr->is_tied) fprintf (fp, "-"); } else /* it's a rest */ { fprintf (fp, "z"); printlength (fp, &length); } } /** * Export the ABC. This works as follows: * 1. Write out all the headers (X:, T:, C:, Q:, and K:). * 2. Write out each voice in turn. */ void exportabc (gchar * thefilename, DenemoScore * si, gint start, gint end) { gchar *clefname; gchar *basekeyname; FILE *fp; GString *filename = g_string_new (thefilename); /* FIXME: The following assumes that there is at least one staff. */ DenemoStaff *firststaffstruct = (DenemoStaff *) si->thescore->data; staffnode *curstaff; DenemoStaff *curstaffstruct; gint defaultlength; gint curvoicenum; gint octaveshift; measurenode *curmeasure; gint externalmeasurenum; gint internalmeasurenum; gboolean emptymeasure; objnode *curobjnode; DenemoObject *curobj; struct twoints curtime; gboolean curisnonprimary; gboolean nextisnonprimary; objnode *tupleobjnode; DenemoObject *tupleobj; gint notesintuple; gboolean ishomogenoustuple; gint prevduration; gint prevnumdots; gdouble fraction = 0; enum clefs type = DENEMO_TREBLE_CLEF; /* Append .abc onto the filename if necessary */ if (strcmp (filename->str + filename->len - 4, ".abc")) g_string_append (filename, ".abc"); /* Now open the file */ fp = fopen (filename->str, "w"); /* And cut off the filename extension for use in the file itself */ g_string_truncate (filename, filename->len - 4); fprintf (fp, _("%% ABC file generated by Denemo version ")); fprintf (fp, VERSION "\n\n"); fprintf (fp, "%% http://www.gnu.org/software/denemo/denemo.html\n\n"); /* We export the following headers in this order: * - X: (tune number) * - T: (title) * - C: (composer) * - Q: (tempo) * - M: (initial time signature) * - L: (initial default note length) * - %%staves (for the benefit of abcm2ps) * - K: (key signature) */ determinebasekey (firststaffstruct->skey_isminor ? firststaffstruct->skey + 3 : firststaffstruct->skey, &basekeyname); curtime.a = firststaffstruct->stime1; curtime.b = firststaffstruct->stime2; defaultlength = determinedefaultlength (&curtime); fprintf (fp, "X:1\n"); fprintf (fp, "T:%s\n", si->headerinfo->title->str); if (si->headerinfo->subtitle->len > 0) fprintf (fp, "T:%s\n", si->headerinfo->subtitle->str); if (si->headerinfo->composer->len > 0) fprintf (fp, "C:%s\n", si->headerinfo->composer->str); fprintf (fp, "Q:1/4=%d\n", si->tempo); fprintf (fp, "M:%d/%d\n", firststaffstruct->stime1, firststaffstruct->stime2); fprintf (fp, "L:1/%d\n", defaultlength); fraction = 1.0 / (gdouble) g_list_length (si->thescore); fraction /= 2; g_print ("Fraction %lf\n", fraction); /* Figure out the %%staves comment. It should look something like: * %%staves [{(1 2) 3} 4 (5 6)] [7 8] 9 * * where {} denotes curly braces (e.g. a piano, harp, or organ staff), * [] denotes square braces, * and () denotes two voices on the same staff. */ fprintf (fp, "%%%%staves ["); for (curstaff = si->thescore, curvoicenum = 1; curstaff != NULL; curstaff = curstaff->next, curvoicenum++) { curstaffstruct = (DenemoStaff *) curstaff->data; nextisnonprimary = (curstaff->next != NULL && ((DenemoStaff *) curstaff->next->data)->voicenumber > 1); curisnonprimary = (curstaffstruct->voicenumber > 1); if (curvoicenum != 1) fprintf (fp, " "); if (!curisnonprimary && nextisnonprimary) fprintf (fp, "("); fprintf (fp, "%d", curvoicenum); if (curisnonprimary && !nextisnonprimary) fprintf (fp, ")"); } fprintf (fp, "]\n"); fprintf (fp, "K:%s%s\n", basekeyname, firststaffstruct->skey_isminor ? "m" : ""); /* Now we output each voice in turn. */ for (curstaff = si->thescore, curvoicenum = 1; curstaff != NULL; curstaff = curstaff->next, curvoicenum++) { curstaffstruct = (DenemoStaff *) curstaff->data; determineclef (curstaffstruct->sclef, &clefname, &octaveshift); determinebasekey (curstaffstruct->skey_isminor ? curstaffstruct->skey + 3 : curstaffstruct->skey, &basekeyname); curtime.a = curstaffstruct->stime1; curtime.b = curstaffstruct->stime2; defaultlength = determinedefaultlength (&curtime); /* First, the V: header. FIXME: Output name="..." too. */ fprintf (fp, "%%\n%%\nV:%d clef=%s\n", curvoicenum, clefname); fprintf (fp, "I:octave=%d\n", -octaveshift); /* FIXME: nonstandard */ fprintf (fp, "M:%d/%d\n", curstaffstruct->stime1, curstaffstruct->stime2); fprintf (fp, "L:1/%d\n", defaultlength); fprintf (fp, "K:%s%s\n", basekeyname, curstaffstruct->skey_isminor ? "m" : ""); /* Then the actual notes, measure for measure (sorry, excuse the bad * Shakespeare pun). */ for (curmeasure = curstaffstruct->measures, internalmeasurenum = MAX (start, 1), externalmeasurenum = 1; curmeasure != NULL && (end == 0 || internalmeasurenum <= end); curmeasure = curmeasure->next, externalmeasurenum++, internalmeasurenum++) { /* Print the measure number every 5 measures. */ if (externalmeasurenum % 5 == 0) fprintf (fp, "%%%d\n", externalmeasurenum); emptymeasure = TRUE; /* Print out everything in this measure. */ for (curobjnode = (objnode *) curmeasure->data; curobjnode; curobjnode = curobjnode->next) { curobj = (DenemoObject *) curobjnode->data; switch (curobj->type) { case CHORD: emptymeasure = FALSE; printchord (fp, (chord *) curobj->object, octaveshift, defaultlength); if (curobj->isend_beamgroup) fprintf (fp, " "); break; case CLEF: type = ((clef *) curobj->object)->type; determineclef (type, &clefname, &octaveshift); fprintf (fp, "[K:%s][I:octave=%d]", clefname, -octaveshift); break; case KEYSIG: determinebasekey (((keysig *) curobj->object)->isminor ? ((keysig *) curobj->object)->number + 3 : ((keysig *) curobj->object)->number, &basekeyname); fprintf (fp, "[K:%s%s]", basekeyname, ((keysig *) curobj->object)->isminor ? "m" : ""); break; case TIMESIG: curtime.a = ((timesig *) curobj->object)->time1; curtime.b = ((timesig *) curobj->object)->time2; defaultlength = determinedefaultlength (&curtime); fprintf (fp, "[M:%d/%d][L:1/%d]", curtime.a, curtime.b, defaultlength); break; case TUPOPEN: /* Count the number of chords in the tuplet, and determine * whether they are all of the same length. * * Note: This could eventually cross measures, maybe, and * the current algorithm doesn't take that into * account. */ notesintuple = 0; ishomogenoustuple = TRUE; prevduration = G_MAXINT; prevnumdots = G_MAXINT; for (tupleobjnode = curobjnode->next; tupleobjnode != NULL; tupleobjnode = tupleobjnode->next) { tupleobj = (DenemoObject *) tupleobjnode->data; if (tupleobj->type == TUPCLOSE) break; if (tupleobj->type == CHORD) { notesintuple++; if (ishomogenoustuple) { if ((prevduration != G_MAXINT && prevduration != ((chord *) tupleobj->object)->baseduration) || (prevnumdots != G_MAXINT && prevnumdots != ((chord *) tupleobj->object)->numdots)) { ishomogenoustuple = FALSE; } else { prevduration = ((chord *) tupleobj->object)-> baseduration; prevnumdots = ((chord *) tupleobj->object)->numdots; } } /* End if ishomogenoustuple */ } /* End if tuple object is a chord */ } /* End for each object in tuple */ if (notesintuple == 3 && ishomogenoustuple && ((tupopen *) curobj->object)->numerator == 2 && ((tupopen *) curobj->object)->denominator == 3) /* a simple triplet */ fprintf (fp, "(3"); else fprintf (fp, "(%d:%d:%d", ((tupopen *) curobj->object)->denominator, ((tupopen *) curobj->object)->numerator, notesintuple); break; case GRACE_START: fprintf (fp, "{"); break; case GRACE_END: fprintf (fp, "}"); break; case TUPCLOSE: case STEMDIRECTIVE: case DYNAMIC: /* Do nothing at present. * FIXME: I should probably do the !pp! thing for dynamics * at some point, when it becomes mainstream in ABC * parsers. */ break; default: /* Danger, Will Robinson! */ g_warning ("Warning: unknown DenemoObject type \"%d\" " "found, ignoring", curobj->type); break; } /* End switch on object type */ } /* End for each note */ /* Print out (e.g.) "x3" for an empty measure. */ if (emptymeasure) fprintf (fp, "x%d ", curtime.a * (defaultlength / curtime.b)); /* And finally, of course, the barline. */ if (curmeasure->next) fprintf (fp, "|\n"); else fprintf (fp, "|]\n"); } /* End for each measure */ } /* End for each staff (voice) */ /* Clean up and go home. */ fclose (fp); g_string_free (filename, FALSE); }