diff --git a/.gitignore b/.gitignore index 60eb1315..849d8cd2 100644 --- a/.gitignore +++ b/.gitignore @@ -127,3 +127,4 @@ ctan *.pyg .agignore *.orig +compile_commands.json diff --git a/CHANGELOG.md b/CHANGELOG.md index be3b97fc..bc708ccf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ As of v3.0.0 this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased][develop] +- Added a configurable setting `\gresetunisonbreakbehavior` to control automatic line breaks between unison notes above a syllable. Defaults to `breakable` for backwards compatibility, but may be set to `unbreakable` if that behavior is desired. See [#1504](https://github.com/gregorio-project/gregorio/issues/1504). ## [Unreleased][CTAN] diff --git a/doc/Command_Index_User.tex b/doc/Command_Index_User.tex index c05cf7d9..e1be8838 100644 --- a/doc/Command_Index_User.tex +++ b/doc/Command_Index_User.tex @@ -1178,6 +1178,19 @@ \subsubsection{End of Line Behavior} lines. Defaults to \getgrecount{unbreakablefinalnotes}.\\ \end{argtable} +\macroname{\textbackslash gresetunisonbreakbehavior}{\{\#1\}}{gregoriotex-main.tex} +Macro to determine whether an automatic linebreak may occur between unison notes +over a syllable. This behavior governs how the system works when no explicit +space is put between the notes. In other words, any explicit space between the +notes take precedence over this setting. + +\begin{argtable} + \#1 & \texttt{breakable} & Allow automatic linebreaks between unison notes + (default)\\ + & \texttt{unbreakable} & Disallow automatic linebreaks between unison + notes\\ +\end{argtable} + \subsubsection{Bar spacing} diff --git a/doc/Command_Index_gregorio.tex b/doc/Command_Index_gregorio.tex index 03de3379..4a12f820 100644 --- a/doc/Command_Index_gregorio.tex +++ b/doc/Command_Index_gregorio.tex @@ -402,6 +402,7 @@ \section{Gregorio Controls} & \texttt{4} & Ad-hoc space.\\ \#2 & \texttt{0} & Space is breakable.\\ & \texttt{1} & Space is unbreakable.\\ + & \texttt{2} & Space is breakable according to the \verb=\setunisonbreakbehavior= setting.\\ \#3 & integer & The number of notes in the syllable prior to this macro.\\ \end{argtable} @@ -444,11 +445,12 @@ \section{Gregorio Controls} & \texttt{20} & Space between a punctum inclinatum and a ``no-bar'' glyph two pitches above. \\ & \texttt{21} & Space between a punctum inclinatum and a ``no-bar'' glyph three or four pitches above \\ & \texttt{22} & Half-space. \\ + & \texttt{23} & Space between unison puncta inclinata. \\ \end{argtable} \macroname{\textbackslash GreFinalCustos}{\#1\#2}{gregoriotex-signs.tex} Typesets a custos after the final bar in a score. - +um \begin{argtable} \#1 & integer & Height number of custos.\\ \#2 & \texttt{Flat} & The custos should have a flat.\\ diff --git a/doc/Command_Index_internal.tex b/doc/Command_Index_internal.tex index e0f52cf2..b2d6ef07 100644 --- a/doc/Command_Index_internal.tex +++ b/doc/Command_Index_internal.tex @@ -1961,6 +1961,10 @@ \subsection{Flags} \macroname{\textbackslash ifgre@unbreakableendofelement}{}{gregoriotex-main.tex} Boolean used by \verb=\GreEndOfElement= to store whether the line may be broken at that point. +\macroname{\textbackslash gre@unbreakableendofelement@unison}{}{gregoriotex-main.tex} +Alias that sets \verb=\ifgre@unbreakableendofelement= according to the +\verb=\setunisonbreakbehavior= preference. + \macroname{\textbackslash gre@count@syllablenotes}{}{gregoriotex-syllable.tex} Count containing the number of notes in the syllable. diff --git a/src/gregoriotex/gregoriotex-write.c b/src/gregoriotex/gregoriotex-write.c index ae394a33..f1864a44 100644 --- a/src/gregoriotex/gregoriotex-write.c +++ b/src/gregoriotex/gregoriotex-write.c @@ -3847,6 +3847,75 @@ static bool is_before_linebreak(const gregorio_syllable *syllable, return false; } +static void write_default_end_of_element(FILE *f, + const gregorio_element *const element, + const unsigned int note_unit_count) { + const gregorio_element *next_element; + const gregorio_glyph *last_glyph; + const gregorio_note *last_note, *next_note; + signed char last_pitch = NO_PITCH; + int break_flag = 0; + + /* This is only used in write_syllable, so element is assumed to be: + * 1. not NULL + * 2. of type GRE_ELEMENT + */ + if (element->next) { + next_element = element->next; + if (next_element->type == GRE_ALT) { + next_element = next_element->next; + } + if (next_element && next_element->type == GRE_ELEMENT) { + for (last_glyph = element->u.first_glyph; last_glyph->next; + last_glyph = last_glyph->next) { + /* iterate to find the last glyph */ + } + if (last_glyph->type == GRE_GLYPH) { + last_note = gregorio_glyph_last_note(last_glyph); + assert(last_note != NULL); + if (last_note->type == GRE_NOTE) + switch(last_note->u.note.shape) { + case S_FLAT: + case S_FLAT_PAREN: + case S_SHARP: + case S_SHARP_PAREN: + case S_NATURAL: + case S_NATURAL_PAREN: + break; + default: + last_pitch = last_note->u.note.pitch; + break; + } + } + if (last_pitch != NO_PITCH && next_element->u.first_glyph + && next_element->u.first_glyph->type == GRE_GLYPH) { + next_note = next_element->u.first_glyph->u.notes.first_note; + if (next_note->type == GRE_NOTE) { + switch(next_note->u.note.shape) { + case S_FLAT: + case S_FLAT_PAREN: + case S_SHARP: + case S_SHARP_PAREN: + case S_NATURAL: + case S_NATURAL_PAREN: + break; + default: + if (next_note->u.note.pitch != NO_PITCH && + next_note->u.note.pitch - last_pitch == 0) { + /* at a unison */ + break_flag = 2; + } + break; + } + } + } + + fprintf(f, "\\GreEndOfElement{0}{%d}{%u}%%\n", break_flag, + note_unit_count); + } + } +} + /* * Arguments are relatively obvious. The most obscure is certainly first_of_disc * which is 0 all the time, except in the case of a "clef change syllable". In @@ -4204,19 +4273,12 @@ static void write_syllable(FILE *f, gregorio_syllable *syllable, break; default: - /* here current_element->type is GRE_ELEMENT */ + /* here element->type is GRE_ELEMENT */ assert(element->type == GRE_ELEMENT); handle_last_of_voice(f, syllable, element, *last_of_voice); note_unit_count += write_element(f, syllable, element, status, score); - if (element->next && (element->next->type == GRE_ELEMENT - || (element->next->next - && element->next->type == GRE_ALT - && element->next->next->type == - GRE_ELEMENT))) { - fprintf(f, "\\GreEndOfElement{0}{0}{%u}%%\n", - note_unit_count); - } + write_default_end_of_element(f, element, note_unit_count); break; } } diff --git a/tex/gregoriotex-main.tex b/tex/gregoriotex-main.tex index 7fd0168b..2155f4d0 100644 --- a/tex/gregoriotex-main.tex +++ b/tex/gregoriotex-main.tex @@ -1506,13 +1506,28 @@ }% \newif\ifgre@unbreakableendofelement % +\def\gresetunisonbreakbehavior#1{% + \IfStrEqCase{#1}{% + {breakable}% + {\global\let\gre@unbreakableendofelement@unison\gre@unbreakableendofelementfalse\relax}% + {unbreakable}% + {\global\let\gre@unbreakableendofelement@unison\gre@unbreakableendofelementtrue\relax}% + }[% all other cases + \gre@error{Unrecognized option "#1" for \protect\gresetunisonbreakbehavior\MessageBreak Possible options are: 'breakable' and 'unbreakable'}% + ]% +}% +\gresetunisonbreakbehavior{breakable} + % macro to end elements, #1 is the type of space, it can be : %% 0: default space %% 1: larger space %% 2: glyph space %% 3: zero-width space %% 4: custom space -% #2 is if the space is unbreakable (1) or not (0) +% #2 is whether the space is breakable : +%% 0: breakable +%% 1: unbreakable +%% 2: unison (breakable according to the unisonbreakbehavior setting) % #3 is the number of notes emitted in this syllable before this macro \def\GreEndOfElement#1#2#3{% \ifnum\gre@count@syllablenotes<\gre@count@unbreakabletotalnotes\relax % @@ -1525,10 +1540,14 @@ \gre@count@unbreakablefinalnotes\relax % \gre@unbreakableendofelementtrue % \else % - \ifnum#2=1\relax % + \ifcase#2\relax % + \gre@unbreakableendofelementfalse % + \or% case 1 \gre@unbreakableendofelementtrue % + \or% case 2 + \gre@unbreakableendofelement@unison % \else % - \gre@unbreakableendofelementfalse % + \gre@error{Unrecognized breakable argument #2}% \fi % \fi % \fi % @@ -1538,7 +1557,7 @@ \else % \gre@penalty{\the\gre@space@count@endofelementpenalty}% \fi % - \ifcase#1% + \ifcase#1\relax% \gre@skip@temp@four = \gre@space@skip@interelementspace\relax% \gre@hskip\gre@skip@temp@four % \or% case 1 @@ -1552,6 +1571,8 @@ \gre@hskip\gre@skip@temp@four % \or% case 4 \gre@hskip\gre@skip@temp@four % + \else% + \gre@error{Unrecognized space type #1}% \fi% \ifgre@unbreakableendofelement % \GreNoBreak %