diff options
Diffstat (limited to 'Build/source/texk/gregorio/gregorio-src/src/gregoriotex/gregoriotex-position.c')
-rw-r--r-- | Build/source/texk/gregorio/gregorio-src/src/gregoriotex/gregoriotex-position.c | 585 |
1 files changed, 501 insertions, 84 deletions
diff --git a/Build/source/texk/gregorio/gregorio-src/src/gregoriotex/gregoriotex-position.c b/Build/source/texk/gregorio/gregorio-src/src/gregoriotex/gregoriotex-position.c index 987cddd7372..29500d7e840 100644 --- a/Build/source/texk/gregorio/gregorio-src/src/gregoriotex/gregoriotex-position.c +++ b/Build/source/texk/gregorio/gregorio-src/src/gregoriotex/gregoriotex-position.c @@ -85,10 +85,17 @@ OFFSET_CASE(FinalConnectedVirga); OFFSET_CASE(InitialVirga); OFFSET_CASE(SalicusOriscusWide); OFFSET_CASE(SalicusOriscusOne); +OFFSET_CASE(LeadingPunctum); +OFFSET_CASE(LeadingQuilisma); +OFFSET_CASE(LeadingOriscus); +OFFSET_CASE(Flat); +OFFSET_CASE(Sharp); +OFFSET_CASE(Natural); static __inline const char *note_before_last_note_case_ignoring_deminutus( const gregorio_note *const current_note) { + assert(current_note->next); if ((current_note->u.note.pitch - current_note->next->u.note.pitch) == 1 || (current_note->u.note.pitch - current_note->next->u.note.pitch) == -1) { @@ -112,8 +119,7 @@ static __inline const char *note_before_last_note_case( const gregorio_glyph *const current_glyph, const gregorio_note *const current_note) { - if ((current_glyph->u.notes.liquescentia == L_DEMINUTUS_INITIO_DEBILIS - || current_glyph->u.notes.liquescentia == L_DEMINUTUS) + if ((current_glyph->u.notes.liquescentia & L_DEMINUTUS) && current_note->next) { return PenultBeforeDeminutus; } else { @@ -145,7 +151,7 @@ static __inline const char *last_note_case( } if (current_note->previous->u.note.pitch < current_note->u.note.pitch) { if (current_glyph->u.notes.liquescentia & - (L_AUCTUS_ASCENDENS | L_AUCTUS_DESCENDENS | L_AUCTA)) { + (L_AUCTUS_ASCENDENS | L_AUCTUS_DESCENDENS)) { return FinalConnectedAuctus; } else { return FinalConnectedVirga; @@ -169,28 +175,42 @@ static __inline const char *first_note_case( != 1) { ambitus_one = false; } - if (current_note->u.note.shape == S_ORISCUS) { + switch (current_note->u.note.shape) { + case S_ORISCUS_ASCENDENS: + case S_ORISCUS_DESCENDENS: return ambitus_one ? InitialOriscus : InitialConnectedOriscus; - } - if (current_note->u.note.shape == S_QUILISMA) { + + case S_QUILISMA: return ambitus_one ? InitialQuilisma : InitialConnectedQuilisma; - } - if (ambitus_one) { - return InitialPunctum; - } - if (current_note->u.note.shape > current_note->next->u.note.shape) { - return InitialConnectedVirga; - } else { - return InitialConnectedPunctum; + + default: + if (ambitus_one) { + return InitialPunctum; + } + if (current_note->u.note.pitch > current_note->next->u.note.pitch) { + return InitialConnectedVirga; + } else { + return InitialConnectedPunctum; + } } } } +static __inline const char *fused_single_note_case( + const gregorio_glyph *const glyph, const char *const unfused_case, + const char *const fused_case) { + int fuse_to_next_glyph = glyph->u.notes.fuse_to_next_glyph; + if (fuse_to_next_glyph < -1 || fuse_to_next_glyph > 1) { + return fused_case; + } + return unfused_case; +} + static __inline gregorio_vposition above_if_auctus( const gregorio_glyph *const glyph) { if (glyph->u.notes.liquescentia & - (L_AUCTUS_ASCENDENS | L_AUCTUS_DESCENDENS | L_AUCTA)) { + (L_AUCTUS_ASCENDENS | L_AUCTUS_DESCENDENS)) { return VPOS_ABOVE; } return VPOS_BELOW; @@ -200,7 +220,7 @@ static __inline gregorio_vposition below_if_auctus( const gregorio_glyph *const glyph) { if (glyph->u.notes.liquescentia & - (L_AUCTUS_ASCENDENS | L_AUCTUS_DESCENDENS | L_AUCTA)) { + (L_AUCTUS_ASCENDENS | L_AUCTUS_DESCENDENS)) { return VPOS_BELOW; } return VPOS_ABOVE; @@ -215,6 +235,17 @@ static __inline gregorio_vposition above_if_h_episema( return VPOS_BELOW; } +static __inline gregorio_vposition above_if_fused_next_h_episema( + const gregorio_glyph *glyph) +{ + const gregorio_glyph *next = gregorio_next_non_texverb_glyph(glyph); + if (next && next->type == GRE_GLYPH + && is_fused(next->u.notes.liquescentia)) { + return above_if_h_episema(next->u.notes.first_note); + } + return VPOS_BELOW; +} + static __inline gregorio_vposition above_if_either_h_episema( const gregorio_note *const note) { @@ -241,7 +272,7 @@ static __inline void low_high_set_lower(const gregorio_glyph *const glyph, { if ((glyph->u.notes.liquescentia & L_DEMINUTUS) || !(glyph->u.notes.liquescentia - & (L_AUCTUS_ASCENDENS|L_AUCTUS_DESCENDENS|L_AUCTA))) { + & (L_AUCTUS_ASCENDENS | L_AUCTUS_DESCENDENS))) { note->is_lower_note = true; } } @@ -251,7 +282,7 @@ static __inline void low_high_set_upper(const gregorio_glyph *const glyph, { if ((glyph->u.notes.liquescentia & L_DEMINUTUS) || !(glyph->u.notes.liquescentia - & (L_AUCTUS_ASCENDENS|L_AUCTUS_DESCENDENS|L_AUCTA))) { + & (L_AUCTUS_ASCENDENS | L_AUCTUS_DESCENDENS))) { note->is_upper_note = true; } } @@ -349,6 +380,7 @@ static gregorio_vposition advise_positioning(const gregorio_glyph *const glyph, { gregorio_vposition h_episema = VPOS_AUTO, v_episema = VPOS_AUTO; bool v_episema_below_is_lower = false, done; + const gregorio_glyph *previous; /* no need to clear is_lower_note/is_upper_note because we used calloc */ @@ -386,8 +418,11 @@ static gregorio_vposition advise_positioning(const gregorio_glyph *const glyph, } break; case T_PESQUADRATUM: + case T_PESQUADRATUM_LONGQUEUE: case T_PESQUASSUS: + case T_PESQUASSUS_LONGQUEUE: case T_PESQUILISMAQUADRATUM: + case T_PESQUILISMAQUADRATUM_LONGQUEUE: if (i == 1) { note->gtex_offset_case = first_note_case(note, glyph); h_episema = above_if_h_episema(note->next); @@ -455,7 +490,8 @@ static gregorio_vposition advise_positioning(const gregorio_glyph *const glyph, note->gtex_offset_case = TorcResQuilismaAuctusSecondOneAny; break; - case S_ORISCUS: + case S_ORISCUS_ASCENDENS: + case S_ORISCUS_DESCENDENS: note->gtex_offset_case = TorcResOriscusAuctusSecondOneAny; break; @@ -469,7 +505,8 @@ static gregorio_vposition advise_positioning(const gregorio_glyph *const glyph, note->gtex_offset_case = TorcResQuilismaAuctusSecondWideAny; break; - case S_ORISCUS: + case S_ORISCUS_ASCENDENS: + case S_ORISCUS_DESCENDENS: note->gtex_offset_case = TorcResOriscusAuctusSecondWideAny; break; @@ -543,7 +580,7 @@ static gregorio_vposition advise_positioning(const gregorio_glyph *const glyph, case HEPISEMA_FIRST_TWO: case 2: if (glyph->u.notes.liquescentia & - (L_AUCTUS_ASCENDENS | L_AUCTUS_DESCENDENS | L_AUCTA)) { + (L_AUCTUS_ASCENDENS | L_AUCTUS_DESCENDENS)) { /* auctus */ if (glyph->u.notes.liquescentia & L_INITIO_DEBILIS) { note->gtex_offset_case = TorcResDebilisAuctusSecondAnyAny; @@ -555,7 +592,8 @@ static gregorio_vposition advise_positioning(const gregorio_glyph *const glyph, note->gtex_offset_case = TorcResQuilismaAuctusSecondOneAny; break; - case S_ORISCUS: + case S_ORISCUS_ASCENDENS: + case S_ORISCUS_DESCENDENS: note->gtex_offset_case = TorcResOriscusAuctusSecondOneAny; break; @@ -569,7 +607,8 @@ static gregorio_vposition advise_positioning(const gregorio_glyph *const glyph, note->gtex_offset_case = TorcResQuilismaAuctusSecondWideAny; break; - case S_ORISCUS: + case S_ORISCUS_ASCENDENS: + case S_ORISCUS_DESCENDENS: note->gtex_offset_case = TorcResOriscusAuctusSecondWideAny; break; @@ -593,7 +632,8 @@ static gregorio_vposition advise_positioning(const gregorio_glyph *const glyph, note->gtex_offset_case = TorcResQuilismaNonAuctusSecondOneOne; break; - case S_ORISCUS: + case S_ORISCUS_ASCENDENS: + case S_ORISCUS_DESCENDENS: note->gtex_offset_case = TorcResOriscusNonAuctusSecondOneOne; break; @@ -608,7 +648,8 @@ static gregorio_vposition advise_positioning(const gregorio_glyph *const glyph, note->gtex_offset_case = TorcResQuilismaNonAuctusSecondWideOne; break; - case S_ORISCUS: + case S_ORISCUS_ASCENDENS: + case S_ORISCUS_DESCENDENS: note->gtex_offset_case = TorcResOriscusNonAuctusSecondWideOne; break; @@ -632,7 +673,8 @@ static gregorio_vposition advise_positioning(const gregorio_glyph *const glyph, note->gtex_offset_case = TorcResQuilismaNonAuctusSecondOneWide; break; - case S_ORISCUS: + case S_ORISCUS_ASCENDENS: + case S_ORISCUS_DESCENDENS: note->gtex_offset_case = TorcResOriscusNonAuctusSecondOneWide; break; @@ -647,7 +689,8 @@ static gregorio_vposition advise_positioning(const gregorio_glyph *const glyph, note->gtex_offset_case = TorcResQuilismaNonAuctusSecondWideWide; break; - case S_ORISCUS: + case S_ORISCUS_ASCENDENS: + case S_ORISCUS_DESCENDENS: note->gtex_offset_case = TorcResOriscusNonAuctusSecondWideWide; break; @@ -673,16 +716,17 @@ static gregorio_vposition advise_positioning(const gregorio_glyph *const glyph, break; } /* else fallthrough to the next case! */ - case T_PORRECTUS: + case T_PORRECTUS: /* or porrectus-like flexus */ switch (i) { case HEPISEMA_FIRST_TWO: case 1: - if (glyph->u.notes.liquescentia & - (L_AUCTUS_ASCENDENS | L_AUCTUS_DESCENDENS | L_AUCTA)) { + if (glyph->u.notes.fuse_to_next_glyph || + (glyph->u.notes.liquescentia + & (L_AUCTUS_ASCENDENS | L_AUCTUS_DESCENDENS))) { /* auctus */ note->gtex_offset_case = PorrAuctusInitialAny; - } else if (note->next->next->u.note.pitch - - note->next->u.note.pitch == 1) { + } else if (note->next->next->u.note.pitch + - note->next->u.note.pitch == 1) { /* non-auctus with a second ambitus of 1 */ note->gtex_offset_case = PorrNonAuctusInitialOne; } else { @@ -698,12 +742,16 @@ static gregorio_vposition advise_positioning(const gregorio_glyph *const glyph, if (glyph->u.notes.liquescentia & L_DEMINUTUS) { note->gtex_offset_case = PenultBeforeDeminutus; } else if (glyph->u.notes.liquescentia - & (L_AUCTUS_ASCENDENS|L_AUCTUS_DESCENDENS|L_AUCTA)) { + & (L_AUCTUS_ASCENDENS | L_AUCTUS_DESCENDENS)) { note->gtex_offset_case = PenultBeforePunctumWide; } else { note->gtex_offset_case = FinalPunctum; } h_episema = above_if_h_episema(note->previous); + if (h_episema == VPOS_BELOW && !note->next) { + /* handle porrectus-like flexus */ + h_episema = above_if_fused_next_h_episema(glyph); + } v_episema = VPOS_BELOW; break; default: /* case 3 */ @@ -741,6 +789,27 @@ static gregorio_vposition advise_positioning(const gregorio_glyph *const glyph, break; } break; + case T_SALICUS_FLEXUS: + done = true; + v_episema = VPOS_BELOW; + switch (i) { + case 3: + note->gtex_offset_case = note_before_last_note_case(glyph, note); + h_episema = VPOS_ABOVE; + break; + case 4: + note->gtex_offset_case = last_note_case(glyph, FinalPunctum, note, + false); + h_episema = VPOS_BELOW; + break; + default: + done = false; + break; + } + if (done) { + break; + } + /* else fallthrough to the next case! */ case T_SALICUS: case T_SALICUS_LONGQUEUE: v_episema = VPOS_BELOW; @@ -841,7 +910,14 @@ static gregorio_vposition advise_positioning(const gregorio_glyph *const glyph, } break; default: /* case T_ONE_NOTE */ - h_episema = VPOS_ABOVE; + previous = gregorio_previous_non_texverb_glyph(glyph); + if ((!previous || previous->type != GRE_GLYPH + || previous->u.notes.fuse_to_next_glyph < 0) + && (glyph->u.notes.fuse_to_next_glyph > 0)) { + h_episema = above_if_fused_next_h_episema(glyph); + } else { + h_episema = VPOS_ABOVE; + } v_episema = VPOS_BELOW; switch (note->u.note.shape) { case S_PUNCTUM_INCLINATUM_DEMINUTUS: @@ -857,11 +933,18 @@ static gregorio_vposition advise_positioning(const gregorio_glyph *const glyph, note->gtex_offset_case = FinalStropha; break; case S_QUILISMA: - note->gtex_offset_case = FinalQuilisma; + note->gtex_offset_case = fused_single_note_case(glyph, + FinalQuilisma, LeadingQuilisma); break; - case S_ORISCUS: - case S_ORISCUS_AUCTUS: + case S_ORISCUS_ASCENDENS: + case S_ORISCUS_DESCENDENS: case S_ORISCUS_DEMINUTUS: + note->gtex_offset_case = fused_single_note_case(glyph, FinalOriscus, + LeadingOriscus); + break; + case S_ORISCUS_CAVUM_ASCENDENS: + case S_ORISCUS_CAVUM_DESCENDENS: + case S_ORISCUS_CAVUM_DEMINUTUS: note->gtex_offset_case = FinalOriscus; break; case S_VIRGA: @@ -881,9 +964,19 @@ static gregorio_vposition advise_positioning(const gregorio_glyph *const glyph, case S_LINEA: note->gtex_offset_case = FinalPunctum; break; + case S_FLAT: + note->gtex_offset_case = Flat; + break; + case S_SHARP: + note->gtex_offset_case = Sharp; + break; + case S_NATURAL: + note->gtex_offset_case = Natural; + break; default: - note->gtex_offset_case = last_note_case(glyph, FinalPunctum, note, - true); + note->gtex_offset_case = last_note_case(glyph, + fused_single_note_case(glyph, FinalPunctum, LeadingPunctum), + note, true); break; } break; @@ -966,6 +1059,8 @@ typedef struct height_computation { const gregorio_element *start_element; const gregorio_glyph *start_glyph; gregorio_note *start_note; + const gregorio_element *last_connected_element; + const gregorio_glyph *last_connected_glyph; gregorio_note *last_connected_note; } height_computation; @@ -1023,6 +1118,23 @@ static bool is_h_episema_below_better_height(const signed char new_height, return new_height < old_height; } +static __inline bool has_high_ledger_line(const signed char height, + bool is_sign, const gregorio_score *const score) +{ + if (is_sign) { + return height > score->high_ledger_line_pitch; + } + return height >= score->high_ledger_line_pitch; +} + +static __inline bool has_low_ledger_line(const signed char height, bool is_sign) +{ + if (is_sign) { + return height < LOW_LEDGER_LINE_PITCH; + } + return height <= LOW_LEDGER_LINE_PITCH; +} + static __inline void start_h_episema(height_computation *const h, const gregorio_element *const element, const gregorio_glyph *const glyph, gregorio_note *const note) @@ -1034,8 +1146,115 @@ static __inline void start_h_episema(height_computation *const h, h->height = compute_h_episema_height(glyph, note, h->vpos); } +static __inline void position_h_episema(gregorio_note *const note, + const height_computation *const h, const bool connect, + const bool high_ledger_line, const bool low_ledger_line) +{ + h->position(note, h->height, connect); + if (!note->explicit_high_ledger_line && !note->supposed_high_ledger_line) { + note->supposed_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; + } +} + +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) +{ + const gregorio_element *element = h->last_connected_element; + const gregorio_glyph *glyph = h->last_connected_glyph; + gregorio_note *note = h->last_connected_note; + bool eat_upper_note = note->is_lower_note, keep_going = false; + + if (!*high_ledger_line || !*low_ledger_line) { + /* going forwards, we must iterate through any lower notes */ + do { + note = note->next; + if (!note) { + do { + glyph = glyph->next; + if (!glyph) { + do { + element = element->next; + if (!element) { + return; + } + } while (element->type != GRE_ELEMENT); + glyph = element->u.first_glyph; + } + } while (glyph->type != GRE_GLYPH); + 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); + + if (keep_going) { + keep_going = false; + } + if (eat_upper_note && note->is_upper_note) { + eat_upper_note = false; + keep_going = true; + } + } while ((!*high_ledger_line || !*low_ledger_line) + && (note->is_lower_note || keep_going)); + } +} + +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) +{ + const gregorio_element *element = h->start_element; + const gregorio_glyph *glyph = h->start_glyph; + gregorio_note *note = h->start_note; + bool eat_lower_note = note->is_upper_note, keep_going = false; + + if (!*high_ledger_line || !*low_ledger_line) { + /* going backwards, we must iterate through any upper notes */ + do { + note = note->previous; + if (!note) { + do { + glyph = glyph->previous; + if (!glyph) { + do { + element = element->previous; + if (!element) { + return; + } + } while (element->type != GRE_ELEMENT); + for (glyph = element->u.first_glyph; glyph->next; + glyph = glyph->next) { + /* just iterate to find the last glyph */ + } + } + } 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); + + if (keep_going) { + keep_going = false; + } + if (eat_lower_note && note->is_lower_note) { + eat_lower_note = false; + keep_going = true; + } + } while ((!*high_ledger_line || !*low_ledger_line) + && (note->is_upper_note || keep_going)); + } +} + static __inline void set_h_episema_height(const height_computation *const h, - gregorio_note *const end) + gregorio_note *const end, const gregorio_score *const score) { gregorio_note *last_note = NULL; @@ -1043,6 +1262,14 @@ static __inline void set_h_episema_height(const height_computation *const h, const gregorio_glyph *glyph = h->start_glyph; gregorio_note *note = h->start_note; + bool high_ledger_line = has_high_ledger_line(h->height, true, score) + || has_high_ledger_line(h->height - h->vpos, false, score); + 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); + for ( ; element; element = element->next) { if (element->type == GRE_ELEMENT) { for (glyph = glyph? glyph : element->u.first_glyph; glyph; @@ -1052,12 +1279,14 @@ static __inline void set_h_episema_height(const height_computation *const h, note = note->next) { if (end && note == end) { if (last_note) { - h->position(last_note, h->height, false); + position_h_episema(last_note, h, false, + high_ledger_line, low_ledger_line); } return; } if (h->is_applicable(note)) { - h->position(note, h->height, true); + position_h_episema(note, h, true, high_ledger_line, + low_ledger_line); last_note = note; } } @@ -1069,7 +1298,8 @@ static __inline void set_h_episema_height(const height_computation *const h, } if (last_note) { - h->position(last_note, h->height, false); + position_h_episema(last_note, h, false, high_ledger_line, + low_ledger_line); } } @@ -1101,45 +1331,68 @@ static __inline bool has_space_to_left(const gregorio_note *const note) { } static __inline void end_h_episema(height_computation *const h, - gregorio_note *const end) + gregorio_note *const end, const gregorio_score *const score) { signed char proposed_height; if (h->active) { /* don't let the episema clash with the note before or after */ - if (is_connected_left(h->get_size(h->start_note)) - && h->start_note->previous - && h->start_note->previous->type == GRE_NOTE - && is_connected_right(h->get_size(h->start_note->previous)) - && !has_space_to_left(h->start_note)) { - proposed_height = h->start_note->previous->u.note.pitch + h->vpos; - if (h->is_better_height(proposed_height, h->height)) { - h->height = proposed_height; - } - } - if (end && end->type == GRE_NOTE) { - gregorio_note *note; - /* this loop checks that it's within the same glyph */ - for (note = end->previous; note; note = note->previous) { - if (note == h->start_note) { - if (is_connected_left(h->get_size(end)) - && h->last_connected_note - && h->last_connected_note->next - && h->last_connected_note->next->type == GRE_NOTE - && !has_space_to_left(h->last_connected_note->next) - && is_connected_right(h->get_size( - h->last_connected_note))) { - proposed_height = end->u.note.pitch + h->vpos; + if (is_connected_left(h->get_size(h->start_note))) { + if (h->start_note->previous) { + if (!has_space_to_left(h->start_note) + && h->start_note->previous->type == GRE_NOTE + && is_connected_right(h->get_size( + h->start_note->previous))) { + proposed_height = h->start_note->previous->u.note.pitch + h->vpos; + if (h->is_better_height(proposed_height, h->height)) { + h->height = proposed_height; + } + } + } else { + const gregorio_glyph *previous = + gregorio_previous_non_texverb_glyph(h->start_glyph); + if (previous && previous->type == GRE_GLYPH + && is_fused(h->start_glyph->u.notes.liquescentia)) { + gregorio_note *note = gregorio_glyph_last_note( + h->start_glyph->previous); + if (note->type == GRE_NOTE + && is_connected_right(h->get_size(note))) { + proposed_height = note->u.note.pitch + h->vpos; if (h->is_better_height(proposed_height, h->height)) { h->height = proposed_height; } } - break; + } + } + } + if (end && end->type == GRE_NOTE && is_connected_left(h->get_size(end)) + && h->last_connected_note + && is_connected_right(h->get_size(h->last_connected_note))) { + if (h->last_connected_note->next) { + if (h->last_connected_note->next->type == GRE_NOTE + && !has_space_to_left(h->last_connected_note->next)) { + gregorio_note *note; + /* this loop checks that it's within the same glyph */ + for (note = end->previous; note; note = note->previous) { + if (note == h->start_note) { + proposed_height = end->u.note.pitch + h->vpos; + if (h->is_better_height(proposed_height, + h->height)) { + h->height = proposed_height; + } + break; + } + } + } + } else if (is_fused(end->u.note.liquescentia) && !end->previous) { + proposed_height = end->u.note.pitch + h->vpos; + if (h->is_better_height(proposed_height, h->height)) { + h->height = proposed_height; } } } - set_h_episema_height(h, end); + set_h_episema_height(h, end, score); h->active = false; h->height = 0; @@ -1147,6 +1400,8 @@ static __inline void end_h_episema(height_computation *const h, h->start_element = NULL; h->start_glyph = NULL; h->start_note = NULL; + h->last_connected_element = NULL; + h->last_connected_glyph = NULL; h->last_connected_note = NULL; } } @@ -1154,7 +1409,7 @@ static __inline void end_h_episema(height_computation *const h, static __inline void compute_h_episema(height_computation *const h, const gregorio_element *const element, const gregorio_glyph *const glyph, gregorio_note *const note, - const int i, const gtex_type type) + const int i, const gtex_type type, const gregorio_score *const score) { signed char next_height; grehepisema_size size; @@ -1174,7 +1429,7 @@ static __inline void compute_h_episema(height_computation *const h, } } else { - end_h_episema(h, note); + end_h_episema(h, note, score); start_h_episema(h, element, glyph, note); } } else { @@ -1182,9 +1437,11 @@ static __inline void compute_h_episema(height_computation *const h, } h->connected = h->is_connected(note) && is_connected_right(size); + h->last_connected_element = element; + h->last_connected_glyph = glyph; h->last_connected_note = note; } else { - end_h_episema(h, note); + end_h_episema(h, note, score); } } else if (!h->is_shown(note)) { /* special handling for porrectus shapes because of their shape: @@ -1195,14 +1452,14 @@ static __inline void compute_h_episema(height_computation *const h, case T_PORRECTUS: case T_PORRECTUS_FLEXUS: if (i == 2) { - end_h_episema(h, note); + end_h_episema(h, note, score); } break; case T_TORCULUS_RESUPINUS: case T_TORCULUS_RESUPINUS_FLEXUS: if (i == 3) { - end_h_episema(h, note); + end_h_episema(h, note, score); } break; @@ -1216,7 +1473,7 @@ static __inline void compute_h_episema(height_computation *const h, static __inline void compute_note_positioning(height_computation *const above, height_computation *const below, const gregorio_element *const element, const gregorio_glyph *const glyph, gregorio_note *const note, - const int i, const gtex_type type) + const int i, const gtex_type type, const gregorio_score *const score) { gregorio_vposition default_vpos = advise_positioning(glyph, note, i, type); @@ -1230,11 +1487,165 @@ static __inline void compute_note_positioning(height_computation *const above, } } - compute_h_episema(above, element, glyph, note, i, type); - compute_h_episema(below, element, glyph, note, i, type); + compute_h_episema(above, element, glyph, note, i, type, score); + compute_h_episema(below, element, glyph, note, i, type, score); +} + +static __inline int compute_fused_shift(const gregorio_glyph *glyph) +{ + int shift; + gregorio_note *first_note, *prev_note; + const gregorio_glyph *next, *previous; + bool next_is_fused; + + if (!glyph || !is_fused(glyph->u.notes.liquescentia) + || glyph->type != GRE_GLYPH + || !(first_note = glyph->u.notes.first_note) + || first_note->type != GRE_NOTE + || !(previous = gregorio_previous_non_texverb_glyph(glyph)) + || previous->type != GRE_GLYPH + || previous->u.notes.liquescentia & TAIL_LIQUESCENTIA_MASK + || !previous->u.notes.first_note) { + /* base conditions prevent fusion */ + return 0; + } + + next = gregorio_next_non_texverb_glyph(glyph); + next_is_fused = next && next->type == GRE_GLYPH + && is_fused(next->u.notes.liquescentia); + + switch (glyph->u.notes.glyph_type) { + case G_PORRECTUS: + case G_PODATUS: + if (next_is_fused) { + return 0; + } + /* else it may be fused to the previous note */ + break; + + case G_PUNCTUM: + case G_FLEXA: + /* these may be fused to the previous note */ + break; + + default: + /* anything else may not be fused to the previous note */ + return 0; + } + + switch (first_note->u.note.shape) { + case S_QUILISMA: + case S_QUILISMA_QUADRATUM: + case S_PUNCTUM_CAVUM: + case S_ORISCUS_CAVUM_ASCENDENS: + case S_ORISCUS_CAVUM_DESCENDENS: + case S_ORISCUS_CAVUM_DEMINUTUS: + case S_FLAT: + case S_SHARP: + case S_NATURAL: + /* if this glyph starts with one of these, it's not fusable */ + return 0; + + default: + /* anything else is potentially fusable */ + break; + } + + switch (previous->u.notes.glyph_type) { + case G_PUNCTUM: + case G_FLEXA: + case G_VIRGA_REVERSA: + /* these are potentially fusable to this note */ + break; + + default: + /* everything else is not fusable */ + return 0; + } + + prev_note = gregorio_glyph_last_note(previous); + + if (prev_note->type != GRE_NOTE) { + /* previous note wasn't a note */ + return 0; + } + + switch (prev_note->u.note.shape) { + case S_PUNCTUM_CAVUM: + case S_ORISCUS_CAVUM_ASCENDENS: + case S_ORISCUS_CAVUM_DESCENDENS: + case S_ORISCUS_CAVUM_DEMINUTUS: + case S_FLAT: + case S_SHARP: + case S_NATURAL: + /* these don't fuse to anything */ + return 0; + + default: + /* anything else is potentially fusable */ + break; + } + + shift = first_note->u.note.pitch - prev_note->u.note.pitch; + if (shift < -5 || shift > 5) { + /* ambitus too large to fuse */ + return 0; + } + + if (shift > 0 && previous->u.notes.glyph_type == G_VIRGA_REVERSA) { + /* virga reversa cannot fuse upwards */ + return 0; + } + + /* the FLEXA check below checks for a porrectus-like flexus, which is not + * fusable from above */ + if (shift < 0 && ((next_is_fused && glyph->u.notes.glyph_type == G_FLEXA) + || glyph->u.notes.glyph_type == G_PORRECTUS + || (previous->u.notes.glyph_type == G_PUNCTUM + && is_initio_debilis(previous->u.notes.liquescentia)))) { + /* may not be fused from above */ + return 0; + } + + /* Special cases for oriscus */ + switch (first_note->u.note.shape) { + gregorio_note *next_note; + const gregorio_glyph *next_glyph; + case S_ORISCUS_ASCENDENS: + case S_ORISCUS_DESCENDENS: + case S_ORISCUS_SCAPUS: + next_note = first_note->next; + if (!next_note && (next_glyph = gregorio_next_non_texverb_glyph(glyph)) + && next_glyph->type == GRE_GLYPH + && is_fused(next_glyph->u.notes.liquescentia)) { + next_note = next_glyph->u.notes.first_note; + } + if (next_note) { + if (next_note->u.note.pitch < first_note->u.note.pitch) { + /* then this note should be an down-up-down oriscus */ + if (shift > 0) { + /* down-up-down oricus cannot be fused from below */ + return 0; + } + } else { + /* then this note should be an up-down-up oriscus */ + if (shift < 0) { + /* up-down-up oricus cannot be fused from above */ + return 0; + } + } + } + break; + + default: + break; + } + + return shift; } -void gregoriotex_compute_positioning(const gregorio_element *element) +void gregoriotex_compute_positioning(const gregorio_element *element, + const gregorio_score *const score) { height_computation above = { /*.vpos =*/ VPOS_ABOVE, @@ -1251,6 +1662,8 @@ void gregoriotex_compute_positioning(const gregorio_element *element) /*.start_element =*/ NULL, /*.start_glyph =*/ NULL, /*.start_note =*/ NULL, + /*.last_connected_element =*/ NULL, + /*.last_connected_glyph =*/ NULL, /*.last_connected_note =*/ NULL, }; height_computation below = { @@ -1268,6 +1681,8 @@ void gregoriotex_compute_positioning(const gregorio_element *element) /*.start_element =*/ NULL, /*.start_glyph =*/ NULL, /*.start_note =*/ NULL, + /*.last_connected_element =*/ NULL, + /*.last_connected_glyph =*/ NULL, /*.last_connected_note =*/ NULL, }; int i; @@ -1276,11 +1691,13 @@ void gregoriotex_compute_positioning(const gregorio_element *element) for (; element; element = element->next) { if (element->type == GRE_ELEMENT) { - const gregorio_glyph *glyph; + gregorio_glyph *glyph; for (glyph = element->u.first_glyph; glyph; glyph = glyph->next) { if (glyph->type == GRE_GLYPH) { gregorio_note *note; + glyph->u.notes.fuse_to_next_glyph = compute_fused_shift( + gregorio_next_non_texverb_glyph(glyph)); i = 0; gregoriotex_determine_glyph_name(glyph, element, &ignored, &type); @@ -1288,17 +1705,17 @@ void gregoriotex_compute_positioning(const gregorio_element *element) note = note->next) { if (note->type == GRE_NOTE) { compute_note_positioning(&above, &below, element, - glyph, note, ++i, type); + glyph, note, ++i, type, score); } } } } } else if (!is_bridgeable_space(element)) { - end_h_episema(&above, NULL); - end_h_episema(&below, NULL); + end_h_episema(&above, NULL, score); + end_h_episema(&below, NULL, score); } } - end_h_episema(&above, NULL); - end_h_episema(&below, NULL); + end_h_episema(&above, NULL, score); + end_h_episema(&below, NULL, score); } |