/* * Gregorio is a program that translates gabc files to GregorioTeX * This file contains the logic for positioning signs on neumes. * * Copyright (C) 2008-2019 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 . */ #include "config.h" #include #include #include "bool.h" #include "struct.h" #include "gregoriotex.h" /* (loose) naming convention, employing camel case to be TeX-csname-compliant: * {specific-glyph-shape}{note-position}{note-shape}{first-ambitus}{second-ambitus} */ OFFSET_CASE(FinalPunctum); OFFSET_CASE(FinalDeminutus); OFFSET_CASE(PenultBeforePunctumWide); OFFSET_CASE(PenultBeforeDeminutus); OFFSET_CASE(AntepenultBeforePunctum); OFFSET_CASE(AntepenultBeforeDeminutus); OFFSET_CASE(InitialPunctum); OFFSET_CASE(InitioDebilis); OFFSET_CASE(PorrNonAuctusInitialWide); OFFSET_CASE(PorrNonAuctusInitialOne); OFFSET_CASE(PorrAuctusInitialAny); OFFSET_CASE(FinalInclinatum); OFFSET_CASE(FinalInclinatumDeminutus); OFFSET_CASE(FinalStropha); OFFSET_CASE(FinalQuilisma); OFFSET_CASE(FinalOriscus); OFFSET_CASE(PenultBeforePunctumOne); OFFSET_CASE(FinalUpperPunctum); OFFSET_CASE(InitialOriscus); OFFSET_CASE(InitialQuilisma); OFFSET_CASE(TorcResNonAuctusSecondWideWide); OFFSET_CASE(TorcResNonAuctusSecondOneWide); OFFSET_CASE(TorcResDebilisNonAuctusSecondAnyWide); OFFSET_CASE(FinalLineaPunctum); OFFSET_CASE(TorcResQuilismaNonAuctusSecondWideWide); OFFSET_CASE(TorcResOriscusNonAuctusSecondWideWide); OFFSET_CASE(TorcResQuilismaNonAuctusSecondOneWide); OFFSET_CASE(TorcResOriscusNonAuctusSecondOneWide); OFFSET_CASE(TorcResNonAuctusSecondWideOne); OFFSET_CASE(TorcResDebilisNonAuctusSecondAnyOne); OFFSET_CASE(TorcResQuilismaNonAuctusSecondWideOne); OFFSET_CASE(TorcResOriscusNonAuctusSecondWideOne); OFFSET_CASE(TorcResNonAuctusSecondOneOne); OFFSET_CASE(TorcResQuilismaNonAuctusSecondOneOne); OFFSET_CASE(TorcResOriscusNonAuctusSecondOneOne); OFFSET_CASE(TorcResAuctusSecondWideAny); OFFSET_CASE(TorcResDebilisAuctusSecondAnyAny); OFFSET_CASE(TorcResQuilismaAuctusSecondWideAny); OFFSET_CASE(TorcResOriscusAuctusSecondWideAny); OFFSET_CASE(TorcResAuctusSecondOneAny); OFFSET_CASE(TorcResQuilismaAuctusSecondOneAny); OFFSET_CASE(TorcResOriscusAuctusSecondOneAny); OFFSET_CASE(ConnectedPenultBeforePunctumWide); OFFSET_CASE(ConnectedPenultBeforePunctumOne); OFFSET_CASE(InitialConnectedPunctum); OFFSET_CASE(InitialConnectedVirga); OFFSET_CASE(InitialConnectedQuilisma); OFFSET_CASE(InitialConnectedOriscus); OFFSET_CASE(FinalConnectedPunctum); OFFSET_CASE(FinalConnectedAuctus); OFFSET_CASE(FinalVirgaAuctus); 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) { if (!current_note->previous || current_note->u.note.pitch - current_note->previous->u.note.pitch > 1) { return ConnectedPenultBeforePunctumOne; } else { return PenultBeforePunctumOne; } } else { if (!current_note->previous || current_note->u.note.pitch - current_note->previous->u.note.pitch > 1) { return ConnectedPenultBeforePunctumWide; } else { return PenultBeforePunctumWide; } } } 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) && current_note->next) { return PenultBeforeDeminutus; } else { return note_before_last_note_case_ignoring_deminutus(current_note); } } /* num can be FinalPunctum or FinalUpperPunctum according if the last note is a * standard punctum or a smaller punctum (for pes, porrectus and torculus * resupinus */ static __inline const char *last_note_case( const gregorio_glyph *const current_glyph, const char* offset_pos, gregorio_note *current_note, bool no_ambitus_one) { if (current_glyph->u.notes.liquescentia & L_DEMINUTUS) { /* * may seem strange, but it is unlogical to typeset a small horizontal * episema at the end of a flexus deminutus */ return FinalDeminutus; } if (!current_note->previous || (!no_ambitus_one && (offset_pos == FinalUpperPunctum || current_note->previous->u.note.pitch - current_note->u.note.pitch == 1 || current_note->u.note.pitch - current_note->previous->u.note.pitch == 1))) { return offset_pos; } if (current_note->previous->u.note.pitch < current_note->u.note.pitch) { if (current_glyph->u.notes.liquescentia & (L_AUCTUS_ASCENDENS | L_AUCTUS_DESCENDENS)) { return FinalConnectedAuctus; } else { return FinalConnectedVirga; } } else { return FinalConnectedPunctum; } } static __inline const char *first_note_case( const gregorio_note *const current_note, const gregorio_glyph *const current_glyph) { bool ambitus_one = true; if (current_glyph->u.notes.liquescentia >= L_INITIO_DEBILIS) { return InitioDebilis; } else { if (current_note->next && current_note->next->u.note.pitch - current_note->u.note.pitch != 1 && current_note->u.note.pitch - current_note->next->u.note.pitch != 1) { ambitus_one = false; } switch (current_note->u.note.shape) { case S_ORISCUS_ASCENDENS: case S_ORISCUS_DESCENDENS: return ambitus_one ? InitialOriscus : InitialConnectedOriscus; case S_QUILISMA: return ambitus_one ? InitialQuilisma : InitialConnectedQuilisma; 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 below_if_auctus( const gregorio_glyph *const glyph) { if (glyph->u.notes.liquescentia & (L_AUCTUS_ASCENDENS | L_AUCTUS_DESCENDENS)) { return VPOS_BELOW; } return VPOS_ABOVE; } static __inline gregorio_vposition above_if_h_episema( const gregorio_note *const note) { if (note && note->h_episema_above) { return VPOS_ABOVE; } 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); gregorio_assert(next && next->type == GRE_GLYPH && is_fused(next->u.notes.liquescentia), above_if_fused_next_h_episema, "expected this glyph to be fused to the next one", return VPOS_BELOW); return above_if_h_episema(next->u.notes.first_note); } static __inline gregorio_vposition above_if_either_h_episema( const gregorio_note *const note) { if ((note->previous && note->previous->h_episema_above) || (note->next && note->next->h_episema_above)) { return VPOS_ABOVE; } return VPOS_BELOW; } static __inline gregorio_vposition below_if_next_ambitus_allows( const gregorio_note *const note) { assert(note->next); if (!note->next || note->u.note.pitch - note->next->u.note.pitch > 2) { return VPOS_BELOW; } return VPOS_ABOVE; } static __inline void low_high_set_lower(const gregorio_glyph *const glyph, gregorio_note *const note) { if ((glyph->u.notes.liquescentia & L_DEMINUTUS) || !(glyph->u.notes.liquescentia & (L_AUCTUS_ASCENDENS | L_AUCTUS_DESCENDENS))) { note->is_lower_note = true; } } static __inline void low_high_set_upper(const gregorio_glyph *const glyph, gregorio_note *const note) { if ((glyph->u.notes.liquescentia & L_DEMINUTUS) || !(glyph->u.notes.liquescentia & (L_AUCTUS_ASCENDENS | L_AUCTUS_DESCENDENS))) { note->is_upper_note = true; } } static __inline void high_low_set_upper(const gregorio_glyph *const glyph, gregorio_note *const note) { if (glyph->u.notes.liquescentia & L_DEMINUTUS) { note->is_upper_note = true; } } static __inline void high_low_set_lower(const gregorio_glyph *const glyph, gregorio_note *const note) { if (glyph->u.notes.liquescentia & L_DEMINUTUS) { note->is_lower_note = true; } } /* a function that finds the good sign (additional line, vepisema or * hepisema) number, according to the gregoriotex convention (described in * gregoriotex.tex) * this function is REALLY a pain in the ass, but it is sadly necessary */ /* * * For the first note of a porrectus (flexus), this table summarizes the sign * number (amb2 is the ambitus after the diagonal stroke): * * Porrectus | Porrectus | Porrectus | Porrectus * non-auctus | non-auctus | auctus | Flexus * amb2=1 | amb2>1 | | * =================|=================|==============|============== * PorrNonAuctus | PorrNonAuctus | PorrAuctus | PorrAuctus * ~InitialOne | ~InitialWide | ~InitialAny | ~InitialAny * * For the second note of a torculus resupinus (flexus), this table summarizes * the number (amb1 is the ambitus before the diagonal stroke and amb2 is the * ambitus after the diagonal stroke): * * | | Torculus | Torculus | Torculus | Torculus * | | Resupinus | Resupinus | Resupinus | Resupinus * | Initial | non-auctus | non-auctus | auctus | Flexus * amb1 | Note | amb2=1 | amb2>1 | | * ======|============|=============|=============|============|============ * 1 | Punctum | TorcRes | TorcRes | TorcRes | TorcRes * | | ~NonAuctus | ~NonAuctus | ~Auctus | ~Auctus * | | ~Second | ~Second | ~Second | ~Second * | | ~OneOne | ~OneWide | ~OneAny | ~OneAny * ------+------------+-------------+-------------+------------+------------ * 1 | I. Debilis | TorcRes | TorcRes | TorcRes | TorcRes * | | ~Debilis | ~Debilis | ~Debilis | ~Debilis * | | ~NonAuctus | ~NonAuctus | ~Auctus | ~Auctus * | | ~Second | ~Second | ~Second | ~Second * | | ~AnyOne | ~AnyWide | ~AnyAny | ~AnyAny * ------+------------+-------------+-------------+------------+------------ * 1 | Quilisma | TorcRes | TorcRes | TorcRes | TorcRes * | | ~Quilisma | ~Quilisma | ~Quilisma | ~Quilisma * | | ~NonAuctus | ~NonAuctus | ~Auctus | ~Auctus * | | ~Second | ~Second | ~Second | ~Second * | | ~OneOne | ~OneWide | ~OneAny | ~OneAny * ------+------------+-------------+-------------+------------+------------ * 1 | Oriscus | TorcRes | TorcRes | TorcRes | TorcRes * | | ~Oriscus | ~Oriscus | ~Oriscus | ~Oriscus * | | ~NonAuctus | ~NonAuctus | ~Auctus | ~Auctus * | | ~Second | ~Second | ~Second | ~Second * | | ~OneOne | ~OneWide | ~OneAny | ~OneAny * ======|============|=============|=============|============|============ * >1 | Punctum | TorcRes | TorcRes | TorcRes | TorcRes * | | ~NonAuctus | ~NonAuctus | ~Auctus | ~Auctus * | | ~Second | ~Second | ~Second | ~Second * | | ~WideOne | ~WideWide | ~WideAny | ~WideAny * ------+------------+-------------+-------------+------------+------------ * >1 | I. Debilis | TorcRes | TorcRes | TorcRes | TorcRes * | | ~Debilis | ~Debilis | ~Debilis | ~Debilis * | | ~NonAuctus | ~NonAuctus | ~Auctus | ~Auctus * | | ~Second | ~Second | ~Second | ~Second * | | ~AnyOne | ~AnyWide | ~AnyAny | ~AnyAny * ------+------------+-------------+-------------+------------+------------ * >1 | Quilisma | TorcRes | TorcRes | TorcRes | TorcRes * | | ~Quilisma | ~Quilisma | ~Quilisma | ~Quilisma * | | ~NonAuctus | ~NonAuctus | ~Auctus | ~Auctus * | | ~Second | ~Second | ~Second | ~Second * | | ~WideOne | ~WideWide | ~WideAny | ~WideAny * ------+------------+-------------+-------------+------------+------------ * >1 | Oriscus | TorcRes | TorcRes | TorcRes | TorcRes * | | ~Oriscus | ~Oriscus | ~Oriscus | ~Oriscus * | | ~NonAuctus | ~NonAuctus | ~Auctus | ~Auctus * | | ~Second | ~Second | ~Second | ~Second * | | ~WideOne | ~WideWide | ~WideAny | ~WideAny * */ static gregorio_vposition advise_positioning(const gregorio_glyph *const glyph, gregorio_note *const note, int i, const gtex_type type) { 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 */ switch (type) { case T_PES: case T_QUILISMA_PES: /* * in the case of a pes, we put the episema just under the bottom note */ if (i == 1) { if (glyph->u.notes.liquescentia & L_INITIO_DEBILIS) { note->gtex_offset_case = InitioDebilis; h_episema = above_if_h_episema(note->next); } else { note->is_lower_note = true; if (note->u.note.shape == S_QUILISMA) { note->gtex_offset_case = FinalQuilisma; } else { note->gtex_offset_case = FinalPunctum; } h_episema = VPOS_BELOW; } v_episema = VPOS_BELOW; } else { /* i=2 */ if (!(glyph->u.notes.liquescentia & L_INITIO_DEBILIS)) { note->is_upper_note = true; } if (glyph->u.notes.liquescentia & L_DEMINUTUS) { note->gtex_offset_case = FinalDeminutus; } else { note->gtex_offset_case = FinalUpperPunctum; } h_episema = VPOS_ABOVE; v_episema = below_if_auctus(glyph); } break; case T_PES_QUADRATUM: case T_PES_QUASSUS: case T_QUILISMA_PES_QUADRATUM: if (i == 1) { note->gtex_offset_case = first_note_case(note, glyph); h_episema = above_if_h_episema(note->next); v_episema = VPOS_BELOW; } else { /* i=2 */ if (glyph->u.notes.liquescentia & L_DEMINUTUS) { note->gtex_offset_case = InitioDebilis; } else { note->gtex_offset_case = last_note_case(glyph, FinalPunctum, note, true); } h_episema = VPOS_ABOVE; v_episema = VPOS_BELOW; } break; case T_FLEXUS: case T_FLEXUS_ORISCUS: if (i == 1) { high_low_set_upper(glyph, note); if (type == T_FLEXUS_ORISCUS) { note->gtex_offset_case = InitialConnectedOriscus; } else { if (note->next && note->u.note.pitch - note->next->u.note.pitch == 1) { note->gtex_offset_case = InitialVirga; } else { note->gtex_offset_case = InitialConnectedVirga; } } h_episema = VPOS_ABOVE; if (glyph->u.notes.liquescentia & L_DEMINUTUS) { v_episema = below_if_next_ambitus_allows(note); } else { v_episema = VPOS_BELOW; } } else { /* i=2 */ high_low_set_lower(glyph, note); note->gtex_offset_case = last_note_case(glyph, FinalPunctum, note, false); if (glyph->u.notes.liquescentia & L_DEMINUTUS) { h_episema = VPOS_BELOW; } else { h_episema = above_if_h_episema(note->previous); } v_episema = VPOS_BELOW; } break; case T_TORCULUS_RESUPINUS_FLEXUS: done = true; switch (i) { case 1: note->gtex_offset_case = first_note_case(note, glyph); h_episema = above_if_h_episema(note->next); v_episema = VPOS_BELOW; break; case HEPISEMA_FIRST_TWO: case 2: if (glyph->u.notes.liquescentia & L_INITIO_DEBILIS) { note->gtex_offset_case = TorcResDebilisAuctusSecondAnyAny; } else { if (note->u.note.pitch - note->previous->u.note.pitch == 1) { switch (note->previous->u.note.shape) { case S_QUILISMA: note->gtex_offset_case = TorcResQuilismaAuctusSecondOneAny; break; case S_ORISCUS_ASCENDENS: case S_ORISCUS_DESCENDENS: note->gtex_offset_case = TorcResOriscusAuctusSecondOneAny; break; default: note->gtex_offset_case = TorcResAuctusSecondOneAny; break; } } else { switch (note->previous->u.note.shape) { case S_QUILISMA: note->gtex_offset_case = TorcResQuilismaAuctusSecondWideAny; break; case S_ORISCUS_ASCENDENS: case S_ORISCUS_DESCENDENS: note->gtex_offset_case = TorcResOriscusAuctusSecondWideAny; break; default: note->gtex_offset_case = TorcResAuctusSecondWideAny; break; } } } h_episema = VPOS_ABOVE; v_episema = VPOS_BELOW; v_episema_below_is_lower = true; break; default: --i; done = false; break; } if (done) { break; } /* else fallthrough to the next case! */ case T_PORRECTUS_FLEXUS: switch (i) { case HEPISEMA_FIRST_TWO: case 1: note->gtex_offset_case = PorrAuctusInitialAny; h_episema = VPOS_ABOVE; v_episema = VPOS_BELOW; v_episema_below_is_lower = true; break; case 2: if (glyph->u.notes.liquescentia & L_DEMINUTUS) { note->gtex_offset_case = AntepenultBeforeDeminutus; } else { note->gtex_offset_case = AntepenultBeforePunctum; } h_episema = above_if_either_h_episema(note); v_episema = VPOS_BELOW; break; case 3: high_low_set_upper(glyph, note); h_episema = VPOS_ABOVE; if ((glyph->u.notes.liquescentia & L_DEMINUTUS) && note->next) { v_episema = below_if_next_ambitus_allows(note); note->gtex_offset_case = PenultBeforeDeminutus; } else { v_episema = VPOS_BELOW; note->gtex_offset_case = note_before_last_note_case(glyph, note); } break; default: high_low_set_lower(glyph, note); note->gtex_offset_case = last_note_case(glyph, FinalPunctum, note, false); h_episema = above_if_h_episema(note->previous); v_episema = VPOS_BELOW; break; } break; case T_TORCULUS_RESUPINUS: done = true; switch (i) { case 1: note->gtex_offset_case = first_note_case(note, glyph); h_episema = above_if_h_episema(note->next); v_episema = VPOS_BELOW; break; case HEPISEMA_FIRST_TWO: case 2: if (glyph->u.notes.liquescentia & (L_AUCTUS_ASCENDENS | L_AUCTUS_DESCENDENS)) { /* auctus */ if (glyph->u.notes.liquescentia & L_INITIO_DEBILIS) { note->gtex_offset_case = TorcResDebilisAuctusSecondAnyAny; } else { if (note->u.note.pitch - note->previous->u.note.pitch == 1) { switch (note->previous->u.note.shape) { case S_QUILISMA: note->gtex_offset_case = TorcResQuilismaAuctusSecondOneAny; break; case S_ORISCUS_ASCENDENS: case S_ORISCUS_DESCENDENS: note->gtex_offset_case = TorcResOriscusAuctusSecondOneAny; break; default: note->gtex_offset_case = TorcResAuctusSecondOneAny; break; } } else { switch (note->previous->u.note.shape) { case S_QUILISMA: note->gtex_offset_case = TorcResQuilismaAuctusSecondWideAny; break; case S_ORISCUS_ASCENDENS: case S_ORISCUS_DESCENDENS: note->gtex_offset_case = TorcResOriscusAuctusSecondWideAny; break; default: note->gtex_offset_case = TorcResAuctusSecondWideAny; break; } } } } else if (note->next->next->u.note.pitch - note->next->u.note.pitch == 1) { /* non-auctus with a second ambitus of 1 */ if (glyph->u.notes.liquescentia & L_INITIO_DEBILIS) { note->gtex_offset_case = TorcResDebilisNonAuctusSecondAnyOne; } else { if (note->u.note.pitch - note->previous->u.note.pitch == 1) { switch (note->previous->u.note.shape) { case S_QUILISMA: note->gtex_offset_case = TorcResQuilismaNonAuctusSecondOneOne; break; case S_ORISCUS_ASCENDENS: case S_ORISCUS_DESCENDENS: note->gtex_offset_case = TorcResOriscusNonAuctusSecondOneOne; break; default: note->gtex_offset_case = TorcResNonAuctusSecondOneOne; break; } } else { switch (note->previous->u.note.shape) { case S_QUILISMA: note->gtex_offset_case = TorcResQuilismaNonAuctusSecondWideOne; break; case S_ORISCUS_ASCENDENS: case S_ORISCUS_DESCENDENS: note->gtex_offset_case = TorcResOriscusNonAuctusSecondWideOne; break; default: note->gtex_offset_case = TorcResNonAuctusSecondWideOne; break; } } } } else { /* non-auctus with a second ambitus of at least 2 */ if (glyph->u.notes.liquescentia & L_INITIO_DEBILIS) { note->gtex_offset_case = TorcResDebilisNonAuctusSecondAnyWide; } else { if (note->u.note.pitch - note->previous->u.note.pitch == 1) { switch (note->previous->u.note.shape) { case S_QUILISMA: note->gtex_offset_case = TorcResQuilismaNonAuctusSecondOneWide; break; case S_ORISCUS_ASCENDENS: case S_ORISCUS_DESCENDENS: note->gtex_offset_case = TorcResOriscusNonAuctusSecondOneWide; break; default: note->gtex_offset_case = TorcResNonAuctusSecondOneWide; break; } } else { switch (note->previous->u.note.shape) { case S_QUILISMA: note->gtex_offset_case = TorcResQuilismaNonAuctusSecondWideWide; break; case S_ORISCUS_ASCENDENS: case S_ORISCUS_DESCENDENS: note->gtex_offset_case = TorcResOriscusNonAuctusSecondWideWide; break; default: note->gtex_offset_case = TorcResNonAuctusSecondWideWide; break; } } } } h_episema = VPOS_ABOVE; v_episema = VPOS_BELOW; v_episema_below_is_lower = true; break; default: --i; done = false; break; } if (done) { break; } /* else fallthrough to the next case! */ case T_PORRECTUS: /* or porrectus-like flexus */ switch (i) { case HEPISEMA_FIRST_TWO: case 1: 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) { /* non-auctus with a second ambitus of 1 */ note->gtex_offset_case = PorrNonAuctusInitialOne; } else { /* non-auctus with a second ambitus of at least 2 */ note->gtex_offset_case = PorrNonAuctusInitialWide; } h_episema = VPOS_ABOVE; v_episema = VPOS_BELOW; v_episema_below_is_lower = true; break; case 2: low_high_set_lower(glyph, note); if (glyph->u.notes.liquescentia & L_DEMINUTUS) { note->gtex_offset_case = PenultBeforeDeminutus; } else if (glyph->u.notes.liquescentia & (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 */ low_high_set_upper(glyph, note); note->gtex_offset_case = last_note_case(glyph, FinalUpperPunctum, note, true); h_episema = VPOS_ABOVE; v_episema = below_if_auctus(glyph); break; } break; case T_SCANDICUS: switch (i) { case 1: note->gtex_offset_case = first_note_case(note, glyph); h_episema = VPOS_BELOW; v_episema = VPOS_BELOW; break; case 2: low_high_set_lower(glyph, note); if (note->u.note.pitch - note->previous->u.note.pitch == 1) { note->gtex_offset_case = FinalPunctum; } else { note->gtex_offset_case = FinalConnectedPunctum; } h_episema = VPOS_BELOW; v_episema = VPOS_BELOW; break; default: low_high_set_upper(glyph, note); note->gtex_offset_case = last_note_case(glyph, FinalPunctum, note, true); h_episema = VPOS_ABOVE; v_episema = VPOS_BELOW; 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: v_episema = VPOS_BELOW; switch (i) { case 1: note->gtex_offset_case = InitialConnectedPunctum; h_episema = above_if_h_episema(note->next); break; case 2: if (note->next->u.note.pitch - note->u.note.pitch == 1) { note->gtex_offset_case = SalicusOriscusOne; } else { note->gtex_offset_case = SalicusOriscusWide; } h_episema = VPOS_ABOVE; break; default: note->gtex_offset_case = last_note_case(glyph, FinalPunctum, note, false); if (glyph->u.notes.liquescentia & L_DEMINUTUS) { v_episema = VPOS_ABOVE; } h_episema = VPOS_ABOVE; break; } break; case T_TORCULUS_LIQUESCENS: done = true; switch (i) { case 1: note->gtex_offset_case = first_note_case(note, glyph); h_episema = above_if_h_episema(note->next); v_episema = VPOS_BELOW; break; case 2: note->gtex_offset_case = note_before_last_note_case_ignoring_deminutus(note); h_episema = VPOS_ABOVE; v_episema = VPOS_BELOW; break; default: --i; done = false; break; } if (done) { break; } /* else fallthrough to the next case! */ case T_ANCUS: switch (i) { case 1: note->gtex_offset_case = first_note_case(note, glyph); h_episema = above_if_h_episema(note->next); v_episema = VPOS_BELOW; break; case 2: note->is_upper_note = true; note->gtex_offset_case = note_before_last_note_case(glyph, note); h_episema = VPOS_ABOVE; v_episema = below_if_next_ambitus_allows(note); break; default: note->is_lower_note = true; note->gtex_offset_case = last_note_case(glyph, FinalPunctum, note, false); h_episema = VPOS_BELOW; v_episema = VPOS_BELOW; break; } break; case T_TORCULUS: switch (i) { case 1: note->gtex_offset_case = first_note_case(note, glyph); h_episema = above_if_h_episema(note->next); v_episema = VPOS_BELOW; break; case 2: high_low_set_upper(glyph, note); note->gtex_offset_case = note_before_last_note_case(glyph, note); h_episema = VPOS_ABOVE; if (glyph->u.notes.liquescentia & L_DEMINUTUS) { v_episema = below_if_next_ambitus_allows(note); } else { v_episema = VPOS_BELOW; } break; default: high_low_set_lower(glyph, note); note->gtex_offset_case = last_note_case(glyph, FinalPunctum, note, false); h_episema = above_if_h_episema(note->previous); v_episema = VPOS_BELOW; break; } break; default: /* case T_ONE_NOTE */ 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: note->gtex_offset_case = FinalInclinatumDeminutus; break; case S_PUNCTUM_INCLINATUM_AUCTUS: case S_PUNCTUM_INCLINATUM_ASCENDENS: case S_PUNCTUM_INCLINATUM_DESCENDENS: note->gtex_offset_case = FinalInclinatum; break; case S_STROPHA: note->gtex_offset_case = FinalStropha; break; case S_QUILISMA: note->gtex_offset_case = fused_single_note_case(glyph, FinalQuilisma, LeadingQuilisma); break; 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_VIRGA: note->gtex_offset_case = InitialVirga; break; case S_VIRGA_REVERSA: if (glyph->u.notes.liquescentia == L_AUCTUS_DESCENDENS) { note->gtex_offset_case = FinalVirgaAuctus; } else { note->gtex_offset_case = InitialVirga; } break; case S_LINEA_PUNCTUM: note->gtex_offset_case = FinalLineaPunctum; break; 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, fused_single_note_case(glyph, FinalPunctum, LeadingPunctum), note, true); break; } break; } if (note->signs & _V_EPISEMA) { if (note->v_episema_height) { if (note->v_episema_height > note->u.note.pitch) { /* above is always higher because of GregorioTeX's design */ note->v_episema_height += (int)VPOS_ABOVE; } } else { note->v_episema_height = note->u.note.pitch + (int)v_episema; if ((v_episema == VPOS_BELOW && v_episema_below_is_lower) || v_episema == VPOS_ABOVE) { /* above is always higher because of GregorioTeX's design */ note->v_episema_height += (int)v_episema; } } } return h_episema; } static __inline char compute_h_episema_height( const gregorio_glyph *const glyph, const gregorio_note *const note, const gregorio_vposition vpos) { char height = note->u.note.pitch; if (note->signs & _V_EPISEMA) { if ((vpos == VPOS_ABOVE && note->v_episema_height >= height) || (vpos == VPOS_BELOW && note->v_episema_height <= height)) { height = note->v_episema_height; } } if (vpos == VPOS_ABOVE && note->choral_sign && !choral_sign_here_is_low(glyph, note, NULL)) { ++height; if (!is_on_a_line(note->u.note.pitch)) { ++height; } } return height + (int)vpos; } static bool is_bridgeable_space(const gregorio_element *const element) { if (element->type == GRE_SPACE) { switch (element->u.misc.unpitched.info.space) { case SP_NEUMATIC_CUT: case SP_LARGER_SPACE: case SP_NEUMATIC_CUT_NB: case SP_LARGER_SPACE_NB: return true; default: /* do nothing */ break; } } return false; } typedef struct height_computation { const gregorio_vposition vpos; const gregorio_sign_orientation orientation; bool (*const is_applicable)(const gregorio_note *); bool (*const is_shown)(const gregorio_note *); bool (*const is_connected)(const gregorio_note *); grehepisema_size (*const get_size)(const gregorio_note *); bool (*const is_better_height)(signed char, signed char); void (*const position)(gregorio_note *, signed char, bool); bool active; char height; bool connected; 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; unsigned short adjustment_index; } height_computation; static bool is_h_episema_above_applicable(const gregorio_note *const note) { return note->h_episema_above || !note->is_lower_note; } static bool is_h_episema_below_applicable(const gregorio_note *const note) { return note->h_episema_below || !note->is_upper_note; } bool gtex_is_h_episema_above_shown(const gregorio_note *const note) { return note->h_episema_above; } bool gtex_is_h_episema_below_shown(const gregorio_note *const note) { return note->h_episema_below; } static bool is_h_episema_above_connected(const gregorio_note *const note) { return note->h_episema_above_connect; } static bool is_h_episema_below_connected(const gregorio_note *const note) { return note->h_episema_below_connect; } static grehepisema_size get_h_episema_above_size( const gregorio_note *const note) { return note->h_episema_above_size; } static grehepisema_size get_h_episema_below_size( const gregorio_note *const note) { return note->h_episema_below_size; } static bool is_h_episema_above_better_height(const signed char new_height, const signed char old_height) { return new_height > old_height; } static bool is_h_episema_below_better_height(const signed char new_height, const signed char old_height) { return new_height < old_height; } static void adjust_h_episema_above_if_better(gregorio_note *const note, signed char potential_height) { if (is_h_episema_above_better_height(potential_height, note->h_episema_above)) { note->h_episema_above = potential_height; } } static void adjust_h_episema_below_if_better(gregorio_note *const note, signed char potential_height) { if (is_h_episema_below_better_height(potential_height, note->h_episema_below)) { note->h_episema_below = potential_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) { h->start_element = element; h->start_glyph = glyph; h->start_note = note; h->active = true; 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) { const unsigned short adjustment_index = note->he_adjustment_index[h->orientation]; h->position(note, h->height, connect); if (adjustment_index) { gregorio_hepisema_adjustment *adj = gregorio_get_hepisema_adjustment( adjustment_index); if (adj->pitch_extremum == NO_PITCH || h->is_better_height(h->height, adj->pitch_extremum)) { adj->pitch_extremum = h->height; } } if (!note->high_ledger_specificity && !note->high_ledger_line) { note->high_ledger_line = high_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_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 || note->high_ledger_line; *low_ledger_line = *low_ledger_line || note->low_ledger_line; 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_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 || note->high_ledger_line; *low_ledger_line = *low_ledger_line || note->low_ledger_line; 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, const gregorio_score *const score) { gregorio_note *last_note = NULL; const gregorio_element *element = h->start_element; 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); previous_has_ledger_line(h, &high_ledger_line, &low_ledger_line); for ( ; element; element = element->next) { if (element->type == GRE_ELEMENT) { for (glyph = glyph? glyph : element->u.first_glyph; glyph; glyph = glyph->next) { if (glyph && glyph->type == GRE_GLYPH) { for (note = note? note : glyph->u.notes.first_note; note; note = note->next) { if (end && note == end) { if (last_note) { position_h_episema(last_note, h, false, high_ledger_line, low_ledger_line); } return; } if (h->is_applicable(note)) { position_h_episema(note, h, true, high_ledger_line, low_ledger_line); last_note = note; } } } } } else if (!is_bridgeable_space(element)) { break; } } if (last_note) { position_h_episema(last_note, h, false, high_ledger_line, low_ledger_line); } } static __inline bool is_connected_left(const grehepisema_size size) { return size == H_NORMAL || size == H_SMALL_LEFT; } static __inline bool is_connected_right(const grehepisema_size size) { return size == H_NORMAL || size == H_SMALL_RIGHT; } static __inline bool is_connectable_interglyph_ambitus(const signed char first, const signed char second) { return abs(first - second) < 3; } static __inline bool is_connectable_interglyph_notes( const gregorio_note *const first, const gregorio_note *const second) { return first && second && is_connectable_interglyph_ambitus( first->u.note.pitch, second->u.note.pitch); } static __inline bool has_space_to_left(const gregorio_note *const note) { switch (note->u.note.shape) { case S_PUNCTUM_INCLINATUM_ASCENDENS: case S_PUNCTUM_INCLINATUM_DESCENDENS: case S_PUNCTUM_INCLINATUM_DEMINUTUS: case S_PUNCTUM_INCLINATUM_AUCTUS: return !is_connectable_interglyph_notes(note->previous, note); default: return !note->previous; } } static __inline void end_h_episema(height_computation *const h, 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))) { 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; } } } } } if (end && end->type == GRE_NOTE && is_connected_left(h->get_size(end)) && !(end->u.note.liquescentia & L_DEMINUTUS) && 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, score); h->active = false; h->height = 0; h->connected = false; 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; } } 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 gregorio_score *const score) { grehepisema_size size; if (h->is_applicable(note)) { if (h->is_shown(note)) { size = h->get_size(note); if (h->active) { const signed char next_height = compute_h_episema_height( glyph, note, h->vpos); if (h->connected && is_connected_left(size) && (i != 1 || is_connectable_interglyph_ambitus( next_height, h->height))) { if (h->is_better_height(next_height, h->height)) { h->height = next_height; } } else { end_h_episema(h, note, score); start_h_episema(h, element, glyph, note); } } else { start_h_episema(h, element, glyph, note); } 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, score); } } else if (!h->is_shown(note)) { /* special handling for porrectus shapes because of their shape: * the lower note of the porrectus stroke is normally not applicable, * but we have to end the episema on the upper note if the episema * on the lower note is not shown. */ switch(type) { case T_PORRECTUS: case T_PORRECTUS_FLEXUS: if (i == 2) { end_h_episema(h, note, score); } break; case T_TORCULUS_RESUPINUS: case T_TORCULUS_RESUPINUS_FLEXUS: if (i == 3) { end_h_episema(h, note, score); } break; default: /* do nothing */ break; } } } 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 gregorio_score *const score) { gregorio_vposition default_vpos = advise_positioning(glyph, note, i, type); if (note->h_episema_above == HEPISEMA_AUTO && note->h_episema_below == HEPISEMA_AUTO) { if (default_vpos == VPOS_BELOW) { note->h_episema_above = HEPISEMA_NONE; } else { /* default_vpos == VPOS_ABOVE */ note->h_episema_below = HEPISEMA_NONE; } } 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_FLAT: case S_SHARP: case S_NATURAL: /* if this glyph starts with one of these, it's not fusible */ return 0; default: /* anything else is potentially fusible */ break; } switch (previous->u.notes.glyph_type) { case G_PUNCTUM: case G_FLEXA: case G_VIRGA_REVERSA: /* these are potentially fusible to this note */ break; default: /* everything else is not fusible */ return 0; } prev_note = gregorio_glyph_last_note(previous); gregorio_assert(prev_note->type == GRE_NOTE, compute_fused_shift, "previous note wasn't a note", return 0); shift = first_note->u.note.pitch - prev_note->u.note.pitch; gregorio_assert(shift >= -MAX_AMBITUS && shift <= MAX_AMBITUS, compute_fused_shift, "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; } if (shift < 0) { if (glyph->u.notes.glyph_type == G_PODATUS) { if (glyph->u.notes.liquescentia & L_DEMINUTUS) { /* podatus deminutus may be fused from above */ return shift; } switch (first_note->u.note.shape) { case S_ORISCUS_ASCENDENS: case S_ORISCUS_DESCENDENS: case S_ORISCUS_SCAPUS_ASCENDENS: case S_ORISCUS_SCAPUS_DESCENDENS: return shift; default: /* otherwise, podatus may not be fused from above */ return 0; } } /* the FLEXA check below checks for a porrectus-like flexus, which is * not fusible from above */ if ((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; } } return shift; } void gregoriotex_compute_positioning( const gregorio_element *const param_element, const gregorio_score *const score) { height_computation above = { /*.vpos =*/ VPOS_ABOVE, /*.orientation =*/ SO_OVER, /*.is_applicable =*/ &is_h_episema_above_applicable, /*.is_shown =*/ >ex_is_h_episema_above_shown, /*.is_connected =*/ &is_h_episema_above_connected, /*.get_size =*/ &get_h_episema_above_size, /*.is_better_height =*/ &is_h_episema_above_better_height, /*.position =*/ &gregorio_position_h_episema_above, /*.active =*/ false, /*.height =*/ 0, /*.connected =*/ false, /*.start_element =*/ NULL, /*.start_glyph =*/ NULL, /*.start_note =*/ NULL, /*.last_connected_element =*/ NULL, /*.last_connected_glyph =*/ NULL, /*.last_connected_note =*/ NULL, /*.adjustment_index =*/ 0, }; height_computation below = { /*.vpos =*/ VPOS_BELOW, /*.orientation =*/ SO_UNDER, /*.is_applicable =*/ &is_h_episema_below_applicable, /*.is_shown =*/ >ex_is_h_episema_below_shown, /*.is_connected =*/ &is_h_episema_below_connected, /*.get_size =*/ &get_h_episema_below_size, /*.is_better_height =*/ &is_h_episema_below_better_height, /*.position =*/ &gregorio_position_h_episema_below, /*.active =*/ false, /*.height =*/ 0, /*.connected =*/ false, /*.start_element =*/ NULL, /*.start_glyph =*/ NULL, /*.start_note =*/ NULL, /*.last_connected_element =*/ NULL, /*.last_connected_glyph =*/ NULL, /*.last_connected_note =*/ NULL, /*.adjustment_index =*/ 0, }; int i; gtex_alignment ignored; gtex_type type; const gregorio_element *element; for (element = param_element; 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; glyph->u.notes.fuse_to_next_glyph = compute_fused_shift( gregorio_next_non_texverb_glyph(glyph)); i = 0; gregoriotex_determine_glyph_name(glyph, &ignored, &type); for (note = glyph->u.notes.first_note; note; note = note->next) { if (note->type == GRE_NOTE) { compute_note_positioning(&above, &below, element, glyph, note, ++i, type, score); } } } } } else if (!is_bridgeable_space(element)) { end_h_episema(&above, NULL, score); end_h_episema(&below, NULL, score); } } end_h_episema(&above, NULL, score); end_h_episema(&below, NULL, score); } static __inline void adjust_hepisema(gregorio_note *const note, const gregorio_sign_orientation orientation, bool (*const is_shown)(const gregorio_note *), void (*const adjust_if_better)(gregorio_note *, signed char)) { const unsigned short adjustment_index = note->he_adjustment_index[orientation]; if (adjustment_index && is_shown(note)) { gregorio_hepisema_adjustment *adj = gregorio_get_hepisema_adjustment( adjustment_index); adjust_if_better(note, adj->pitch_extremum); } } void gregoriotex_compute_cross_syllable_positioning( const gregorio_score *const score) { gregorio_syllable *syllable; for (syllable = score->first_syllable; syllable; syllable = syllable->next_syllable) { int voice; for (voice = 0; voice < score->number_of_voices; ++voice) { gregorio_element *element; for (element = syllable->elements[voice]; 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) { adjust_hepisema(note, SO_OVER, gtex_is_h_episema_above_shown, adjust_h_episema_above_if_better); adjust_hepisema(note, SO_UNDER, gtex_is_h_episema_below_shown, adjust_h_episema_below_if_better); } } } } } } } } }