diff --git a/.gitignore b/.gitignore index 6d4ab020f..11b9f20dd 100644 --- a/.gitignore +++ b/.gitignore @@ -125,3 +125,4 @@ gregorio-* ctan *.pyg uninstall-gtex.sh +.agignore diff --git a/CHANGELOG.md b/CHANGELOG.md index 95c1662e8..330466398 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,8 @@ As of v3.0.0 this project adheres to [Semantic Versioning](http://semver.org/). ## Changed - Notes are now left-aligned as if all clefs had the same width as the largest clef in the score. You can get previous behavior back with `\grebolshiftcleftype{current}`, or temporary force alignment until the end of a score with `\grelocalbolshiftcleftype`. See Documentation of these functions and [#1189](https://github.com/gregorio-project/gregorio/issues/1189). -- A clef change immediately before a line break `(z)` will now typeset the new clef at the beginning of the next line. An explicit custos `(z0)` immediately before such a clef change (or separated by only a bar) will be suppressed. See [1190](https://github.com/gregorio-project/gregorio/issues/1190). +- A clef change immediately before a line break `(z)` will now typeset the new clef at the beginning of the next line. An explicit custos `(z0)` immediately before such a clef change (or separated by only a bar) will be suppressed. See [#1190](https://github.com/gregorio-project/gregorio/issues/1190). +- Ledger lines are now extended through notes on either side of a ledger line that crosses a stem, as long as the notes are within the same "element." If the algorithm doesn't produce the result you want, you can use `[oll:0]` to suppress an over-the-staff ledger line on a note, `[ull:0]` to suppress an under-the-staff ledger line on a note, `[oll:1]` to force an over-the-line ledger line on a note, or [ull:1] to force an under-the-staff ledger line on a note. Please note that other forms of `[oll:...]` and `[ull:...]` can interfere with these new settings. See [UPGRADE.md](UPGRADE.md) and [#1215](https://github.com/gregorio-project/gregorio/issues/1215) for details. ### Added - More cavum shapes are now available. To use them, simply add `r` in gabc to any note in a glyph. See [#844](https://github.com/gregorio-project/gregorio/issues/844). diff --git a/UPGRADE.md b/UPGRADE.md index c0fbc8f7d..8441e0fb3 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -2,7 +2,7 @@ This file contains instructions to upgrade to a new release of Gregorio. -## Next version +## 5.0 ### Gregorio and Grana Padano fonts @@ -10,6 +10,21 @@ The gregorio and granapadano fonts, as well as their "-op" Dominican variants, w *** NOTE: put the logistics of these downloads here once they've been determined *** +### Ledger lines + +As of version 5.0, ledger lines are extended through notes on either side of a ledger line that crosses a stem, as long as the notes are within the same element. + +The algorithm for this is simple so it can be predictable, and it cannot take into account spacing adjustments made it TeX. This means it may not produce the exactly desired results. In order to get the results you want, you can override the automatic behavior in gabc: + +- `[oll:1]` will force an over-the-staff ledger line on a note. +- `[oll:0]` will suppress an over-the-staff ledger line on a note. +- `[ull:1]` will force an under-the-staff ledger line on a note. +- `[ull:0]` will suppress an under-the-staff ledger line on a note. + +The other `oll` and `ull` forms take precendence over and will interfere with the above settings, so if you are using them, you may need to adjust them to get the output you want. + +Note: You may need to use a construct such as `/!` to keep notes that are separated in the same element. For example, `abcV` is two elements (`ab` and `cV`), so the ledger line on `b` is not extended to `cV`. In contrast, `ab/!cV` is one element, so the ledger line on `b` is extended to `cV`. + ## 4.2 ### Executable file name diff --git a/src/Makefile.am b/src/Makefile.am index f0c275215..3f727a642 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -35,9 +35,13 @@ gregorio___FILENAME_VERSION__SOURCES = \ @MK@else @MK@ ifneq ($(wildcard ../.hg/git),) @MK@ _gitnode_ = $(shell hg log --template='{gitnode}' -r .) -@MK@ _tag_ = $(shell git --git-dir=../.hg/git describe --exact-match $(_gitnode_) 2>/dev/null) -@MK@ ifeq ($(_tag_),) -@MK@ DEFS += -DBRANCH_VERSION='"$(shell cat ../.hg/bookmarks.current)-$(shell git --git-dir=../.hg/git rev-parse --short '$(_gitnode_)')-$(shell git --git-dir=../.hg/git rev-list '$(_gitnode_)' --count)"' +@MK@ ifeq ($(_gitnode_),) +@MK@ DEFS += -DBRANCH_VERSION='"$(shell hg log --template='{activebookmark}-hg:{manifest}' -r .)"' +@MK@ else +@MK@ _tag_ = $(shell git --git-dir=../.hg/git describe --exact-match '$(_gitnode_)' 2>/dev/null) +@MK@ ifeq ($(_tag_),) +@MK@ DEFS += -DBRANCH_VERSION='"$(shell hg log --template='{activebookmark}' -r .)-$(shell git --git-dir=../.hg/git rev-parse --short '$(_gitnode_)')-$(shell git --git-dir=../.hg/git rev-list '$(_gitnode_)' --count)"' +@MK@ endif @MK@ endif @MK@ endif @MK@endif diff --git a/src/dump/dump.c b/src/dump/dump.c index 32cdb1c02..a62f5d609 100644 --- a/src/dump/dump.c +++ b/src/dump/dump.c @@ -477,13 +477,37 @@ void dump_write_score(FILE *f, gregorio_score *score) dump_hepisema_adjustment(f, note, SO_OVER, "above"); dump_hepisema_adjustment(f, note, SO_UNDER, "below"); - if (note->explicit_high_ledger_line) { + switch (note->high_ledger_specificity) { + case LEDGER_SUPPOSED: + break; + case LEDGER_EXPLICIT: fprintf(f, " explicit high line %s\n", - dump_bool(note->supposed_high_ledger_line)); + dump_bool(note->high_ledger_line)); + break; + case LEDGER_DRAWN: + fprintf(f, " drawn high line %s\n", + dump_bool(note->high_ledger_line)); + break; + case LEDGER_EXPLICITLY_DRAWN: + fprintf(f, " forced drawn high line %s\n", + dump_bool(note->high_ledger_line)); + break; } - if (note->explicit_low_ledger_line) { + switch (note->low_ledger_specificity) { + case LEDGER_SUPPOSED: + break; + case LEDGER_EXPLICIT: fprintf(f, " explicit low line %s\n", - dump_bool(note->supposed_low_ledger_line)); + dump_bool(note->low_ledger_line)); + break; + case LEDGER_DRAWN: + fprintf(f, " drawn low line %s\n", + dump_bool(note->low_ledger_line)); + break; + case LEDGER_EXPLICITLY_DRAWN: + fprintf(f, " forced drawn low line %s\n", + dump_bool(note->low_ledger_line)); + break; } } } diff --git a/src/gabc/gabc-notes-determination.l b/src/gabc/gabc-notes-determination.l index 5a6e8ba87..5c552d405 100644 --- a/src/gabc/gabc-notes-determination.l +++ b/src/gabc/gabc-notes-determination.l @@ -49,11 +49,18 @@ static char *before_ledger_length = NULL; static unsigned short ledger_var[2] = { 0, 0 }; static unsigned char staff_lines; static signed char highest_pitch; +static signed char high_ledger_line_pitch; static bool legacy_oriscus_orientation; static unsigned short he_adjustment_index[2] = { 0, 0 }; static signed char bracket_low_pitch, bracket_high_pitch; static unsigned short left_bracket_texverb = 0; +#define LEDGER(WHICH, SPECIFICITY, VALUE) \ + if (LEDGER_##SPECIFICITY > current_note->WHICH##_ledger_specificity) { \ + current_note->WHICH##_ledger_line = VALUE; \ + current_note->WHICH##_ledger_specificity = LEDGER_##SPECIFICITY; \ + } + typedef struct slur_info { unsigned short var; char shift; @@ -130,21 +137,25 @@ static gregorio_shape punctum_inclinatum(const char orientation) static __inline void lex_add_note(int i, gregorio_shape shape, char signs, char liquescentia) { + signed char height = pitch_letter_to_height(tolower( + (unsigned char)gabc_notes_determination_text[i])); + nbof_isolated_episema = 0; - gregorio_add_note(¤t_note, pitch_letter_to_height( - tolower((unsigned char)gabc_notes_determination_text[i])), - shape, signs, liquescentia, NULL, ¬es_lloc); + gregorio_add_note(¤t_note, height, shape, signs, liquescentia, NULL, + ¬es_lloc); current_note->he_adjustment_index[SO_OVER] = he_adjustment_index[SO_OVER]; current_note->he_adjustment_index[SO_UNDER] = he_adjustment_index[SO_UNDER]; - if (ledger_var[SO_OVER]) { - current_note->supposed_high_ledger_line = true; - current_note->explicit_high_ledger_line = true; + if (height >= high_ledger_line_pitch) { + LEDGER(high, DRAWN, true); + } else if (ledger_var[SO_OVER]) { + LEDGER(high, EXPLICIT, true); } - if (ledger_var[SO_UNDER]) { - current_note->supposed_low_ledger_line = true; - current_note->explicit_low_ledger_line = true; + if (height <= LOW_LEDGER_LINE_PITCH) { + LEDGER(low, DRAWN, true); + } else if (ledger_var[SO_UNDER]) { + LEDGER(low, EXPLICIT, true); } } @@ -1035,6 +1046,12 @@ void gabc_det_notes_finish(void) gregorio_add_space_as_note(¤t_note, SP_AD_HOC_SPACE_NB, gregorio_strdup(gabc_notes_determination_text), ¬es_lloc); } +\[oll:1\] { + LEDGER(high, EXPLICITLY_DRAWN, true); + } +\[oll:0\] { + LEDGER(high, EXPLICITLY_DRAWN, false); + } \[oll:\}\] { end_variable_ledger(SO_OVER); } @@ -1053,6 +1070,12 @@ void gabc_det_notes_finish(void) add_variable_ledger(SO_OVER, gabc_notes_determination_text); BEGIN(endledger); } +\[ull:1\] { + LEDGER(low, EXPLICITLY_DRAWN, true); + } +\[ull:0\] { + LEDGER(low, EXPLICITLY_DRAWN, false); + } \[ull:\}\] { end_variable_ledger(SO_UNDER); } @@ -1383,20 +1406,16 @@ s { legacy_oriscus_orientation); } \[hl:1\] { - current_note->supposed_high_ledger_line = true; - current_note->explicit_high_ledger_line = true; + LEDGER(high, EXPLICIT, true); } \[hl:0\] { - current_note->supposed_high_ledger_line = false; - current_note->explicit_high_ledger_line = true; + LEDGER(high, EXPLICIT, false); } \[ll:1\] { - current_note->supposed_low_ledger_line = true; - current_note->explicit_low_ledger_line = true; + LEDGER(low, EXPLICIT, true); } \[ll:0\] { - current_note->supposed_low_ledger_line = false; - current_note->explicit_low_ledger_line = true; + LEDGER(low, EXPLICIT, false); } .|\n { gregorio_messagef("det_notes_from_string", VERBOSITY_ERROR, 0, @@ -1423,6 +1442,7 @@ gregorio_note *gabc_det_notes_from_string(char *str, char *newmacros[10], staff_lines = score->staff_lines; highest_pitch = score->highest_pitch; + high_ledger_line_pitch = score->high_ledger_line_pitch; legacy_oriscus_orientation = score->legacy_oriscus_orientation; /* a small optimization could uccur here: we could do it only once at the diff --git a/src/gabc/gabc-score-determination.c b/src/gabc/gabc-score-determination.c index 526a9b43d..6a3a1c3af 100644 --- a/src/gabc/gabc-score-determination.c +++ b/src/gabc/gabc-score-determination.c @@ -24,6 +24,7 @@ #include "config.h" #include #include +#include #include #include "bool.h" #include "struct.h" @@ -31,6 +32,7 @@ #include "gabc.h" #include "gabc-score-determination.h" #include "messages.h" +#include "support.h" void gabc_suppress_extra_custos_at_linebreak(gregorio_score *score) { @@ -268,7 +270,8 @@ void gabc_determine_oriscus_orientation(const gregorio_score *const score) { gregorio_note *oriscus = NULL; - gregorio_for_each_note(score, oriscus_orientation_visit, &oriscus); + gregorio_for_each_note(score, oriscus_orientation_visit, NULL, + GRESTRUCT_NONE, &oriscus); if (oriscus) { /* oriscus at the end of the score */ @@ -363,8 +366,8 @@ static void punctum_inclinatum_orientation_visit( ? S_PUNCTUM_INCLINATUM_ASCENDENS : S_PUNCTUM_INCLINATUM_DESCENDENS; } - gregorio_from_note_to_note(&v->first, &v->previous, set_shape, - &v->orientation); + gregorio_from_note_to_note(&v->first, &v->previous, set_shape, NULL, + GRESTRUCT_NONE, &v->orientation); v->first.syllable = NULL; v->first.element = NULL; v->first.glyph = NULL; @@ -400,13 +403,188 @@ void gabc_determine_punctum_inclinatum_orientation( /* .running = */ 0, }; - gregorio_for_each_note(score, punctum_inclinatum_orientation_visit, &v); + gregorio_for_each_note(score, punctum_inclinatum_orientation_visit, NULL, + GRESTRUCT_NONE, &v); if (v.first.note) { v.orientation = (v.running > 0) ? S_PUNCTUM_INCLINATUM_ASCENDENS : S_PUNCTUM_INCLINATUM_DESCENDENS; - gregorio_from_note_to_note(&v.first, &v.previous, set_shape, - &v.orientation); + gregorio_from_note_to_note(&v.first, &v.previous, set_shape, NULL, + GRESTRUCT_NONE, &v.orientation); } } + +typedef struct note_stack { + gregorio_note *note; + struct note_stack *prev; +} note_stack; + +static void note_stack_push(note_stack **const stack, gregorio_note *note) { + note_stack *item = gregorio_malloc(sizeof(note_stack)); + item->note = note; + item->prev = *stack; + *stack = item; +} + +static gregorio_note *note_stack_pop(note_stack **const stack) { + note_stack *item = *stack; + if (item) { + gregorio_note *note = item->note; + note_stack *prev = item->prev; + free(item); + *stack = prev; + return note; + } + return NULL; +} + +static void note_stack_clear(note_stack **const stack) { + note_stack *item = *stack; + while (item) { + note_stack *prev = item->prev; + free(item); + item = prev; + } + *stack = NULL; +} + +typedef struct { + note_stack *high, *low; + gregorio_note *prev_note; + signed char high_ledger_line_pitch; + bool running_high, running_low; +} ledger_line_vars; + +static __inline void clear_ledger_line_vars(ledger_line_vars *const v) { + note_stack_clear(&v->high); + note_stack_clear(&v->low); + v->prev_note = NULL; + v->running_high = false; + v->running_low = false; +} + +static __inline void adjust_ledger(const gregorio_note_iter_position *const p, + const gregorio_ledger_specificity specificity, bool ledger_line, + note_stack **const stack, bool *const running, gregorio_note *prev_note, + const signed char high_ledger_line_pitch, + bool (*extend_ledger)(gregorio_note *, const gregorio_note *, + const gregorio_note *, signed char)) +{ + if (specificity & LEDGER_DRAWN) { + if (ledger_line) { + gregorio_note *after = p->note; + gregorio_note *note; + /* process from this ledger backwards */ + while ((note = note_stack_pop(stack))) { + if (!extend_ledger(note, NULL, after, high_ledger_line_pitch)) { + /* ledger has ended */ + break; + } + after = note; + } + *running = true; + } else { + *running = false; + } + note_stack_clear(stack); + } else { + if (*running) { + if (!extend_ledger(p->note, prev_note, NULL, + high_ledger_line_pitch)) { + /* ledger has ended */ + note_stack_push(stack, p->note); + *running = false; + } + /* else stack should be empty, keep it that way */ + } else { + note_stack_push(stack, p->note); + } + } +} + +static bool extend_high_ledger(gregorio_note *const note, + const gregorio_note *const note_before, + const gregorio_note *const note_after, + const signed char high_ledger_line_pitch) +{ + bool extend = false; + + if (note_before) { + extend = note_before->u.note.pitch < note->u.note.pitch + || (note_before->u.note.pitch > high_ledger_line_pitch + && note->u.note.pitch < high_ledger_line_pitch); + } else if (note_after) { + extend = note_after->u.note.pitch < note->u.note.pitch + || (note_after->u.note.pitch > high_ledger_line_pitch + && note->u.note.pitch < high_ledger_line_pitch); + } + + if (extend) { + note->high_ledger_line = true; + note->high_ledger_specificity = LEDGER_DRAWN; + } + return extend; +} + +static bool extend_low_ledger(gregorio_note *const note, + const gregorio_note *const note_before, + const gregorio_note *const note_after, + const signed char high_ledger_line_pitch __attribute__((__unused__))) +{ + bool extend = false; + + if (note_before) { + extend = note_before->u.note.pitch < note->u.note.pitch + || (note_before->u.note.pitch > LOW_LEDGER_LINE_PITCH + && note->u.note.pitch < LOW_LEDGER_LINE_PITCH); + } else if (note_after) { + extend = note_after->u.note.pitch < note->u.note.pitch + || (note_after->u.note.pitch > LOW_LEDGER_LINE_PITCH + && note->u.note.pitch < LOW_LEDGER_LINE_PITCH); + } + + if (extend) { + note->low_ledger_line = true; + note->low_ledger_specificity = LEDGER_DRAWN; + } + return extend; +} + +/* data must be (ledger_line_vars *) */ +static void ledger_line_visit(const gregorio_note_iter_position *const p, + void *const data) +{ + ledger_line_vars *const v = (ledger_line_vars *)data; + + adjust_ledger(p, p->note->high_ledger_specificity, p->note->high_ledger_line, + &v->high, &v->running_high, v->prev_note, v->high_ledger_line_pitch, + &extend_high_ledger); + adjust_ledger(p, p->note->low_ledger_specificity, p->note->low_ledger_line, + &v->low, &v->running_low, v->prev_note, v->high_ledger_line_pitch, + &extend_low_ledger); + + v->prev_note = p->note; +} + +/* data must be (ledger_line_vars *) */ +static void ledger_line_end_item( + const gregorio_note_iter_position *const p __attribute__((__unused__)), + const gregorio_note_iter_item_type item_type, void *const data) +{ + if (item_type == GRESTRUCT_ELEMENT) { + clear_ledger_line_vars((ledger_line_vars *)data); + } +} + +void gabc_determine_ledger_lines(const gregorio_score *const score) +{ + ledger_line_vars v; + memset(&v, 0, sizeof v); + v.high_ledger_line_pitch = score->high_ledger_line_pitch; + + gregorio_for_each_note(score, ledger_line_visit, ledger_line_end_item, + GRESTRUCT_ELEMENT, &v); + + /* stacks should be cleared by ledger_line_end_item */ +} diff --git a/src/gabc/gabc-score-determination.h b/src/gabc/gabc-score-determination.h index 769f3921f..2b646b086 100644 --- a/src/gabc/gabc-score-determination.h +++ b/src/gabc/gabc-score-determination.h @@ -59,5 +59,6 @@ bool gabc_check_score_integrity(gregorio_score *score_to_check); bool gabc_check_infos_integrity(gregorio_score *score_to_check); void gabc_determine_oriscus_orientation(const gregorio_score *score); void gabc_determine_punctum_inclinatum_orientation(const gregorio_score *score); +void gabc_determine_ledger_lines(const gregorio_score *const score); #endif diff --git a/src/gabc/gabc-score-determination.y b/src/gabc/gabc-score-determination.y index 5953e5c97..d27e108bc 100644 --- a/src/gabc/gabc-score-determination.y +++ b/src/gabc/gabc-score-determination.y @@ -508,6 +508,7 @@ gregorio_score *gabc_read_score(FILE *f_in, bool point_and_click) gabc_determine_oriscus_orientation(score); } gabc_determine_punctum_inclinatum_orientation(score); + gabc_determine_ledger_lines(score); gregorio_fix_initial_keys(score, gregorio_default_clef); rebuild_score_characters(); gabc_suppress_extra_custos_at_linebreak(score); diff --git a/src/gabc/gabc-write.c b/src/gabc/gabc-write.c index d7fcd4f92..266eb0f81 100644 --- a/src/gabc/gabc-write.c +++ b/src/gabc/gabc-write.c @@ -442,11 +442,25 @@ static const char *mora_vposition(gregorio_note *note) } static void write_note_heuristics(FILE *f, gregorio_note *note) { - if (note->explicit_high_ledger_line) { - fprintf(f, "[hl:%c]", note->supposed_high_ledger_line? '1' : '0'); + switch (note->high_ledger_specificity) { + case LEDGER_EXPLICIT: + fprintf(f, "[hl:%c]", note->high_ledger_line? '1' : '0'); + break; + case LEDGER_EXPLICITLY_DRAWN: + fprintf(f, "[oll:%c]", note->high_ledger_line? '1' : '0'); + break; + default: + break; } - if (note->explicit_low_ledger_line) { - fprintf(f, "[ll:%c]", note->supposed_low_ledger_line? '1' : '0'); + switch (note->low_ledger_specificity) { + case LEDGER_EXPLICIT: + fprintf(f, "[ll:%c]", note->low_ledger_line? '1' : '0'); + break; + case LEDGER_EXPLICITLY_DRAWN: + fprintf(f, "[ull:%c]", note->low_ledger_line? '1' : '0'); + break; + default: + break; } } diff --git a/src/gregoriotex/gregoriotex-position.c b/src/gregoriotex/gregoriotex-position.c index 61168ee7f..4cb715852 100644 --- a/src/gregoriotex/gregoriotex-position.c +++ b/src/gregoriotex/gregoriotex-position.c @@ -1160,17 +1160,17 @@ static __inline void position_h_episema(gregorio_note *const note, adj->pitch_extremum = h->height; } } - if (!note->explicit_high_ledger_line && !note->supposed_high_ledger_line) { - note->supposed_high_ledger_line = high_ledger_line; + if (!note->high_ledger_specificity && !note->high_ledger_line) { + note->high_ledger_line = high_ledger_line; } - if (!note->explicit_low_ledger_line && !note->supposed_low_ledger_line) { - note->supposed_low_ledger_line = low_ledger_line; + if (!note->low_ledger_specificity && !note->low_ledger_line) { + note->low_ledger_line = low_ledger_line; } } static __inline void next_has_ledger_line( const height_computation *const h, bool *high_ledger_line, - bool *low_ledger_line, const gregorio_score *const score) + bool *low_ledger_line) { const gregorio_element *element = h->last_connected_element; const gregorio_glyph *glyph = h->last_connected_glyph; @@ -1197,10 +1197,8 @@ static __inline void next_has_ledger_line( note = glyph->u.notes.first_note; } - *high_ledger_line = *high_ledger_line - || has_high_ledger_line(note->u.note.pitch, false, score); - *low_ledger_line = *low_ledger_line - || has_low_ledger_line(note->u.note.pitch, false); + *high_ledger_line = *high_ledger_line || note->high_ledger_line; + *low_ledger_line = *low_ledger_line || note->low_ledger_line; if (keep_going) { keep_going = false; @@ -1216,7 +1214,7 @@ static __inline void next_has_ledger_line( static __inline void previous_has_ledger_line( const height_computation *const h, bool *high_ledger_line, - bool *low_ledger_line, const gregorio_score *const score) + bool *low_ledger_line) { const gregorio_element *element = h->start_element; const gregorio_glyph *glyph = h->start_glyph; @@ -1245,10 +1243,8 @@ static __inline void previous_has_ledger_line( } while (glyph->type != GRE_GLYPH); note = gregorio_glyph_last_note(glyph); } - *high_ledger_line = *high_ledger_line - || has_high_ledger_line(note->u.note.pitch, false, score); - *low_ledger_line = *low_ledger_line - || has_low_ledger_line(note->u.note.pitch, false); + *high_ledger_line = *high_ledger_line || note->high_ledger_line; + *low_ledger_line = *low_ledger_line || note->low_ledger_line; if (keep_going) { keep_going = false; @@ -1276,8 +1272,8 @@ static __inline void set_h_episema_height(const height_computation *const h, bool low_ledger_line = has_low_ledger_line(h->height, true) || has_low_ledger_line(h->height - h->vpos, false); - next_has_ledger_line(h, &high_ledger_line, &low_ledger_line, score); - previous_has_ledger_line(h, &high_ledger_line, &low_ledger_line, score); + next_has_ledger_line(h, &high_ledger_line, &low_ledger_line); + previous_has_ledger_line(h, &high_ledger_line, &low_ledger_line); for ( ; element; element = element->next) { if (element->type == GRE_ELEMENT) { @@ -1633,70 +1629,6 @@ static __inline int compute_fused_shift(const gregorio_glyph *glyph) return shift; } -static __inline void guess_ledger_lines(const gregorio_element *element, - const gregorio_score *const score) -{ - bool high_ledger_line = false; - bool low_ledger_line = false; - gregorio_note *prev = NULL; - - for (; element; element = element->next) { - if (element->type == GRE_ELEMENT) { - gregorio_glyph *glyph; - for (glyph = element->u.first_glyph; glyph; - glyph = glyph->next) { - if (glyph->type == GRE_GLYPH) { - gregorio_note *note; - for (note = glyph->u.notes.first_note; note; - note = note->next) { - if (note->type == GRE_NOTE) { - if (high_ledger_line - && !note->explicit_high_ledger_line - && !note->supposed_high_ledger_line) { - note->supposed_high_ledger_line = true; - } - if (low_ledger_line - && !note->explicit_low_ledger_line - && !note->supposed_low_ledger_line) { - note->supposed_low_ledger_line = true; - } - high_ledger_line = has_high_ledger_line( - note->u.note.pitch, false, score); - low_ledger_line = has_low_ledger_line( - note->u.note.pitch, false); - if (high_ledger_line) { - if (!note->explicit_high_ledger_line - && !note->supposed_high_ledger_line) { - note->supposed_high_ledger_line = true; - } - if (prev && !prev->explicit_high_ledger_line - && !prev->supposed_high_ledger_line) { - prev->supposed_high_ledger_line = true; - } - } - if (low_ledger_line) { - if (!note->explicit_low_ledger_line - && !note->supposed_low_ledger_line) { - note->supposed_low_ledger_line = true; - } - if (prev && !prev->explicit_low_ledger_line - && !prev->supposed_low_ledger_line) { - prev->supposed_low_ledger_line = true; - } - } - prev = note; - } - } - } - } - /* this heuristic ends eith the element */ - high_ledger_line = false; - low_ledger_line = false; - prev = NULL; - } - } -} - void gregoriotex_compute_positioning( const gregorio_element *const param_element, const gregorio_score *const score) @@ -1748,8 +1680,6 @@ void gregoriotex_compute_positioning( gtex_type type; const gregorio_element *element; - guess_ledger_lines(param_element, score); - for (element = param_element; element; element = element->next) { if (element->type == GRE_ELEMENT) { gregorio_glyph *glyph; diff --git a/src/gregoriotex/gregoriotex-write.c b/src/gregoriotex/gregoriotex-write.c index 395fa51b7..c33f396e2 100644 --- a/src/gregoriotex/gregoriotex-write.c +++ b/src/gregoriotex/gregoriotex-write.c @@ -207,10 +207,10 @@ static queuetype adjusted_queuetype_of(const gregorio_note *const note, case 1: return Q_ON_BOTTOM_LINE; case 2: - return queue_note->supposed_low_ledger_line? + return queue_note->low_ledger_line? Q_ON_SPACE_ABOVE_BOTTOM_LINE : Q_ON_SPACE_BELOW_BOTTOM_LINE; case 3: - return queue_note->supposed_low_ledger_line? + return queue_note->low_ledger_line? Q_ON_LINE_ABOVE_BOTTOM_LINE : Q_ON_BOTTOM_LINE; case 5: case 7: @@ -1758,12 +1758,12 @@ static void write_bar(FILE *f, const gregorio_score *const score, static __inline char *suppose_high_ledger_line(const gregorio_note *const note) { - return note->supposed_high_ledger_line? "\\GreSupposeHighLedgerLine" : ""; + return note->high_ledger_line? "\\GreSupposeHighLedgerLine" : ""; } static __inline char *suppose_low_ledger_line(const gregorio_note *const note) { - return note->supposed_low_ledger_line? "\\GreSupposeLowLedgerLine" : ""; + return note->low_ledger_line? "\\GreSupposeLowLedgerLine" : ""; } /* @@ -2765,6 +2765,12 @@ static __inline void fixup_height_extrema(signed char *const top_height, } } +static __inline bool is_ledger_drawn(const bool setting, + const gregorio_ledger_specificity specificity) +{ + return ((specificity & LEDGER_DRAWN) && setting); +} + static void write_signs(FILE *f, gtex_type type, const gregorio_glyph *glyph, const gregorio_note *note, int fuse_to_next_note, gregoriotex_status *const status, @@ -2783,11 +2789,13 @@ static void write_signs(FILE *f, gtex_type type, for (current_note = note, i = 1; current_note; current_note = current_note->next, ++i) { /* we start by the additional lines */ - if (current_note->u.note.pitch <= LOW_LEDGER_LINE_PITCH) { + if (is_ledger_drawn(current_note->low_ledger_line, + current_note->low_ledger_specificity)) { write_additional_line(f, i, type, true, current_note, score); status->bottom_line = 1; } - if (current_note->u.note.pitch >= score->high_ledger_line_pitch) { + if (is_ledger_drawn(current_note->high_ledger_line, + current_note->high_ledger_specificity)) { write_additional_line(f, i, type, false, current_note, score); } if (current_note->texverb) { diff --git a/src/struct.c b/src/struct.c index 1397ec5d4..6e959d9df 100644 --- a/src/struct.c +++ b/src/struct.c @@ -206,6 +206,10 @@ void gregorio_add_note(gregorio_note **current_note, signed char pitch, prototype->he_adjustment_index[SO_OVER]; element->he_adjustment_index[SO_UNDER] = prototype->he_adjustment_index[SO_UNDER]; + element->high_ledger_line = prototype->high_ledger_line; + element->high_ledger_specificity = prototype->high_ledger_specificity; + element->low_ledger_line = prototype->low_ledger_line; + element->low_ledger_specificity = prototype->low_ledger_specificity; } element->texverb = 0; element->choral_sign = NULL; diff --git a/src/struct.h b/src/struct.h index d873abf73..bcb593b76 100644 --- a/src/struct.h +++ b/src/struct.h @@ -374,6 +374,15 @@ ENUM(gregorio_sign_orientation, GREGORIO_SIGN_ORIENTATION); X(HVB_U_HIGH, 5) ENUM(gregorio_hepisema_vbasepos, GREGORIO_HEPISEMA_VBASEPOS); +/* these may be used as bit fields, with LEDGER_EXPLICIT and LEDGER_DRAWN being + * the bits */ +#define GREGORIO_LEDGER_SPECIFICITY(A,E,X,L) \ + A(LEDGER_SUPPOSED, 0) \ + A(LEDGER_EXPLICIT, 1) \ + A(LEDGER_DRAWN, 2) \ + X(LEDGER_EXPLICITLY_DRAWN, 3) +ENUM(gregorio_ledger_specificity, GREGORIO_LEDGER_SPECIFICITY); + typedef struct gregorio_extra_info { char *ad_hoc_space_factor; ENUM_BITFIELD(gregorio_bar) bar:4; @@ -481,12 +490,11 @@ typedef struct gregorio_note { ENUM_BITFIELD(grehepisema_size) h_episema_below_size:2; bool h_episema_above_connect:1; bool h_episema_below_connect:1; - bool supposed_high_ledger_line:1; - bool supposed_low_ledger_line:1; - /* the "explicit" flags indicate that the "supposed" flags contain values - * that were explicitly specified in the gabc file */ - bool explicit_high_ledger_line:1; - bool explicit_low_ledger_line:1; + /* the "specificity" field indicates how to interpret the line flag */ + bool high_ledger_line:1; + ENUM_BITFIELD(gregorio_ledger_specificity) high_ledger_specificity:2; + bool low_ledger_line:1; + ENUM_BITFIELD(gregorio_ledger_specificity) low_ledger_specificity:2; bool is_lower_note:1; bool is_upper_note:1; ENUM_BITFIELD(gregorio_vposition) mora_vposition:2; diff --git a/src/struct_iter.h b/src/struct_iter.h index 33ed7ec92..505cb7120 100644 --- a/src/struct_iter.h +++ b/src/struct_iter.h @@ -32,6 +32,14 @@ #include "struct.h" +typedef enum { + GRESTRUCT_NONE = 0, + GRESTRUCT_NOTE = 1 << 0, + GRESTRUCT_GLYPH = 1 << 1, + GRESTRUCT_ELEMENT = 1 << 2, + GRESTRUCT_SYLLABLE = 1 << 3 +} gregorio_note_iter_item_type; + typedef struct { gregorio_syllable *syllable; gregorio_element *element; @@ -43,7 +51,9 @@ static __inline void gregorio_from_note_to_note( const gregorio_note_iter_position *const start, const gregorio_note_iter_position *const end, void (*const visit)(const gregorio_note_iter_position *, void *), - void *data) + void (*const end_item)(const gregorio_note_iter_position *, + gregorio_note_iter_item_type, void *), + const gregorio_note_iter_item_type desired_iter_items, void *data) { gregorio_note_iter_position p = *start; @@ -67,24 +77,55 @@ static __inline void gregorio_from_note_to_note( } if (end && p.note == end->note) { + if (end_item) { + if (desired_iter_items & GRESTRUCT_NOTE) { + end_item(&p, GRESTRUCT_NOTE, data); + } + if (desired_iter_items & GRESTRUCT_GLYPH) { + end_item(&p, GRESTRUCT_GLYPH, data); + } + if (desired_iter_items + & GRESTRUCT_ELEMENT) { + end_item(&p, GRESTRUCT_ELEMENT, data); + } + if (desired_iter_items + & GRESTRUCT_SYLLABLE) { + end_item(&p, GRESTRUCT_SYLLABLE, data); + } + } return; } + if (end_item + && (desired_iter_items & GRESTRUCT_NOTE)) { + end_item(&p, GRESTRUCT_NOTE, data); + } p.note = p.note->next; } /* note */ } + if (end_item && (desired_iter_items & GRESTRUCT_GLYPH)) { + end_item(&p, GRESTRUCT_GLYPH, data); + } p.glyph = p.glyph->next; } /* glyph */ } + if (end_item && (desired_iter_items & GRESTRUCT_ELEMENT)) { + end_item(&p, GRESTRUCT_ELEMENT, data); + } p.element = p.element->next; } /* element */ + if (end_item && (desired_iter_items & GRESTRUCT_SYLLABLE)) { + end_item(&p, GRESTRUCT_SYLLABLE, data); + } p.syllable = p.syllable->next_syllable; } /* syllable */ } static __inline void gregorio_for_each_note(const gregorio_score *score, void (*const visit)(const gregorio_note_iter_position *, void *), - void *data) + void (*const end_item)(const gregorio_note_iter_position *, + gregorio_note_iter_item_type, void *), + const gregorio_note_iter_item_type desired_iter_items, void *data) { gregorio_note_iter_position p = { /* .syllable = */ NULL, @@ -95,7 +136,8 @@ static __inline void gregorio_for_each_note(const gregorio_score *score, p.syllable = score->first_syllable; - gregorio_from_note_to_note(&p, NULL, visit, data); + gregorio_from_note_to_note(&p, NULL, visit, end_item, desired_iter_items, + data); } #endif