From 80afafcbe4ccf57c286f2fecf1e0ddbe8452f4c9 Mon Sep 17 00:00:00 2001 From: "Henry So, Jr." Date: Sat, 5 Feb 2022 16:56:01 -0500 Subject: [PATCH] Implemented [shape:] hints in gabc and [shape:stroke] for the flexus. Part of the implementation for #1558 --- .editorconfig | 2 +- doc/Gabc.tex | 13 +++++ src/dump/dump.c | 16 ++++-- src/gabc/gabc-notes-determination.l | 14 ++++- src/gabc/gabc-score-determination.c | 12 ++-- src/gabc/gabc-write.c | 86 +++++++++++++++-------------- src/gregoriotex/gregoriotex-write.c | 44 +++++++++++++++ src/struct.c | 25 +++++++-- src/struct.h | 4 +- 9 files changed, 154 insertions(+), 62 deletions(-) diff --git a/.editorconfig b/.editorconfig index 8154a87fe..61338eadf 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,5 +8,5 @@ indent_size = 2 charset = utf-8 trim_trailing_whitespace = true -[*.{py,c,h}] +[*.{py,c,h,l,y}] indent_size = 4 diff --git a/doc/Gabc.tex b/doc/Gabc.tex index 32189a945..e56f91deb 100644 --- a/doc/Gabc.tex +++ b/doc/Gabc.tex @@ -647,6 +647,19 @@ \subsubsection{Neume Spacing}\label{neumespacing} space, makes the space that follows a non-breaking space \\ \end{tabularx} +\subsubsection{Shape Hints} + +In some cases, an alternate form of a shape is desired. To do this, put a +\texttt{[shape:}\textit{hint}\texttt{]} after the figure that needs to be +altered. The available alternate form hints are as follows: + +\begin{tabularx}{\textwidth}{l|X} + \textit{hint} & Description \\ + \hline + \texttt{stroke} & Render a clivis/flexus as a stroke (like in a porrectus) + rather than as two notes \\ +\end{tabularx} + \subsubsection{Additional Symbols} Puncta mora, episemata, and other symbols may also be added to a note by diff --git a/src/dump/dump.c b/src/dump/dump.c index 77fcfeea1..20faa9b1e 100644 --- a/src/dump/dump.c +++ b/src/dump/dump.c @@ -5,19 +5,19 @@ * Copyright (C) 2007-2021 The Gregorio Project (see CONTRIBUTORS.md) * * This file is part of Gregorio. - * + * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) * any later version. - * + * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . */ #include "config.h" @@ -424,9 +424,13 @@ void dump_write_score(FILE *f, gregorio_score *score) gregorio_texverb(note->texverb)); } if (note->choral_sign) { - fprintf(f, " Choral Sign \"%s\"\n", + fprintf(f, " choral sign \"%s\"\n", note->choral_sign); } + if (note->shape_hint) { + fprintf(f, " shape hint \"%s\"\n", + note->shape_hint); + } if (note->signs) { fprintf(f, " signs %d (%s)\n", note->signs, diff --git a/src/gabc/gabc-notes-determination.l b/src/gabc/gabc-notes-determination.l index d142b546a..9fc185f3a 100644 --- a/src/gabc/gabc-notes-determination.l +++ b/src/gabc/gabc-notes-determination.l @@ -778,6 +778,7 @@ void gabc_det_notes_finish(void) %x overledger overledger2 %x underledger underledger2 %x endledger +%x shapehint %% \% { @@ -1021,11 +1022,11 @@ void gabc_det_notes_finish(void) gregorio_add_texverb_to_note(current_note, gabc_unescape(tempstr)); } (\$.|[^\]])+ { - gregorio_add_cs_to_note(¤t_note, + gregorio_add_cs_to_note(current_note, gabc_unescape(gabc_notes_determination_text), false); } (\$.|[^\]])+ { - gregorio_add_cs_to_note(¤t_note, + gregorio_add_cs_to_note(current_note, gabc_unescape(gabc_notes_determination_text), true); } (\$.|[^\]])+ { @@ -1109,7 +1110,14 @@ void gabc_det_notes_finish(void) add_variable_ledger(SO_UNDER, gabc_notes_determination_text); BEGIN(endledger); } -\] { +\[shape: { + BEGIN(shapehint); + } +(\$.|[^\]])+ { + gregorio_add_shape_hint_to_note(current_note, + gabc_unescape(gabc_notes_determination_text)); + } +\] { BEGIN(INITIAL); } \[[ou]slur:[012];[^,]+,[^\]]+\] { diff --git a/src/gabc/gabc-score-determination.c b/src/gabc/gabc-score-determination.c index 31a69c8cc..6dd5f6aa6 100644 --- a/src/gabc/gabc-score-determination.c +++ b/src/gabc/gabc-score-determination.c @@ -6,18 +6,18 @@ * Copyright (C) 2016-2021 The Gregorio Project (see CONTRIBUTORS.md) * * This file is part of Gregorio. - * + * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) * any later version. - * + * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. - * - * You should have received a copy of the GNU General Public License along with + * + * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ @@ -662,8 +662,8 @@ char *gabc_unescape(const char *const string) { /* * in this context, unescape means to discard any special meaning of a - * character that follows a backslash. Thus backslash-{something} is - * reduced to {something} + * character that follows a dollar sign. Thus $-{something} is reduced + * to {something} */ char *result, *to; const char *from = string; diff --git a/src/gabc/gabc-write.c b/src/gabc/gabc-write.c index 7e5985de9..9ebed376a 100644 --- a/src/gabc/gabc-write.c +++ b/src/gabc/gabc-write.c @@ -5,20 +5,20 @@ * Copyright (C) 2006-2021 The Gregorio Project (see CONTRIBUTORS.md) * * This file is part of Gregorio. - * + * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) * any later version. - * + * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. - * - * You should have received a copy of the GNU General Public License along with + * + * You should have received a copy of the GNU General Public License along with * this program. If not, see . - * + * * This is a simple and easyly understandable output module. If you want to * write a module, you can consider it as a model. */ @@ -64,7 +64,7 @@ static __inline void unsupported(const char *fn, const int line, /* LCOV_EXCL_STOP */ /* - * Output one attribute, allowing for multi-line values + * Output one attribute, allowing for multi-line values */ static void gabc_write_str_attribute(FILE *f, const char *name, const char *attr) @@ -75,13 +75,13 @@ static void gabc_write_str_attribute(FILE *f, const char *name, } /* - * + * * Then we start the functions made to write the text of the syllable. See * comments on struct.h and struct-utils.c to understand more deeply. - * + * * This first function will be called each time we will encounter a * gregorio_character which is the beginning of a style. - * + * */ static void gabc_write_begin(FILE *f, grestyle_style style) @@ -133,9 +133,9 @@ static void gabc_write_begin(FILE *f, grestyle_style style) } /* - * + * * This function is about the same but for ends of styles. - * + * */ static void gabc_write_end(FILE *f, grestyle_style style) @@ -189,11 +189,11 @@ static void gabc_write_end(FILE *f, grestyle_style style) } /* - * + * * The function called when we will encounter a character. There may be other - * representations of the character (for example for Omega), so it is necessary + * representations of the character (for example for Omega), so it is necessary * to have such a function defined in each module. - * + * */ static void gabc_print_char(FILE *f, const grewchar to_print) @@ -263,11 +263,11 @@ static void gabc_print_string(FILE *f, const char *first_char) } /* - * + * * This function writes the special chars. As the specials chars are - * represented simply in gabc, this function is very simple, but for TeX output + * represented simply in gabc, this function is very simple, but for TeX output * modules, this may be.. a little more difficult. - * + * */ static void gabc_write_special_char(FILE *f, const grewchar *first_char) { @@ -277,10 +277,10 @@ static void gabc_write_special_char(FILE *f, const grewchar *first_char) } /* - * + * * This functions writes verbatim output... but as the previous one it is very * simple. - * + * */ static void gabc_write_verb(FILE *f, const grewchar *first_char) { @@ -295,10 +295,10 @@ static void gabc_write_verb(FILE *f, const grewchar *first_char) } /* - * + * * Quite important: the function that writes the liquescentia. It is called at * the end of the function that writes one glyph. - * + * */ static void gabc_write_end_liquescentia(FILE *f, char liquescentia) @@ -317,9 +317,9 @@ static void gabc_write_end_liquescentia(FILE *f, char liquescentia) } /* - * + * * The function that writes a key change... quite simple. - * + * */ static void gabc_write_clef(FILE *f, gregorio_clef_info clef) @@ -333,9 +333,9 @@ static void gabc_write_clef(FILE *f, gregorio_clef_info clef) } /* - * + * * The function that writes spaces, called when we encounter one. - * + * */ static void gabc_write_space(FILE *f, gregorio_space type, char *factor, @@ -382,9 +382,9 @@ static void gabc_write_space(FILE *f, gregorio_space type, char *factor, } /* - * + * * A function to write a bar. - * + * */ static void gabc_write_bar(FILE *f, gregorio_bar type) @@ -583,9 +583,9 @@ typedef struct glyph_context { } glyph_context; /* - * + * * The function that writes one gregorio_note. - * + * */ static void gabc_write_gregorio_note(FILE *f, gregorio_note *note, @@ -778,6 +778,12 @@ static void gabc_write_gregorio_note(FILE *f, gregorio_note *note, gabc_print_string(f, gregorio_texverb(note->texverb)); fprintf(f, "]"); } + if (note->choral_sign) { + fprintf(f, "[cs:%s]", note->choral_sign); + } + if (note->shape_hint) { + fprintf(f, "[shape:%s]", note->shape_hint); + } } static void get_next_hepisema_adjustments(unsigned short *adjustment_index, @@ -916,11 +922,11 @@ static __inline void close_hepisema_adjustment(FILE *const f, } /* - * + * * The function that writes one glyph. If it is really a glyph (meaning not a * space or an alteration), we just do like always, a loop on the notes and a * call to the function that writes one note on each of them. - * + * */ static void gabc_write_gregorio_glyph(FILE *f, gregorio_glyph *glyph, @@ -1018,12 +1024,12 @@ static void gabc_write_gregorio_glyph(FILE *f, gregorio_glyph *glyph, } /* - * - * To write an element, first we check the type of the element (if it is a bar, + * + * To write an element, first we check the type of the element (if it is a bar, * etc.), and if it is really an element, we make a loop on the list of glyphs * inside the neume, and for each of them we call the function that will write * one glyph. - * + * */ static void gabc_write_gregorio_element(FILE *f, gregorio_element *element, @@ -1115,11 +1121,11 @@ static void gabc_write_gregorio_element(FILE *f, gregorio_element *element, } /* - * + * * Here is defined the function that will write the list of gregorio_elements. * It is very simple: it makes a loop in which it calls a function that writes * one element. - * + * */ static bool gabc_write_gregorio_elements(FILE *f, gregorio_element *element, @@ -1146,9 +1152,9 @@ static bool gabc_write_gregorio_elements(FILE *f, gregorio_element *element, } /* - * + * * Here it goes, we are writing a gregorio_syllable. - * + * */ static void gabc_write_gregorio_syllable(FILE *f, gregorio_syllable *syllable, @@ -1208,10 +1214,10 @@ static void gabc_write_gregorio_syllable(FILE *f, gregorio_syllable *syllable, } /* - * + * * This is the top function, the one called when we want to write a * gregorio_score in gabc. - * + * */ void gabc_write_score(FILE *f, gregorio_score *score) diff --git a/src/gregoriotex/gregoriotex-write.c b/src/gregoriotex/gregoriotex-write.c index ab913f219..ddf0537ee 100644 --- a/src/gregoriotex/gregoriotex-write.c +++ b/src/gregoriotex/gregoriotex-write.c @@ -237,6 +237,39 @@ static queuetype queuetype_of(const gregorio_note *const note) { static grestyle_style gregoriotex_ignore_style = ST_NO_STYLE; static grestyle_style gregoriotex_next_ignore_style = ST_NO_STYLE; +static bool glyph_hint(const gregorio_glyph *const glyph, + const char *const hint) { + const char *const shape_hints = gregorio_glyph_last_note(glyph)->shape_hint; + if (shape_hints == NULL) { + /* no hints */ + return false; + } else { + const char *const found = strstr(shape_hints, hint); + if (found == NULL) { + /* no match */ + return false; + } else { + const char suffix = found[strlen(hint)]; + if (suffix == '\0' || suffix == ',') { + /* one must be true or it's not a match */ + if (found == shape_hints) { + /* found it at the start of shape_hints */ + return true; + } else { + /* + * we're somewhere after the start, so it's ok to get the + * character before; a comma before the start of the found + * string is the only remaining way it can be a match + */ + return (*(found - 1) == ','); + } + } else { + return false; + } + } + } +} + /* * The different liquescentiae are: * 'Nothing' @@ -500,6 +533,17 @@ static const char *compute_glyph_name(const gregorio_glyph *const glyph, * for the liquescent forms */ shape = SHAPE_Salicus; } + if (!fuse_to_next_note && (shape == SHAPE_Flexus + || shape == SHAPE_FlexusLongqueue + || shape == SHAPE_FlexusOpenqueue + || shape == SHAPE_FlexusNobar) + && liquescentia == LIQ_Nothing + && glyph_hint(glyph, "stroke")) { + /* "fake" fusion to get the shape we want */ + fuse_ambitus = 1; + liquescentia = ""; + fuse_tail = FUSE_Up; + } current_note = current_note->next; if (!current_note->next) { gregorio_snprintf(buf, BUFSIZE, "%s%s%s%s%s%s", fuse_head, shape, diff --git a/src/struct.c b/src/struct.c index 788f9e5b0..6871df425 100644 --- a/src/struct.c +++ b/src/struct.c @@ -356,7 +356,7 @@ static __inline void change_note_texverb(gregorio_note *note, char *str) } } -void gregorio_add_texverb_to_note(gregorio_note *current_note, char *str) +void gregorio_add_texverb_to_note(gregorio_note *const current_note, char *str) { size_t len; char *res; @@ -376,12 +376,26 @@ void gregorio_add_texverb_to_note(gregorio_note *current_note, char *str) } } -void gregorio_add_cs_to_note(gregorio_note *const*const current_note, +void gregorio_add_cs_to_note(gregorio_note *const current_note, char *const str, const bool nabc) { - if (*current_note) { - (*current_note)->choral_sign = str; - (*current_note)->choral_sign_is_nabc = nabc; + if (current_note) { + if (current_note->choral_sign) { + free(current_note->choral_sign); + } + current_note->choral_sign = str; + current_note->choral_sign_is_nabc = nabc; + } +} + +void gregorio_add_shape_hint_to_note(gregorio_note *const current_note, + char *const str) +{ + if (current_note) { + if (current_note->shape_hint) { + free(current_note->shape_hint); + } + current_note->shape_hint = str; } } @@ -708,6 +722,7 @@ static __inline void free_one_note(gregorio_note *note) } free_one_texverb(note->texverb); free(note->choral_sign); + free(note->shape_hint); free(note); } diff --git a/src/struct.h b/src/struct.h index 025b23e72..7c1e95e6b 100644 --- a/src/struct.h +++ b/src/struct.h @@ -456,6 +456,7 @@ typedef struct gregorio_note { /* choral sign is a letter that appears next to a note in some choral * scores we put it as char* because sometimes two letters appear */ char *choral_sign; + char *shape_hint; union { /* note is used for GRE_NOTE */ struct { @@ -892,8 +893,9 @@ void gregorio_start_autofuse(gregorio_note **current_note, void gregorio_end_autofuse(gregorio_note **current_note, const gregorio_scanner_location *loc); void gregorio_add_texverb_to_note(gregorio_note *current_note, char *str); -void gregorio_add_cs_to_note(gregorio_note *const*current_note, char *str, +void gregorio_add_cs_to_note(gregorio_note *current_note, char *str, bool nabc); +void gregorio_add_shape_hint_to_note(gregorio_note *current_note, char *str); void gregorio_add_misc_element(gregorio_element **current_element, gregorio_type type, gregorio_misc_element_info *info, unsigned short texverb);