/* exportmudela.c * Functions for actually exporting what Denemo's working on to a mudela file * * AJT 14/3/2000 Parametised for quick midi playback * for Denemo, a gtk+ frontend to GNU Lilypond * (c) 2000, 2001, 2002 Matthew Hiller, Adam Tee */ /* Yes -- if you're curious, this is a very straightforward adaptation * of what I used to export mudela in my extensions to kooBase. */ #include "config.h" #include #include "utils.h" #include #include #include #include #include #include "lyparserfuncs.h" /*#include "lyparser.h"*/ #include "exportmudela.h" #define FIGURES_SEP "|" /* a separator for groups of figured bass figures on one note this could be a user preference thingy */ /** * Output the lilypond repsentation of the * given staff context */ static void determinestaffcontext (gint number, gchar ** contextname) { switch (number) { case DENEMO_NONE: *contextname = "\\new Staff"; break; case DENEMO_PIANO: *contextname = "\\new PianoStaff"; break; case DENEMO_GROUP: *contextname = "\\new StaffGroup"; break; case DENEMO_CHOIR: *contextname = "\\new ChoirStaff"; break; } } /** * Output the lilypond representation of the given keysignature * */ static void determinekey (gint number, gchar ** keyname) { switch (number) { case -7: *keyname = "ces"; break; case -6: *keyname = "ges"; break; case -5: *keyname = "des"; break; case -4: *keyname = "aes"; break; case -3: *keyname = "ees"; break; case -2: *keyname = "bes"; break; case -1: *keyname = "f"; break; case 0: *keyname = "c"; break; case 1: *keyname = "g"; break; case 2: *keyname = "d"; break; case 3: *keyname = "a"; break; case 4: *keyname = "e"; break; case 5: *keyname = "b"; break; case 6: *keyname = "fis"; break; case 7: *keyname = "cis"; break; case 8: *keyname = "gis"; break; case 9: *keyname = "dis"; break; case 10: *keyname = "ais"; break; default: *keyname = _("%{error. defaulting to%}c"); break; } } /** * Output the lilypond representataion of the given clef * */ static void determineclef (gint type, gchar ** clefname) { switch (type) { case DENEMO_TREBLE_CLEF: *clefname = "treble"; break; case DENEMO_BASS_CLEF: *clefname = "bass"; break; case DENEMO_ALTO_CLEF: *clefname = "alto"; break; case DENEMO_G_8_CLEF: *clefname = "\"G_8\""; break; case DENEMO_TENOR_CLEF: *clefname = "tenor"; break; case DENEMO_SOPRANO_CLEF: *clefname = "soprano"; break; default: *clefname = _("%{error. defaulting to%}treble"); break; } /* I've found the quotes are necessary for ^ and _ clefs * to parse correctly */ } /** * Convert denemo duration to lilypond duration */ static gint internaltomuduration (gint internalduration) { return 1 << internalduration; } /** * append mudela duration information to FIGURES. * This could be optimized to remember the previous value and * avoid repetition - an initialization call would be needed to * set up initial values in that case */ static void append_duration (GString * figures, gint duration, gint numdots) { int i; g_string_sprintfa (figures, "%d", duration); for (i = 0; i < numdots; i++) figures = g_string_append (figures, "."); } /** * node is the current TEXT node, concatenate the string str to * its user_string * static void addstr (GList * node, gchar * str) { nodemin *n = (nodemin *) node->data; if (n->user_string) n->user_string = g_strconcat (n->user_string, str, NULL); else n->user_string = str; } */ /*#define OUTPUT_NODE_STR(str) do{if(output_type == TO_FILE) (void)fprintf(fp,"%s",str); \ else if(output_type == TO_NODE) addstr((GList*)out, str); \ else g_error("no such output type");}while(0) */ /** * recursively write out the NODES to file OUT or write to simplfied new tree OUT, depending on INPUT_TYPE The tree simplication comprises of replacing the tree with TEXT and DENEMO_MEASURES nodes: all of the nodes are just turned back into the original user text except for the DENEMO_MEASURES branches; this is so that the chunks of text can be freely edited using the text window. In particular, all the other nodes that are branches (SIMULTANEOUS, SEQENTIAL etc) are inlined. As this is the same process that is needed to write out the file, the two operations are combined in this function with output_type selecting. */ /*void lily_write_out (gpointer out, GList * nodes, enum lily_output_type output_type) { GList *g; #define fp ((FILE *)out) //#define outn ((GList *)out) gint prevduration = 0, prevnumdots = 0; * force explicit duration at start * for (g = nodes; g != 0; g = g->next) { #if DEBUG printf ("Writing: node type = %d string = %s\n", ((nodegeneric *) g->data)->type, ((nodegeneric *) g->data)->user_string); #endif switch (ntype (g)) { case SIMULTANEOUS: OUTPUT_NODE_STR (u_str (g)); lily_write_out (fp, br (g), output_type); if (output_type == TO_NODE) out = g_list_last ((GList *) out); * tricky, the recursive call does not move our outn on * OUTPUT_NODE_STR (u_post_str (g)); break; case DENEMO_MEASURES: if (output_type == TO_NODE) { * put in this node and then start a new TEXT node * nodemin *n = (nodemin *) g_malloc0 (sizeof (nodemin)); g_list_append ((GList *) out, g->data); n->type = TEXT; n->user_string = g_strdup (""); g_list_append ((GList *) out, n); out = (GList *) g_list_last ((GList *) out); } else { GList *measures; OUTPUT_NODE_STR (u_str (g)); for (measures = *(GList **) br (g); measures; measures = measures->next) { * OUTPUT_NODE_STR("\n"); * lily_write_out (fp, (GList *) (measures->data), TO_FILE); } OUTPUT_NODE_STR (u_post_str (g)); } break; case SEQUENTIAL: OUTPUT_NODE_STR (u_str (g)); lily_write_out (fp, br (g), output_type); if (output_type == TO_NODE) out = g_list_last ((GList *) out); * tricky, the recursive call does not move our outn on * OUTPUT_NODE_STR (u_post_str (g)); break; #if 1 case SCORE: OUTPUT_NODE_STR (u_str (g)); lily_write_out (fp, br (g), output_type); if (output_type == TO_NODE) out = g_list_last ((GList *) out); * tricky, the recursive call does not move our outn on * OUTPUT_NODE_STR (u_post_str (g)); break; #endif case '=': OUTPUT_NODE_STR (u_str (g)); lily_write_out (fp, br (g), output_type); if (output_type == TO_NODE) out = g_list_last ((GList *) out); * tricky, the recursive call does not move our outn on * break; case ADDLYRICS: OUTPUT_NODE_STR (u_str (g)); lily_write_out (fp, (GList *) (((GList *) br (g)->data)->data), output_type); if (output_type == TO_NODE) out = g_list_last ((GList *) out); lily_write_out (fp, (GList *) (((GList *) br (g)->next->data)->data), output_type); if (output_type == TO_NODE) out = g_list_last ((GList *) out); break; case CHORD: { * check for context changed due to graphically edited note if so delete the user_string so that it will be re-created * gint duration, numdots; DenemoObject *curobj = (DenemoObject *) g->data; chord chordval = *(chord *) curobj->object; duration = internaltomuduration (chordval.baseduration); numdots = chordval.numdots; if (prevduration && (duration != prevduration || numdots != prevnumdots)) { g_warning ("Discarding original text %s\n", u_str (g)); u_str (g) = NULL; *FIXME memory leak * } } * FALL THRU * default: if (!u_str (g)) u_str (g) = generate_lily (g); OUTPUT_NODE_STR (u_str (g)); break; } * outer switch * } * for nodes * #undef fp //#undef outn }*/ /** * add figures to *pfigures for *pchord */ static void output_figured_bass (DenemoScore * si, GString ** pfigures, chord * pchord) { GString *figures = *pfigures; gint duration = 4; //internaltomuduration (pchord->baseduration); gint numdots = pchord->numdots; GString *fig_str; /*working copy of figures string */ char *str; /* pointer into the figure string fig_str */ gint num_groups = 1; /* number of groups of figures */ if (!figures) figures = g_string_new ("<"); else figures = g_string_append (figures, "<"); if (pchord->figure == NULL || (((GString *) ((chord *) pchord->figure))->str) == NULL) //(((chord // *) ((DenemoObject // *) (((GList *) ((DenemoObject *) pchord->figure))->data))-> // object)->figure))->str == NULL) fig_str = g_string_new ("_"); /* the no-figure figure */ else fig_str = g_string_new (((GString *) ((chord *) pchord->figure))->str); //(((chord // *) ((DenemoObject // *) (((GList *) ((DenemoObject *) pchord-> // figure))->data))->object)-> // figure))->str); /* multiple figures are separated by a FIGURES_SEP char, output these at subdivisions of the duration */ str = index (fig_str->str, *(char *) FIGURES_SEP); if (str != NULL) { /* we have more than one group of figures to be output for one bass note. Count the number of groups */ num_groups = 2; /* one on either side of the FIGURES_SEP found */ while ((str = index (++str, *(char *) FIGURES_SEP)) != NULL) num_groups++; } switch (num_groups) { default: case 1: figures = g_string_append (figures, fig_str->str); figures = g_string_append (figures, ">"); append_duration (figures, duration, numdots); break; /* Each group of figures is assigned a duration to achieve a normal looking output */ case 2: { gint first_duration, second_duration; if (numdots) { /* divide unequally */ first_duration = duration; second_duration = duration * 2; } else { first_duration = second_duration = duration * 2; } str = strtok (fig_str->str, FIGURES_SEP); figures = g_string_append (figures, str); figures = g_string_append (figures, ">"); append_duration (figures, first_duration, 0); figures = g_string_append (figures, "<"); str = strtok (NULL, FIGURES_SEP); figures = g_string_append (figures, str); figures = g_string_append (figures, ">"); append_duration (figures, second_duration, 0); } break; case 3: { gint first_duration, second_duration, third_duration; if (numdots == 1) { /* divide equally */ first_duration = second_duration = third_duration = duration * 2; } else if (numdots == 2) { first_duration = second_duration = duration * 2; third_duration = duration * 4; } /* no more dots please! */ else { /* divide unequally */ first_duration = duration * 2; second_duration = third_duration = duration * 4; } str = strtok (fig_str->str, FIGURES_SEP); figures = g_string_append (figures, str); figures = g_string_append (figures, ">"); append_duration (figures, first_duration, 0); figures = g_string_append (figures, "<"); str = strtok (NULL, FIGURES_SEP); figures = g_string_append (figures, str); figures = g_string_append (figures, ">"); append_duration (figures, second_duration, 0); str = strtok (NULL, FIGURES_SEP); figures = g_string_append (figures, "<"); figures = g_string_append (figures, str); figures = g_string_append (figures, ">"); append_duration (figures, third_duration, 0); } break; } *pfigures = figures; } /** * add figures to *pfigures for *pchord */ static void output_fakechord (DenemoScore * si, GString ** pfakechord, chord * pchord) { GString *fakechord = *pfakechord; gint duration = internaltomuduration (pchord->baseduration); gint numdots = pchord->numdots; GString *fig_str, *extension; /*working copy of figures string */ char *str; /* pointer into the figure string fig_str */ gint num_groups = 1; /* number of groups of figures */ if (!fakechord) fakechord = g_string_new (" "); else fakechord = g_string_append (fakechord, " "); if (pchord->fakechord == NULL || (((GString *) ((chord *) pchord->fakechord))->str) == NULL) fig_str = g_string_new (" r"); /* the no-fakechord figure */ else { fig_str = g_string_new (((GString *) ((chord *) pchord->fakechord))->str); } if (pchord->fakechord_extension == NULL) extension = NULL; else { extension = g_string_new (((GString *) ((chord *) pchord->fakechord_extension))->str); } /* if (pchord->fakechord == NULL || ((GString *) (((chord *) ((DenemoObject *) (((GList *) ((DenemoObject *) pchord->fakechord))->data))-> object)->fakechord))->str == NULL) fig_str = g_string_new ("_"); else fig_str = g_string_new (((GString *) (((chord *) ((DenemoObject *) (((GList *) ((DenemoObject *) pchord-> fakechord))->data))->object)-> fakechord))->str); */ str = index (fig_str->str, *(char *) FIGURES_SEP); if (str != NULL) { /* we have more than one group of figures to be output for one bass note. Count the number of groups */ num_groups = 2; /* one on either side of the FIGURES_SEP found */ while ((str = index (++str, *(char *) FIGURES_SEP)) != NULL) num_groups++; } switch (num_groups) { default: case 1: fakechord = g_string_append (fakechord, fig_str->str); //fakechord = g_string_append (fakechord, " "); append_duration (fakechord, duration, numdots); if (extension != NULL) //printf("\nhas extenion in export mudela\n"); fakechord = g_string_append (fakechord, extension->str); break; /* Each group of fakechord is assigned a duration to achieve a normal looking output */ case 2: { gint first_duration, second_duration; if (numdots) { /* divide unequally */ first_duration = duration; second_duration = duration * 2; } else { first_duration = second_duration = duration * 2; } str = strtok (fig_str->str, FIGURES_SEP); fakechord = g_string_append (fakechord, str); fakechord = g_string_append (fakechord, " "); append_duration (fakechord, first_duration, 0); fakechord = g_string_append (fakechord, " "); str = strtok (NULL, FIGURES_SEP); fakechord = g_string_append (fakechord, str); fakechord = g_string_append (fakechord, " "); append_duration (fakechord, second_duration, 0); } break; case 3: { gint first_duration, second_duration, third_duration; if (numdots == 1) { /* divide equally */ first_duration = second_duration = third_duration = duration * 2; } else if (numdots == 2) { first_duration = second_duration = duration * 2; third_duration = duration * 4; } /* no more dots please! */ else { /* divide unequally */ first_duration = duration * 2; second_duration = third_duration = duration * 4; } str = strtok (fig_str->str, FIGURES_SEP); fakechord = g_string_append (fakechord, str); fakechord = g_string_append (fakechord, " "); append_duration (fakechord, first_duration, 0); fakechord = g_string_append (fakechord, " "); str = strtok (NULL, FIGURES_SEP); fakechord = g_string_append (fakechord, str); fakechord = g_string_append (fakechord, " "); append_duration (fakechord, second_duration, 0); str = strtok (NULL, FIGURES_SEP); fakechord = g_string_append (fakechord, " "); fakechord = g_string_append (fakechord, str); fakechord = g_string_append (fakechord, " "); append_duration (fakechord, third_duration, 0); } break; } *pfakechord = fakechord; } static void old_exportmudela (gchar * thefilename, DenemoScore * si, gint start, gint end); void exportmudela (gchar * thefilename, DenemoScore * si, gint dummy1, gint dummy2) { FILE *fp; GString *filename = g_string_new (thefilename); if (si->lily_file == NULL) { g_string_free (filename, TRUE); old_exportmudela (thefilename, si, dummy1, dummy2); } /* Append .ly onto the filename if necessary if (strcmp (filename->str + filename->len - 3, ".ly")) g_string_append (filename, ".ly"); Now open the file fp = fopen (filename->str, "w"); if (fp) { lily_write_out (fp, si->lily_file, TO_FILE); #if 0 why ? ? ? fprintf (fp, "\n"); #endif fclose (fp); } else g_error ("Could not open file %s\n", filename->str); g_string_free (filename, TRUE); */ } /** * generate the lilypond for the DenemoObject - side effects the * various other variables used by the old_exportmudela routine */ gchar * generate_lily_for_obj (DenemoScore * si, DenemoObject * curobj, GString ** plyrics, GString ** pfigures, GString ** pfakechords, gint * pprevduration, gint * pprevnumdots, gboolean * pempty_measure, gchar ** pclefname, gchar ** pkeyname, gint * pcur_stime1, gint * pcur_stime2) { GString *ret = g_string_new (""); GString *lyrics = *plyrics; GString *figures = *pfigures; GString *fakechords = *pfakechords; gint prevduration = *pprevduration; gint prevnumdots = *pprevnumdots; gboolean empty_measure = *pempty_measure; chord *pchord; gchar *clefname = *pclefname; gchar *keyname = *pkeyname; gint cur_stime1 = *pcur_stime1; gint cur_stime2 = *pcur_stime2; gchar temp[50]; gint j, k; gint duration, numdots; gboolean is_normalnotehead = TRUE; gboolean is_chordmode = FALSE; gint octave, enshift; gint noteheadtype; gint mid_c_offset; GList *curtone; gint extend; gboolean is_syllable = FALSE; gboolean center_lyric = FALSE; gint lyricsstaff; GString *dynamic_string = NULL; switch (curobj->type) { case CHORD: empty_measure = FALSE; pchord = (chord *) curobj->object; duration = internaltomuduration (pchord->baseduration); numdots = pchord->numdots; is_chordmode = FALSE; /*Lyrics */ if (pchord->lyric) { lyricsstaff = 0; if (is_syllable && !pchord->is_syllable) lyrics = g_string_append (lyrics, " "); if (!lyrics) lyrics = g_string_new (pchord->lyric->str); else lyrics = g_string_append (lyrics, pchord->lyric->str); if (pchord->center_lyric) { lyrics = g_string_append (lyrics, "- "); center_lyric = TRUE; } else center_lyric = FALSE; if (pchord->is_syllable) { is_syllable = TRUE; lyrics = g_string_append (lyrics, "__ "); } else { is_syllable = FALSE; lyrics = g_string_append (lyrics, " "); } } else if (is_syllable) { g_print ("duration %d\t mod %d\n", duration, duration % 4); if (duration < 4) { for (extend = 0; extend < duration % 4; extend++) lyrics = g_string_append (lyrics, "__ "); } else if (duration > 4) { for (extend = 0; extend < 4 % duration; extend++) lyrics = g_string_append (lyrics, "_"); } else lyrics = g_string_append (lyrics, "__ "); } else if (lyrics) { g_string_append_printf (lyrics, "\\skip "); /* only in this case do we explicitly note the duration */ g_string_append_printf (lyrics, "%d", duration); for (j = 0; j < numdots; j++) g_string_append_printf (lyrics, "."); lyrics = g_string_append (lyrics, " "); } /* figured bass stuff */ if (si && si->has_figures) output_figured_bass (si, &figures, pchord); /* end of figured bass stuff */ /* fake chord stuff */ if (si && si->has_fakechords) output_fakechord(si, &fakechords, pchord); /* end of fakechord stuff */ if (!pchord->tones) { /* A rest */ if (!curobj->isinvisible) { if (pchord->is_figure) ret = g_string_append (ret, ((GString *) pchord->figure)->str); else g_string_append_printf (ret, "r"); /* Duplicated code follows. I ought to fix that */ if (duration != prevduration || numdots != prevnumdots) { /* only in this case do we explicitly note the duration */ g_string_append_printf (ret, "%d", duration); prevduration = duration; prevnumdots = numdots; for (j = 0; j < numdots; j++) g_string_append_printf (ret, "."); } } else { g_string_append_printf (ret, "\\skip "); /* only in this case do we explicitly note the duration */ g_string_append_printf (ret, "%d", duration); prevduration = duration; prevnumdots = numdots; for (j = 0; j < numdots; j++) g_string_append_printf (ret, "."); } /*do this in caller g_string_append_printf (ret, " "); */ } else { GList *tmpornament; if (!curobj->isinvisible) { if (pchord->tones->next) { is_chordmode = TRUE; g_string_append_printf (ret, "<"); } for (curtone = pchord->tones; curtone; curtone = curtone->next) { noteheadtype = ((note *) curtone->data)->noteheadtype; switch (noteheadtype) { case DENEMO_NORMAL_NOTEHEAD: if (!is_normalnotehead) { g_string_append_printf (ret, "\n\t\\revert NoteHead #'style "); is_normalnotehead = !is_normalnotehead; } break; case DENEMO_CROSS_NOTEHEAD: g_string_append_printf (ret, "\n\t\\once \\override NoteHead #'style = #'cross "); is_normalnotehead = FALSE; break; case DENEMO_HARMONIC_NOTEHEAD: g_string_append_printf (ret, "\n\t\\once \\override NoteHead #'style = #'harmonic "); is_normalnotehead = FALSE; break; case DENEMO_DIAMOND_NOTEHEAD: g_string_append_printf (ret, "\n\t\\once \\override Voice.NoteHead #'style = #'diamond "); is_normalnotehead = FALSE; break; default: g_string_append_printf (ret, "\n\t\\revert Voice.NoteHead #'style "); break; } mid_c_offset = ((note *) curtone->data)->mid_c_offset; g_string_append_printf (ret, "%c", mid_c_offsettoname (mid_c_offset)); enshift = ((note *) curtone->data)->enshift; if (enshift < 0) for (k = enshift; k; k++) g_string_append_printf (ret, "es"); else for (k = enshift; k; k--) g_string_append_printf (ret, "is"); octave = mid_c_offsettooctave (mid_c_offset); if (octave < 0) for (; octave; octave++) g_string_append_printf (ret, ","); else for (; octave; octave--) g_string_append_printf (ret, "\'"); #if 0 /*contrary to what I wrote in denemo.h showaccidental is not to do with cautionary accidentals - there does not appear to be anything for these this code will result in forcing lilypond to show every accidental that denemo thinks should be shown */ if (((note *) curtone->data)->showaccidental) g_string_append_printf (ret, "!"); #endif if (curtone->next) g_string_append_printf (ret, " "); } /* End chord loop */ if (pchord->tones->next && pchord->has_dynamic) { sprintf (temp, ">"); } else if (pchord->tones->next) g_string_append_printf (ret, ">"); } else { g_string_append_printf (ret, "s"); } if (duration != prevduration || numdots != prevnumdots) { /* only in this case do we explicitly note the duration */ g_string_append_printf (ret, "%d", duration); prevduration = duration; prevnumdots = numdots; for (j = 0; j < numdots; j++) g_string_append_printf (ret, "."); } if (pchord->dynamics) { dynamic_string = (GString *) pchord->dynamics->data; if (is_chordmode) g_string_append_printf (ret, "\\%s", dynamic_string->str); else g_string_append_printf (ret, "\\%s ", dynamic_string->str); } for (tmpornament = pchord->ornamentlist; tmpornament; tmpornament = tmpornament->next) { g_print ("in tmpornament\n"); if (*(enum ornament *) tmpornament->data == (enum ornament) STACCATO) g_string_append_printf (ret, " \\staccato"); if (*(enum ornament *) tmpornament->data == (enum ornament) TENUTO) g_string_append_printf (ret, " \\tenuto"); if (*(enum ornament *) tmpornament->data == (enum ornament) D_ACCENT) g_string_append_printf (ret, " \\accent"); if (*(enum ornament *) tmpornament->data == (enum ornament) FERMATA) g_string_append_printf (ret, " \\fermata"); if (*(enum ornament *) tmpornament->data == (enum ornament) TRILL) g_string_append_printf (ret, " \\trill"); if (*(enum ornament *) tmpornament->data == (enum ornament) TURN) g_string_append_printf (ret, " \\turn"); if (*(enum ornament *) tmpornament->data == (enum ornament) MORDENT) g_string_append_printf (ret, " \\mordent"); if (*(enum ornament *) tmpornament->data == (enum ornament) STACCATISSIMO) g_string_append_printf (ret, " \\staccatissimo"); if (*(enum ornament *) tmpornament->data == (enum ornament) CODA) g_string_append_printf (ret, " \\coda"); if (*(enum ornament *) tmpornament->data == (enum ornament) FLAGEOLET) g_string_append_printf (ret, " \\flageolet"); if (*(enum ornament *) tmpornament->data == (enum ornament) OPEN) g_string_append_printf (ret, " \\open"); if (*(enum ornament *) tmpornament->data == (enum ornament) PRALLMORDENT) g_string_append_printf (ret, " \\prallmordent"); if (*(enum ornament *) tmpornament->data == (enum ornament) PRALLPRALL) g_string_append_printf (ret, " \\prallprall"); if (*(enum ornament *) tmpornament->data == (enum ornament) PRALL) g_string_append_printf (ret, " \\prall"); if (*(enum ornament *) tmpornament->data == (enum ornament) REVERSETURN) g_string_append_printf (ret, " \\reverseturn"); if (*(enum ornament *) tmpornament->data == (enum ornament) SEGNO) g_string_append_printf (ret, " \\segno"); if (*(enum ornament *) tmpornament->data == (enum ornament) STOPPED) g_string_append_printf (ret, " \\stopped"); if (*(enum ornament *) tmpornament->data == (enum ornament) THUMB) g_string_append_printf (ret, " \\thumb"); if (*(enum ornament *) tmpornament->data == (enum ornament) UPPRALL) g_string_append_printf (ret, " \\upprall"); if (*(enum ornament *) tmpornament->data == (enum ornament) D_ARPEGGIO) g_string_append_printf (ret, " \\arpeggio"); } if (pchord->crescendo_begin_p) g_string_append_printf (ret, " \\cr"); else if (pchord->diminuendo_begin_p) g_string_append_printf (ret, " \\decr"); if (pchord->crescendo_end_p) g_string_append_printf (ret, " \\rc"); else if (pchord->diminuendo_end_p) g_string_append_printf (ret, " \\rced"); if (pchord->slur_end_p) g_string_append_printf (ret, ")"); if (pchord->slur_begin_p) g_string_append_printf (ret, "("); if (pchord->is_tied) g_string_append_printf (ret, " ~"); /* do this in caller g_string_append_printf (ret, " "); */ } /* End code for dealing with chord */ break; case CLEF: determineclef (((clef *) curobj->object)->type, &clefname); g_string_append_printf (ret, "\\clef %s", clefname); break; case KEYSIG: determinekey (((keysig *) curobj->object)->isminor ? ((keysig *) curobj->object)->number + 3 : ((keysig *) curobj->object)->number, &keyname); g_string_append_printf (ret, "\\key %s", keyname); if (((keysig *) curobj->object)->isminor) g_string_append_printf (ret, " \\minor"); else g_string_append_printf (ret, " \\major"); /*do this in caller g_string_append_printf (ret, " "); */ break; case TIMESIG: g_string_append_printf (ret, "\\time %d/%d", ((timesig *) curobj->object)->time1, ((timesig *) curobj->object)->time2); cur_stime1 = ((timesig *) curobj->object)->time1; cur_stime2 = ((timesig *) curobj->object)->time2; break; case TUPOPEN: /* added by Yu CHeung "toby" Ho 3 Jun 00, adapted by Hiller * 8 Jun 00 (happy birthday to me...) :) */ g_string_append_printf (ret, "\\times %d/%d {", ((tupopen *) curobj->object)->numerator, ((tupopen *) curobj->object)->denominator); break; case TUPCLOSE: g_string_append_printf (ret, "}"); break; case GRACE_START: g_string_append_printf (ret, "\\grace {"); break; case GRACE_END: g_string_append_printf (ret, "}"); break; case STEMDIRECTIVE: switch (((stemdirective *) curobj->object)->type) { case DENEMO_STEMDOWN: g_string_append_printf (ret, "\\stemDown"); break; case DENEMO_STEMBOTH: g_string_append_printf (ret, "\\stemNeutral"); break; case DENEMO_STEMUP: g_string_append_printf (ret, "\\stemUp"); break; } break; case DYNAMIC: /*if (is_chordmode) { sprintf (dynamic_string, "-\\%s ", ((dynamic *)curobj->object)->type->str); strcat (dynamic_string, temp); g_string_append_printf (ret, "%s", dynamic_string); } else g_string_append_printf (ret, "-\\%s ", ((dynamic *)curobj->object)->type->str); */ break; /* case LILYDIRECTIVE: g_string_append_printf (ret, "%s", ((lilydirective *) curobj->object)->directive-> str); break; */ case BARLINE: switch (((barline *) curobj->object)->type) { case ORDINARY_BARLINE: g_string_append (ret, "|\n"); break; case DOUBLE_BARLINE: g_string_append (ret, "\\bar \"||\"\n"); break; case END_BARLINE: g_string_append (ret, "\\bar \"|.\"\n"); break; case OPENREPEAT_BARLINE: g_string_append (ret, "\\bar \"|:\"\n"); break; case CLOSE_REPEAT: g_string_append (ret, "\\bar \":|\"\n"); break; case OPEN_CLOSE_REPEAT: g_string_append (ret, "\\bar \":\"\n"); break; } break; case LYRIC: case FIGURE: break; default: break; } *plyrics = lyrics; *pfigures = figures; *pfakechords = fakechords; *pprevduration = prevduration; *pprevnumdots = prevnumdots; *pempty_measure = empty_measure; *pclefname = clefname; *pkeyname = keyname; *pcur_stime1 = cur_stime1; *pcur_stime2 = cur_stime2; return ret->str; } /** * generate lilypond text for the object node passed in - the string should * be g_freed when finished with by the caller */ gchar * generate_lily (objnode * obj) { gchar *clefname; gchar *keyname; gboolean empty_measure; gint cur_stime1; gint cur_stime2; gint prevduration = -1, prevnumdots = -1; GString *lyrics = NULL; GString *figures = NULL; GString *fakechords = NULL; objnode *n; gchar *ret, *gen; /* * find prevduration if any - we need to add a link to the DenemoObject * struct back to the measure it lives in, so that we can look back to * previous measures as well */ for (n = obj->prev; n; n = n->prev) { if (((DenemoObject *) n->data)->type == CHORD) { chord *pchord = (chord *) ((DenemoObject *) n->data)->object; prevduration = internaltomuduration (pchord->baseduration); prevnumdots = pchord->numdots; break; } } gen = generate_lily_for_obj (NULL, (DenemoObject *) obj->data, &lyrics, &figures, &fakechords, &prevduration, &prevnumdots, &empty_measure, &clefname, &keyname, &cur_stime1, &cur_stime2); ret = g_strconcat (" ", gen, NULL); g_free (gen); /* FIXME free figures and lyrics if generated */ return ret; } /** * Output the header information using Lilypond syntax * * */ static void outputHeader (FILE * fp, DenemoScore * si) { fprintf (fp, _("%% LilyPond file generated by Denemo version ")); fprintf (fp, VERSION "\n\n"); fprintf (fp, "%%http://denemo.sourceforge.net/\n\n"); /*Print out lilypond syntax version */ fprintf (fp, "\\version \"%s\"\n", si->sconfig->lilyversion->str); /* header stuff */ fprintf (fp, "\\header{\n"); fprintf (fp, "\ttitle = \"%s\"\n", si->headerinfo->title->str); fprintf (fp, "\tsubtitle = \"%s\"\n", si->headerinfo->subtitle->str); fprintf (fp, "\tpoet = \"%s\"\n", si->headerinfo->poet->str); fprintf (fp, "\tcomposer = \"%s\"\n", si->headerinfo->composer->str); fprintf (fp, "\tmeter = \"%s\"\n", si->headerinfo->meter->str); fprintf (fp, "\topus = \"%s\"\n", si->headerinfo->opus->str); fprintf (fp, "\tarranger = \"%s\"\n", si->headerinfo->arranger->str); fprintf (fp, "\tinstrument = \"%s\"\n", si->headerinfo->instrument->str); fprintf (fp, "\tdedication = \"%s\"\n", si->headerinfo->dedication->str); fprintf (fp, "\tpiece = \"%s\"\n", si->headerinfo->piece->str); fprintf (fp, "\thead = \"%s\"\n", si->headerinfo->head->str); fprintf (fp, "\tcopyright = \"%s\"\n", si->headerinfo->copyright->str); fprintf (fp, "\tfooter = \"%s\"\n", si->headerinfo->footer->str); fprintf (fp, "\ttagline = \"%s\"\n", si->headerinfo->tagline->str); fprintf (fp, "}\n\n"); fprintf (fp, "#(set-global-staff-size %d)\n", si->sconfig->fontsize); fprintf (fp, "#(set-default-paper-size \"%s\")\n", si->sconfig->papersize->str); fprintf (fp, "\n\n"); } /** * Output a Denemo Staff in Lilypond syntax * * * */ static void outputStaff (FILE * fp, DenemoScore * si, DenemoStaff * curstaffstruct, gint start, gint end, GString ** lyrics, GString ** figures, GString ** fakechords) { gboolean empty_measure; gint cur_stime1; gint cur_stime2; gint prevduration, prevnumdots; gchar *clefname; /* clef name */ gchar *keyname; /* key signature name */ measurenode *curmeasure; objnode *curobjnode; DenemoObject *curobj; gint curmeasurenum; gint i, last = 0, p; prevduration = 0; prevnumdots = -1; fprintf (fp, "%s = \\context Voice = %s {\n", curstaffstruct->lily_name->str, curstaffstruct->lily_name->str); /* When Lilypond changes such that it no longer accepts * '$', fix this. Of course, it's problematic that numerals * are really useful in staff names... */ /* RTS - I've fixed this and fixed set_lily_name() to generate roman numerals. */ if (curstaffstruct->no_of_lines != 5) fprintf (fp, "\n\t\\override Staff.StaffSymbol #'line-count = #%d\n", curstaffstruct->no_of_lines); if (curstaffstruct->transposition != 0) fprintf (fp, "\n\t\\set Staff.transposing = #%d\n", curstaffstruct->transposition); /* Write a comment for Denemo to recognize later if this is not * a primary voice */ if (curstaffstruct->voicenumber == 2) fprintf (fp, "%%!Nonprimary Voice\n"); /* I ought to get rid of Mr. Magic Numeric Constant there */ /* The midi instrument */ fprintf (fp, "\t\\set Staff.midiInstrument = \"%s\"\n", curstaffstruct->midi_instrument->str); /* Time signature */ fprintf (fp, "\t\\time %d/%d\n", curstaffstruct->stime1, curstaffstruct->stime2); cur_stime1 = curstaffstruct->stime1; cur_stime2 = curstaffstruct->stime2; /* Determine the key signature */ determinekey (curstaffstruct->skey_isminor ? curstaffstruct->skey + 3 : curstaffstruct->skey, &keyname); fprintf (fp, "\t\\key %s", keyname); if (curstaffstruct->skey_isminor) fprintf (fp, " \\minor"); else fprintf (fp, " \\major"); fprintf (fp, "\n"); /* Determine the clef */ determineclef (curstaffstruct->sclef, &clefname); fprintf (fp, "\t\\clef %s\n", clefname); curmeasurenum = 0; curmeasure = curstaffstruct->measures; if (!end) last = g_list_length (curmeasure); /* Now each measure */ if (start) curmeasure = g_list_nth (curmeasure, start - 1); for (i = MAX (start, 1); curmeasure && i <= last; curmeasure = curmeasure->next, i++) { empty_measure = TRUE; if ((++curmeasurenum % 5) == 0) fprintf (fp, "%%%d\n", curmeasurenum); fprintf (fp, "\t"); for (curobjnode = (objnode *) curmeasure->data; curobjnode; curobjnode = curobjnode->next) { curobj = (DenemoObject *) curobjnode->data; { gchar *str = generate_lily_for_obj (si, curobj, lyrics, figures, fakechords, &prevduration, &prevnumdots, &empty_measure, &clefname, &keyname, &cur_stime1, &cur_stime2); if (str) { fprintf (fp, "%s ", str); g_free (str); } } } /* Done with this measure */ if (empty_measure) { fprintf (fp, "s1*%d/%d ", cur_stime1, cur_stime2); prevduration = 0; } if (curmeasure->next) fprintf (fp, "|\n"); else fprintf (fp, "\\bar \"|.\"\n"); } /* Done with this staff */ fprintf (fp, "}\n"); } /** * Actually export the mudela. This could've been done with lots * of g_list_foreach'es, but for the most part I consider those things * to be cumbersome. * * The function works in two passes: the first pass writes out all the * voices; the second pass writes out the score block. * I'm also planning to add a third pass * that will write out additional score blocks for instrumental parts, * as instructed by the user, but this is not implemented yet. * * The loading routines, in contrast, glean all the information * they need about the score from the information written in the first * pass. */ static void old_exportmudela (gchar * thefilename, DenemoScore * si, gint start, gint end) { FILE *fp; staffnode *curstaff; DenemoStaff *curstaffstruct; gint p = 0; gdouble fraction = 0.00; GString *filename = g_string_new (thefilename); // gboolean first_staff = TRUE; //gboolean invisible = FALSE; gboolean context = FALSE; DenemoContext curcontext = DENEMO_NONE; /*figured basses */ GString *figures = NULL; GString *fakechords = NULL; /*lyrics */ GString *lyrics = NULL; GList *lyricsstaffs = NULL; /* Append .ly onto the filename if necessary */ if (strcmp (filename->str + filename->len - 3, ".ly")) g_string_append (filename, ".ly"); /* Now open the file */ fp = fopen (filename->str, "w"); if (!fp) { g_warning ("Cannot open %s \n", filename->str); return; } /* And cut off the filename extension for use in the file itself */ g_string_truncate (filename, filename->len - 3); outputHeader (fp, si); //g_print("Fraction %lf\n", fraction); fraction = 1 / g_list_length (si->thescore); fraction /= 4; g_print ("Num staffs Fraction %lf\n", fraction); /* First pass */ for (curstaff = si->thescore; curstaff; curstaff = curstaff->next) { curstaffstruct = (DenemoStaff *) curstaff->data; outputStaff (fp, si, curstaffstruct, start, end, &lyrics, &figures, &fakechords); if (lyrics) { fprintf (fp, "%sLyrics = \\lyricmode { \n", curstaffstruct->lily_name->str); fprintf (fp, "%s", lyrics->str); fprintf (fp, " \n}\n"); lyricsstaffs = g_list_append (lyricsstaffs, curstaffstruct->lily_name->str); // g_free(lyrics); lyrics = NULL; } } /* Now to create staffs; second pass. This pass will also put * score blocks in for each staff -- useful for part extraction -- * but it'll comment them out with Lilypond block-comment markers * first */ if (figures) { fprintf (fp, "BassFiguresLine = \\context FiguredBass\n" "\\figures {\n"); fprintf (fp, "%s", figures->str); fprintf (fp, " \n}\n"); } if (fakechords) { fprintf (fp, "FakeChordLine = \\context ChordNames =\n" "FakeChordLine {\n" "\t\\chordmode{\n"); fprintf (fp, "\t%s", fakechords->str); fprintf (fp, " \n\t}\n}\n"); } g_print ("Fraction %lf\n", fraction); /* Now make the master score; third pass */ fprintf (fp, "\\score {\n << \n"); for (curstaff = si->thescore, p = 0; curstaff; curstaff = curstaff->next, p++) { curstaffstruct = (DenemoStaff *) curstaff->data; if (((curstaffstruct->context == DENEMO_NONE) && context) || ((curstaffstruct->context != curcontext) && context)) { fprintf (fp, "\t>>\n"); context = FALSE; } if (!context) { gchar *tmpcontext; determinestaffcontext (curstaffstruct->context, &tmpcontext); fprintf(fp, "\t %s <<\n ",tmpcontext); context = TRUE; curcontext = curstaffstruct->context; } if (si->has_fakechords) fprintf (fp, "\t\t\\FakeChordLine\n"); if (curstaffstruct->voicenumber != 2) fprintf (fp, "\t\t\\%s\n", curstaffstruct->lily_name->str); else if (curstaffstruct->voicenumber == 2 /*&& curstaffstruct->context == DENEMO_NONE */ ) fprintf (fp, "\t\t\\%s\n", curstaffstruct->lily_name->str); } if (lyricsstaffs) { GList *tmp; for (tmp = lyricsstaffs; tmp; tmp = tmp->next) { fprintf (fp, "\n\t\t \\lyricsto %s \\new Lyrics " "\\%sLyrics\n", (gchar *) tmp->data, (gchar *) tmp->data, (gchar *) tmp->data); } } if (si->has_figures) fprintf (fp, "\t\t\\BassFiguresLine\n"); if (context) fprintf (fp, " >> \n"); fprintf (fp, "\t>>\n" "\t\\layout {\n" "\t}\n" "\t\\midi {\n" "\t\t\\tempo 4 = %d\n" "\t}\n" "}\n", si->tempo); /* Third pass finished */ fclose (fp); g_string_free (filename, TRUE); } void export_lilypond_parts (char *filename, DenemoScore * si, gint start, gint end) { FILE *fp; gchar *staff_filename; staffnode *curstaff; DenemoStaff *curstaffstruct; GString *lyrics = NULL; GString *figures = NULL; GString *fakechords = NULL; GList *lyricsstaffs = NULL; for (curstaff = si->thescore; curstaff; curstaff = curstaff->next) { lyrics = NULL; figures = NULL; fakechords = NULL; lyricsstaffs = NULL; curstaffstruct = (DenemoStaff *) curstaff->data; staff_filename = strtok(filename, "."); staff_filename = g_strconcat(staff_filename, "_", curstaffstruct->lily_name->str, ".ly", NULL); fp = fopen (staff_filename, "w"); if (!fp) { g_warning ("Cannot open %s \n", staff_filename); return; } outputHeader (fp, si); outputStaff (fp, si, curstaffstruct, start, end, &lyrics, &figures, &fakechords); if (lyrics) { fprintf (fp, "%sLyrics = \\lyricmode { \n", curstaffstruct->lily_name->str); fprintf (fp, "%s", lyrics->str); fprintf (fp, " \n}\n"); lyricsstaffs = g_list_append (lyricsstaffs, curstaffstruct->lily_name->str); // g_free(lyrics); } if (figures) { fprintf (fp, "BassFiguresLine = \\context FiguredBass\n" "\\figures {\n" /*"\\set FiguredBass.BassFigure\n" " \\override #'font-relative-size = #-3\n" */ ); fprintf (fp, "%s", figures->str); fprintf (fp, " \n}\n"); } /* Now make the master score; third pass */ fprintf (fp, "\\score {\n \t<<\n"); if (curstaffstruct->voicenumber != 2) fprintf (fp, "\t\t\\%s\n", curstaffstruct->lily_name->str); else if (curstaffstruct->voicenumber == 2 /*&& curstaffstruct->context == DENEMO_NONE */ ) fprintf (fp, "\t\t\\%s\n", curstaffstruct->lily_name->str); if (lyricsstaffs) { GList *tmp; for (tmp = lyricsstaffs; tmp; tmp = tmp->next) { fprintf (fp, "\n\t\t \\lyricsto %s \\new Lyrics " "\\%sLyrics\n", (gchar *) tmp->data, (gchar *) tmp->data, (gchar *) tmp->data); } } if (si->has_figures) fprintf (fp, "\t\t\\BassFiguresLine\n"); fprintf (fp, "\t>>\n" "\t\\layout {\n" "\t}\n" "\t\\midi {\n" "\t\t\\tempo 4 = %d\n" "\t}\n" "}\n", si->tempo); fclose (fp); } if (staff_filename) g_free (staff_filename); if (lyrics) g_string_free (lyrics, TRUE); if (figures) g_string_free (figures, TRUE); }