diff options
author | Karl Berry <karl@freefriends.org> | 2021-02-25 19:22:25 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2021-02-25 19:22:25 +0000 |
commit | ad547a6b5986815fda458221149728d9d9ab1d87 (patch) | |
tree | 16296910eb3eca724371474ea9aea3994dc69614 /Build/source/texk/gregorio/gregorio-src/src/gabc/gabc-glyphs-determination.c | |
parent | 947b43de3dd21d58ccc2ffadefc4441ea1c2a813 (diff) |
restore Build,TODO from r57911
git-svn-id: svn://tug.org/texlive/trunk@57915 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/texk/gregorio/gregorio-src/src/gabc/gabc-glyphs-determination.c')
-rw-r--r-- | Build/source/texk/gregorio/gregorio-src/src/gabc/gabc-glyphs-determination.c | 1303 |
1 files changed, 1303 insertions, 0 deletions
diff --git a/Build/source/texk/gregorio/gregorio-src/src/gabc/gabc-glyphs-determination.c b/Build/source/texk/gregorio/gregorio-src/src/gabc/gabc-glyphs-determination.c new file mode 100644 index 00000000000..094b79291fa --- /dev/null +++ b/Build/source/texk/gregorio/gregorio-src/src/gabc/gabc-glyphs-determination.c @@ -0,0 +1,1303 @@ +/* + * Gregorio is a program that translates gabc files to GregorioTeX + * This file provides functions for determining glyphs from notes. + * + * Copyright (C) 2006-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 <http://www.gnu.org/licenses/>. + */ + +#include "config.h" +#include <stdio.h> +#include <assert.h> +#include "bool.h" +#include "struct.h" +#include "messages.h" + +#include "gabc.h" + +static __inline gregorio_scanner_location *copy_note_location( + const gregorio_note *const note, gregorio_scanner_location *const loc) +{ + loc->first_line = note->src_line; + loc->first_column = note->src_column; + loc->first_offset = note->src_offset; + loc->last_line = note->src_line; + loc->last_column = note->src_column; + loc->last_offset = note->src_offset; + return loc; +} + +static __inline bool is_normal_punctum(const gregorio_note *const note) +{ + return note->u.note.shape == S_PUNCTUM + && note->u.note.liquescentia != L_INITIO_DEBILIS; +} + +static __inline bool is_punctum_inclinatum(const gregorio_shape shape, + const bool determined_only) +{ + switch (shape) { + case S_PUNCTUM_INCLINATUM_ASCENDENS: + case S_PUNCTUM_INCLINATUM_STANS: + case S_PUNCTUM_INCLINATUM_DESCENDENS: + return true; + case S_PUNCTUM_INCLINATUM_UNDETERMINED: + return !determined_only; + default: + return false; + } +} + +/**************************** + * + * This function is the basis of all the determination of glyphs. The + * phylosophy of the function is to say : We have a glyph that we have + * determined, and we have the following note, can we "add" it to the + * glyph, or will it be the first note of another glyph ? + * + * current_glyph_type is the type of the current_glyph (current_glyph + * as meant in gabc_det_glyphs_from_notes). curent_pitch + * is the height of the note that we want to "add" to the glyph. + * last_pitch is the height of the last note of current_glyph. shape + * is the shape of the note we want to "add" to the glyph. + * + * The function returns a char, which meaning is explained below. + * + * end_of_glyph is a pointer to the result of the determination, here + * are the meanings : + * + * DET_NO_END: we have successfully added the note to the glyph, and + * we return the new type of the glyph. We may again add notes to the + * glyph. + * + * DET_END_OF_PREVIOUS: we have not been able to add the note to the + * glyph, so we will have to close the glyph. + * + * DET_END_OF_CURRENT: we have been able to add the note to the glyph, + * but we won't be able to add more notes to the glyph, we can close + * it. The new type is returned. + * + * DET_END_OF_BOTH: we haven't been able to add the note to the glyph, + * and we won't be able to add notes to the new glyph. This special + * case is quite rare, we use it for trivirga, tristropha, etc. + * + * When we encouter a S_QUADRATUM (or S_QUILISMA_QUADRATUM), we build + * a new glyph with the (temporary) shape G_PES_QUADRATUM_FIRST_PART + * (or G_PES_QUILISMA_QUADRATUM_FIRST_PART), and we wait for the next + * note. + * + ****************************/ + +static char add_note_to_a_glyph(gregorio_glyph_type current_glyph_type, + char current_pitch, char last_pitch, gregorio_shape shape, + gregorio_liquescentia liquescentia, + gregorio_note *current_glyph_first_note, + gabc_determination *end_of_glyph, + gregorio_shape *punctum_inclinatum_orientation) +{ + #define this_note_starts_new_glyph { \ + next_glyph_type = G_PUNCTUM; \ + *end_of_glyph = DET_END_OF_PREVIOUS; \ + } + + /* next glyph type is the type of the glyph that will be returned (the + * new type of the glyph with the note added to it, or the type of the + * new glyph with the note alone. */ + gregorio_glyph_type next_glyph_type = G_UNDETERMINED; + + *end_of_glyph = DET_NO_END; + + /* here we separate notes that would be logically in the same glyph + * but that are too far to be so */ + if (last_pitch) { + if (current_pitch - last_pitch > MAX_AMBITUS + || current_pitch - last_pitch < -MAX_AMBITUS) { + current_glyph_type = G_UNDETERMINED; + } + } + + switch (shape) { + case S_LINEA_PUNCTUM: + case S_LINEA: + next_glyph_type = G_PUNCTUM; + *end_of_glyph = DET_END_OF_BOTH; + break; + case S_FLAT: + case S_SHARP: + case S_NATURAL: + next_glyph_type = G_ALTERATION; + *end_of_glyph = DET_END_OF_BOTH; + break; + case S_PUNCTUM: + /* + * we determine here the shape of the thing if it is made of puncta + */ + if (current_pitch == last_pitch) { + this_note_starts_new_glyph; + break; + } + switch (current_glyph_type) { + case G_PUNCTUM: + if (current_pitch > last_pitch) { + next_glyph_type = G_PODATUS; + } else { + next_glyph_type = G_FLEXA; + } + break; + case G_PODATUS: + if (current_pitch > last_pitch) { + if (is_normal_punctum(current_glyph_first_note)) { + next_glyph_type = G_SCANDICUS; + *end_of_glyph = DET_END_OF_CURRENT; + } else { + this_note_starts_new_glyph; + } + } else { + next_glyph_type = G_TORCULUS; + } + break; + case G_PES_QUADRATUM_FIRST_PART: + if (current_pitch > last_pitch) { + next_glyph_type = G_PES_QUADRATUM; + *end_of_glyph = DET_END_OF_CURRENT; + } else { + next_glyph_type = G_FLEXA; + } + break; + case G_PES_QUILISMA_QUADRATUM_FIRST_PART: + if (current_pitch > last_pitch) { + next_glyph_type = G_PES_QUADRATUM; + *end_of_glyph = DET_END_OF_CURRENT; + } else { + this_note_starts_new_glyph; + } + break; + case G_PES_ASCENDENS_ORISCUS: + case G_PES_DESCENDENS_ORISCUS: + if (current_pitch > last_pitch) { + next_glyph_type = G_SALICUS; + } else { + this_note_starts_new_glyph; + } + break; + case G_SALICUS: + if (current_pitch < last_pitch) { + next_glyph_type = G_SALICUS_FLEXUS; + *end_of_glyph = DET_END_OF_CURRENT; + } else { + this_note_starts_new_glyph; + } + break; + case G_FLEXA: + if (current_pitch > last_pitch) { + if (is_normal_punctum(current_glyph_first_note)) { + next_glyph_type = G_PORRECTUS; + } else { + this_note_starts_new_glyph; + } + } else { + if (liquescentia & L_DEMINUTUS) { + *end_of_glyph = DET_END_OF_CURRENT; + next_glyph_type = G_ANCUS; + } else { + this_note_starts_new_glyph; + } + } + break; + case G_TORCULUS: + if (current_pitch > last_pitch) { + next_glyph_type = G_TORCULUS_RESUPINUS; + } else if (liquescentia == L_DEMINUTUS) { + *end_of_glyph = DET_END_OF_CURRENT; + next_glyph_type = G_TORCULUS_LIQUESCENS; + } else { + this_note_starts_new_glyph; + } + break; + case G_TORCULUS_RESUPINUS: + if (current_pitch > last_pitch) { + this_note_starts_new_glyph; + } else { + *end_of_glyph = DET_END_OF_CURRENT; + next_glyph_type = G_TORCULUS_RESUPINUS_FLEXUS; + } + break; + case G_PORRECTUS: + if (current_pitch > last_pitch) { + this_note_starts_new_glyph; + } else { + *end_of_glyph = DET_END_OF_CURRENT; + next_glyph_type = G_PORRECTUS_FLEXUS; + } + break; + default: + this_note_starts_new_glyph; + break; + } + break; + case S_ORISCUS_UNDETERMINED: + case S_ORISCUS_ASCENDENS: + case S_ORISCUS_DESCENDENS: + case S_ORISCUS_DEMINUTUS: + case S_QUILISMA: + this_note_starts_new_glyph; + break; + case S_VIRGA: + if (current_glyph_type == G_VIRGA && last_pitch == current_pitch) { + next_glyph_type = G_BIVIRGA; + } else { + if (current_glyph_type == G_BIVIRGA && last_pitch == current_pitch) { + next_glyph_type = G_TRIVIRGA; + } else { + *end_of_glyph = DET_END_OF_PREVIOUS; + next_glyph_type = G_VIRGA; + } + } + break; + case S_VIRGA_REVERSA: + *end_of_glyph = DET_END_OF_PREVIOUS; + next_glyph_type = G_VIRGA_REVERSA; + break; + case S_BIVIRGA: + if (current_glyph_type == G_VIRGA && last_pitch == current_pitch) { + *end_of_glyph = DET_END_OF_CURRENT; + next_glyph_type = G_TRIVIRGA; + } else { + *end_of_glyph = DET_END_OF_PREVIOUS; + next_glyph_type = G_BIVIRGA; + } + break; + case S_TRIVIRGA: + *end_of_glyph = DET_END_OF_BOTH; + next_glyph_type = G_TRIVIRGA; + break; + case S_QUADRATUM: + *end_of_glyph = DET_END_OF_PREVIOUS; + next_glyph_type = G_PES_QUADRATUM_FIRST_PART; + break; + case S_QUILISMA_QUADRATUM: + *end_of_glyph = DET_END_OF_PREVIOUS; + next_glyph_type = G_PES_QUILISMA_QUADRATUM_FIRST_PART; + break; + case S_ORISCUS_SCAPUS_UNDETERMINED: + case S_ORISCUS_SCAPUS_ASCENDENS: + if (current_glyph_type == G_PUNCTUM && last_pitch < current_pitch) { + next_glyph_type = G_PES_ASCENDENS_ORISCUS; + /* Therefore G_PES_ASCENDENS_ORISCUS might be fronting an + * undetermined oriscus scapus. This will be resolved during + * oriscus orientation determination */ + } else { + this_note_starts_new_glyph; + } + break; + case S_ORISCUS_SCAPUS_DESCENDENS: + if (current_glyph_type == G_PUNCTUM && last_pitch < current_pitch) { + next_glyph_type = G_PES_DESCENDENS_ORISCUS; + } else { + this_note_starts_new_glyph; + } + break; + case S_PUNCTUM_INCLINATUM_UNDETERMINED: + case S_PUNCTUM_INCLINATUM_ASCENDENS: + case S_PUNCTUM_INCLINATUM_STANS: + case S_PUNCTUM_INCLINATUM_DESCENDENS: + /* + * Warning : this part of the code is specific to the + * declarations of the header file + */ + if (current_glyph_type > G_PUNCTA_INCLINATA) { + /* + * meaning that the previous glyph is not a combination of puncta + * inclinata, see header file for details + */ + *end_of_glyph = DET_END_OF_PREVIOUS; + next_glyph_type = G_PUNCTUM_INCLINATUM; + break; + } + if (current_pitch == last_pitch) { + *end_of_glyph = DET_END_OF_PREVIOUS; + next_glyph_type = G_PUNCTUM_INCLINATUM; + break; + } + if (is_punctum_inclinatum(shape, true) + && *punctum_inclinatum_orientation != shape + && *punctum_inclinatum_orientation + != S_PUNCTUM_INCLINATUM_UNDETERMINED) { + /* change of orientation ends the glyph */ + *end_of_glyph = DET_END_OF_PREVIOUS; + next_glyph_type = G_PUNCTUM_INCLINATUM; + break; + } + switch (current_glyph_type) { + case G_PUNCTUM_INCLINATUM: + if (last_pitch < current_pitch) { + next_glyph_type = G_2_PUNCTA_INCLINATA_ASCENDENS; + } else { + next_glyph_type = G_2_PUNCTA_INCLINATA_DESCENDENS; + } + break; + case G_2_PUNCTA_INCLINATA_ASCENDENS: + if (last_pitch < current_pitch) { + next_glyph_type = G_3_PUNCTA_INCLINATA_ASCENDENS; + } else { + next_glyph_type = G_PUNCTA_INCLINATA; + } + break; + case G_3_PUNCTA_INCLINATA_ASCENDENS: + if (last_pitch < current_pitch) { + next_glyph_type = G_4_PUNCTA_INCLINATA_ASCENDENS; + } else { + next_glyph_type = G_PUNCTA_INCLINATA; + } + break; + case G_4_PUNCTA_INCLINATA_ASCENDENS: + if (last_pitch < current_pitch) { + next_glyph_type = G_5_PUNCTA_INCLINATA_ASCENDENS; + } else { + next_glyph_type = G_PUNCTA_INCLINATA; + } + break; + case G_2_PUNCTA_INCLINATA_DESCENDENS: + if (last_pitch < current_pitch) { + next_glyph_type = G_PUNCTA_INCLINATA; + } else { + next_glyph_type = G_3_PUNCTA_INCLINATA_DESCENDENS; + } + break; + case G_3_PUNCTA_INCLINATA_DESCENDENS: + if (last_pitch < current_pitch) { + next_glyph_type = G_PUNCTA_INCLINATA; + } else { + next_glyph_type = G_4_PUNCTA_INCLINATA_DESCENDENS; + } + break; + case G_4_PUNCTA_INCLINATA_DESCENDENS: + if (last_pitch < current_pitch) { + next_glyph_type = G_PUNCTA_INCLINATA; + } else { + next_glyph_type = G_5_PUNCTA_INCLINATA_DESCENDENS; + } + break; + default: + next_glyph_type = G_PUNCTA_INCLINATA; + break; + } + break; + case S_STROPHA: + if (last_pitch != current_pitch) { + *end_of_glyph = DET_END_OF_PREVIOUS; + next_glyph_type = G_STROPHA; + break; + } + switch (current_glyph_type) { + case G_STROPHA: + next_glyph_type = G_DISTROPHA; + break; + case G_DISTROPHA: + *end_of_glyph = DET_END_OF_CURRENT; + next_glyph_type = G_TRISTROPHA; + break; + default: + *end_of_glyph = DET_END_OF_PREVIOUS; + next_glyph_type = G_STROPHA; + break; + } + break; + case S_DISTROPHA: + if (last_pitch == current_pitch && current_glyph_type == G_STROPHA) { + *end_of_glyph = DET_END_OF_CURRENT; + next_glyph_type = G_TRISTROPHA; + } else { + *end_of_glyph = DET_END_OF_PREVIOUS; + next_glyph_type = G_DISTROPHA; + } + break; + case S_TRISTROPHA: + *end_of_glyph = DET_END_OF_BOTH; + next_glyph_type = G_TRISTROPHA; + break; + default: + /* not reachable unless there's a programming error */ + /* LCOV_EXCL_START */ + gregorio_fail2(add_note_to_a_glyph, "unexpected shape: %s", + gregorio_shape_to_string(shape)); + break; + /* LCOV_EXCL_STOP */ + } + /* end of the main switch */ + + if (current_glyph_type == G_UNDETERMINED) { + /* + * means that this is the first glyph or that the previous glyph has + * already been added + */ + if (*end_of_glyph == DET_END_OF_PREVIOUS) { + *end_of_glyph = DET_NO_END; + } else { + if (*end_of_glyph == DET_END_OF_BOTH) { + *end_of_glyph = DET_END_OF_CURRENT; + } + } + } + + /* + * WARNING : Ugly section of the code, just some kind of patch for it to work + * with fonts that can't handle large intervals. + */ + /* here we separate notes that would be logically in the same glyph + * but that are too far to be so, we already said to the function that + * it was not the same glyph, there we must say that the previous + * glyph has ended. */ + if (last_pitch) { + if (current_pitch - last_pitch > MAX_AMBITUS + || current_pitch - last_pitch < -MAX_AMBITUS) { + if (*end_of_glyph == DET_END_OF_CURRENT + || *end_of_glyph == DET_END_OF_BOTH) { + /* There is no current way for the code to end up here, but + * we'll leave it in because it's a good safety precaution in + * case something new is added in the future */ + /* LCOV_EXCL_START */ + gregorio_message(_("Encountered the need to switch " + "DET_END_OF_CURRENT to DET_END_OF_BOTH because of " + "overly large ambitus"), + "add_note_to_a_glyph", VERBOSITY_WARNING, + __LINE__); + *end_of_glyph = DET_END_OF_BOTH; + } else { + /* LCOV_EXCL_STOP */ + *end_of_glyph = DET_END_OF_PREVIOUS; + } + } + } + + switch (shape) { + case S_PUNCTUM_INCLINATUM_ASCENDENS: + case S_PUNCTUM_INCLINATUM_STANS: + case S_PUNCTUM_INCLINATUM_DESCENDENS: + *punctum_inclinatum_orientation = shape; + break; + case S_PUNCTUM_INCLINATUM_UNDETERMINED: + break; + default: + *punctum_inclinatum_orientation = S_PUNCTUM_INCLINATUM_UNDETERMINED; + break; + } + + return next_glyph_type; +} + +static bool is_cavum(gregorio_note *note, gregorio_note *last_note) +{ + bool is_cavum = false; + for (; note; note = note->next) { + if (note->type == GRE_NOTE) { + is_cavum = is_cavum || note->u.note.is_cavum; + } + /* don't look past the last note */ + if (note == last_note) { + break; + } + } + return is_cavum; +} + +/**************************** + * + * First see the comments of + * gabc_det_glyphs_from_notes. This function is used when + * we have finished to determine a glyph. We have the last glyph that + * have been added: last_glyph. The glyph we want to add is given by + * glyph_type and liquescentia. + * + * The glyph we want to add goes from first_note to current_note, we + * isolate these notes from the notes that won't be in the glyph, and + * we add the glyph to the list_of_glyphs. + * +****************************/ + +static gregorio_note *close_normal_glyph(gregorio_glyph **last_glyph, + gregorio_glyph_type glyph_type, gregorio_note **first_note, + gregorio_liquescentia liquescentia, gregorio_note *current_note) +{ + gregorio_note *new_current_note = current_note; + gregorio_scanner_location loc; + + /* patch to have good glyph type in the case where a glyph ends by a note + * with shape S_QUADRATUM */ + if (glyph_type == G_PES_QUADRATUM_FIRST_PART + || glyph_type == G_PES_QUILISMA_QUADRATUM_FIRST_PART) { + glyph_type = G_PUNCTUM; + } + + gregorio_add_glyph(last_glyph, glyph_type, *first_note, liquescentia, + is_cavum(*first_note, current_note)); + + if (current_note->next) { + current_note->next->previous = NULL; + *first_note = current_note->next; + current_note->next = NULL; + } + /* here we "patch" the structure for bivirga, tristropha, etc. */ + /* the idea is not to have a S_BIVIRGA in the shape of the note (which is + * dirty) but rather a G_BIVIRGA in the glyph (which is the case now) and + * two virgas */ + + if (glyph_type == G_BIVIRGA || glyph_type == G_DISTROPHA + || glyph_type == G_TRIVIRGA || glyph_type == G_TRISTROPHA + || glyph_type == G_DISTROPHA_AUCTA + || glyph_type == G_TRISTROPHA_AUCTA) { + gregorio_go_to_first_note(¤t_note); + while (current_note) { + /* a variable necessary for the patch for G_BIVIRGA & co. */ + gregorio_note *added_notes = NULL; + gregorio_note *next_note = NULL; + if (current_note->type == GRE_NOTE) { + switch (current_note->u.note.shape) { + case S_TRIVIRGA: + gregorio_add_note(&added_notes, current_note->u.note.pitch, + S_VIRGA, _NO_SIGN, L_NO_LIQUESCENTIA, current_note, + copy_note_location(current_note, &loc)); + /* fall through */ + case S_BIVIRGA: + gregorio_add_note(&added_notes, current_note->u.note.pitch, + S_VIRGA, _NO_SIGN, L_NO_LIQUESCENTIA, current_note, + copy_note_location(current_note, &loc)); + gregorio_add_note(&added_notes, current_note->u.note.pitch, + S_VIRGA, current_note->signs, + current_note->u.note.liquescentia, current_note, + copy_note_location(current_note, &loc)); + break; + case S_TRISTROPHA: + gregorio_add_note(&added_notes, current_note->u.note.pitch, + S_STROPHA, _NO_SIGN, L_NO_LIQUESCENTIA, + current_note, + copy_note_location(current_note, &loc)); + /* fall through */ + case S_DISTROPHA: + gregorio_add_note(&added_notes, current_note->u.note.pitch, + S_STROPHA, _NO_SIGN, L_NO_LIQUESCENTIA, + current_note, + copy_note_location(current_note, &loc)); + gregorio_add_note(&added_notes, current_note->u.note.pitch, + S_STROPHA, current_note->signs, + current_note->u.note.liquescentia, current_note, + copy_note_location(current_note, &loc)); + break; + default: + break; + } + } + if (!added_notes) { + /* nothing added, but we must go on in case it's + * virga+bivirga = trivirga or stropa+bistropha = tristropha */ + if (current_note->next) { + current_note = current_note->next; + continue; + } else { + break; + } + } + next_note = current_note->next; + /* now we have what we want, we set up the links and free the old + * note */ + if (next_note) { + current_note->next->previous = added_notes; + added_notes->next = next_note; + } + if (current_note == new_current_note) { + new_current_note = added_notes; + } + gregorio_go_to_first_note(&added_notes); + if (current_note->previous) { + current_note->previous->next = added_notes; + added_notes->previous = current_note->previous; + } + /* Detaching current_note is not strictly necessary here because we + * are effectively plucking out added_notes into its own glyph; + * however, detaching the note is safer if this behavior changes in + * the future because gregorio_free_one_note nullifies surrounding + * pointers */ + current_note->next = NULL; + current_note->previous = NULL; + gregorio_free_one_note(¤t_note); + if (!next_note) { + current_note = added_notes; + break; + } + current_note = next_note; + } + gregorio_go_to_first_note(¤t_note); + /* finally we set the just added glyph first_note to current_note */ + (*last_glyph)->u.notes.first_note = current_note; + } + return new_current_note; +} + +static gregorio_note *close_fused_glyph(gregorio_glyph **last_glyph, + gregorio_glyph_type glyph_type, gregorio_note **first_note, + gregorio_liquescentia liquescentia, gregorio_note *current_note) +{ + (*first_note)->u.note.liquescentia |= (liquescentia & L_FUSED); + return close_normal_glyph(last_glyph, glyph_type, first_note, liquescentia, + current_note); +} + +static gregorio_note *next_non_texverb_note(gregorio_note *first_note, + gregorio_note *last_note) +{ + gregorio_not_null(first_note, gregorio_note, return NULL); + + if (first_note == last_note) { + /* not reachable unless there's a programming error */ + /* LCOV_EXCL_START */ + gregorio_assert_only(first_note->type != GRE_TEXVERB_GLYPH, + next_non_texverb_note, "Unexpected texverb at start of iteration"); + return first_note; + /* LCOV_EXCL_STOP */ + } + + for (first_note = first_note->next; first_note && first_note != last_note; + first_note = first_note->next) { + if (first_note->type != GRE_TEXVERB_GLYPH) { + return first_note; + } + } + + gregorio_assert_only(!first_note || first_note->type != GRE_TEXVERB_GLYPH, + next_non_texverb_note, "Unexpected texverb at end of iteration"); + + return last_note; +} + +static void add_intervening_texverbs(gregorio_glyph **last_glyph, + gregorio_note *first_note, gregorio_note *last_note) +{ + for ( ; first_note && first_note->type == GRE_TEXVERB_GLYPH; ) { + bool on_last_note = (first_note == last_note); + + gregorio_add_unpitched_element_as_glyph(last_glyph, first_note->type, + &(first_note->u.other), _NO_SIGN, first_note->texverb); + first_note->texverb = 0; + gregorio_free_one_note(&first_note); + + if (on_last_note) { + break; + } + } +} + +static gregorio_note *close_fusion_glyph(gregorio_glyph **last_glyph, + gregorio_note **first_note, gregorio_liquescentia liquescentia, + gregorio_note *real_last_note, + gregorio_shape *const punctum_inclinatum_orientation) +{ + bool first = true; + gregorio_note *last_note, *texverb_tail; + gregorio_note *next; + int prev_shift = 0, shift, shift2; + gregorio_note *result; + + gregorio_assert((*first_note)->type != GRE_TEXVERB_GLYPH, + close_fusion_glyph, "Unexpected texverb at start of fusion", + return real_last_note); + + for (last_note = real_last_note; + last_note != *first_note && last_note->type == GRE_TEXVERB_GLYPH; + last_note = last_note->previous) { + /* skip over the trailing texverbs */ + } + texverb_tail = (last_note == real_last_note)? NULL : last_note->next; + + for (;;) { + bool processed = false; + + /* fusion must work through glyph-level texverbs */ + if ((*first_note)->type == GRE_TEXVERB_GLYPH) { + gregorio_add_unpitched_element_as_glyph(last_glyph, + (*first_note)->type, &((*first_note)->u.other), _NO_SIGN, + (*first_note)->texverb); + (*first_note)->texverb = 0; + gregorio_assert(*first_note != last_note, + close_fusion_glyph, "Unexpected texverb at end of fusion", + return last_note); + gregorio_free_one_note(first_note); + } + + if (*first_note == last_note) { + close_fused_glyph(last_glyph, G_PUNCTUM, first_note, + liquescentia & ~TAIL_LIQUESCENTIA_MASK, last_note); + return last_note; + } + + gregorio_assert((*first_note)->next, close_fusion_glyph, + "Unexpected single note during fusion", return last_note); + + next = next_non_texverb_note(*first_note, last_note); + + shift = next->u.note.pitch - (*first_note)->u.note.pitch; + if (shift != 0 && next == last_note) { + /* there are exactly two notes left, so we end fusion */ + add_intervening_texverbs(last_glyph, (*first_note)->next, next); + result = close_fused_glyph(last_glyph, + shift < 0? G_FLEXA : G_PODATUS, first_note, + liquescentia, last_note); + if (texverb_tail) { + add_intervening_texverbs(last_glyph, texverb_tail, + real_last_note); + } + return result; + } + if (prev_shift >= 0 && shift < 0) { + /* check for a porrectus-like flexus */ + gregorio_note *next_next = next_non_texverb_note(next, last_note); + gregorio_assert(next_next, close_fusion_glyph, + "Unexpected end of notes during fusion", return last_note); + shift2 = next_next->u.note.pitch - next->u.note.pitch; + if (shift2 > 0) { + if (next_next == last_note) { + /* there are exactly three notes left in a porrectus shape, + * so we end fusion */ + add_intervening_texverbs(last_glyph, (*first_note)->next, + next); + add_intervening_texverbs(last_glyph, next->next, next_next); + result = close_fused_glyph(last_glyph, + G_PORRECTUS, first_note, + liquescentia, last_note); + add_intervening_texverbs(last_glyph, texverb_tail, + real_last_note); + return result; + } + /* found a porrectus-like flexus */ + add_intervening_texverbs(last_glyph, *first_note, next); + close_fused_glyph(last_glyph, + shift < 0? G_FLEXA : G_PODATUS, first_note, + liquescentia & ~TAIL_LIQUESCENTIA_MASK, next); + prev_shift = shift2; + processed = true; + } + } + + if (!processed) { + /* didn't find anything interesting, so fuse the single note */ + gabc_determination ignored; + gregorio_glyph_type next_glyph_type = + add_note_to_a_glyph(G_UNDETERMINED, + (*first_note)->u.note.pitch, 0, (*first_note)->u.note.shape, + (*first_note)->u.note.liquescentia, *first_note, &ignored, + punctum_inclinatum_orientation); + close_fused_glyph(last_glyph, next_glyph_type, first_note, + liquescentia & ~TAIL_LIQUESCENTIA_MASK, *first_note); + prev_shift = shift; + } + + if (first) { + first = false; + liquescentia = (liquescentia & TAIL_LIQUESCENTIA_MASK) | L_FUSED; + } + } +} + +static gregorio_note *close_glyph(gregorio_glyph **last_glyph, + gregorio_glyph_type glyph_type, gregorio_note **first_note, + gregorio_liquescentia liquescentia, gregorio_note *current_note, + gregorio_shape *const punctum_inclinatum_orientation) +{ + /* special case for fusion when rarer shapes don't exist */ + if (glyph_type == G_TORCULUS) { + switch ((*first_note)->u.note.shape) { + case S_ORISCUS_UNDETERMINED: + case S_ORISCUS_ASCENDENS: + case S_ORISCUS_DESCENDENS: + case S_ORISCUS_SCAPUS_UNDETERMINED: + case S_ORISCUS_SCAPUS_ASCENDENS: + case S_ORISCUS_SCAPUS_DESCENDENS: + glyph_type = G_FUSED; + break; + default: + break; + } + } + + if (glyph_type == G_FUSED) { + return close_fusion_glyph(last_glyph, first_note, liquescentia, + current_note, punctum_inclinatum_orientation); + } else { + return close_normal_glyph(last_glyph, glyph_type, first_note, + liquescentia, current_note); + } +} + +/* a small function to automatically determine the pitch of a custo : it is + * the pitch of the next note, but we must take care of the clef changes, as + * custo are (normally and for now) only present before clef changes. */ +/* TODO: there may be a side effect with the flated keys... */ + +static char gabc_determine_custo_pitch(gregorio_note *current_note, + int current_key, const gregorio_score *const score) +{ + int pitch_difference = 0; + int newkey; + for (; current_note; current_note = current_note->next) { + switch (current_note->type) { + case GRE_CLEF: + newkey = gregorio_calculate_new_key(current_note->u.clef); + pitch_difference = newkey - current_key; + break; + + case GRE_NOTE: + pitch_difference = + (int) current_note->u.note.pitch - pitch_difference; + while (pitch_difference < LOWEST_PITCH) { + pitch_difference += 7; + } + while (pitch_difference > score->highest_pitch) { + pitch_difference -= 7; + } + assert(pitch_difference >= LOWEST_PITCH + && pitch_difference <= score->highest_pitch); + return (char) pitch_difference; + + default: + break; + } + } + return DUMMY_PITCH; +} + +/**************************** + * + * Function called with a list of gregorio_notes as argument, this + * list is determined from gabc notation by the function + * gabc_det_notes_from_string. + * + * In this function we create a list of glyphs, by determining their + * type according to the sequence of notes (we look at their height, + * shape, etc.). Each glyph has a pointer gregorio_note *first_note + * that will be filled with the good note (and notes will be cut as + * explained in the comments on close_glyph function). + * + * Here is how it works : + * current_glyph is the glyph we are currently determining, that is to + * say a glyph whose type may change according to the note we will add + * to it. + * current_note is the note we are currently dealing with. + * Let's take an example, if the list of notes is ab, the first step + * (current_glyph: undetermined, current_note: a) will be to add a to + * nothing, that will give a glyph "punctum" containing only one note: + * a. In the second step (current_glyph: punctum, current_note: b) we + * transform the glyph in "pes". + * For special notes like a key change or something, we stop the + * determination of the current glyph, we delete the note and we add + * it as a glyph. + * + * When we determine the glyphs we can encounter the shapes + * S_QUADRATUM and S_QUILISMA_QUADRATUM, which means that the + * corresponding note is the first note of a pes quadratum (it can + * have the shape quilisma for very rare forms). But these shapes must + * not appear in the final form of the score, and we transform them + * respectively in punctum and quilisma (and the glyph type must be + * pes_quadratum, but it is done in add_note_to_a_glyph). + * +****************************/ + +/* this function updates current_key with the new values (with clef changes) */ + +gregorio_glyph *gabc_det_glyphs_from_notes(gregorio_note *current_note, + int *current_key, gregorio_shape *const punctum_inclinatum_orientation, + const gregorio_score *const score) +{ + /* the first note of the current glyph, to be able to close it well: + * later we will cut the link (next_notes and previous_note) between + * this note and the previous one */ + gregorio_note *current_glyph_first_note = current_note; + + /* the last glyph we have totally determined. It is automatically + * updated by close_glyph(). */ + gregorio_glyph *last_glyph = NULL; + + /* type of the glyph we are currently determining */ + gregorio_glyph_type current_glyph_type = G_UNDETERMINED; + gregorio_glyph_type next_glyph_type = G_UNDETERMINED; + char last_pitch = USELESS_VALUE; + gregorio_note *next_note = NULL; + + /* determination of end of glyphs, see comments on + * add_note_to_a_glyph */ + gabc_determination end_of_glyph = DET_NO_END; + + /* a char representing the liquescentia of the current glyph */ + gregorio_liquescentia liquescentia = L_NO_LIQUESCENTIA; + gregorio_liquescentia head_liquescentia; + bool autofuse = false, first_autofused_note = false; + + gregorio_assert(current_note, gabc_det_glyphs_from_notes, + "current_note may not be NULL", return NULL); + + gregorio_go_to_first_note(¤t_note); + + while (current_note) { + bool add = true; + + next_note = current_note->next; + if (current_note->type != GRE_NOTE) { + gregorio_type type = current_note->type; + char pitch = USELESS_VALUE; + bool force = false; + gregorio_sign sign = _NO_SIGN; + + if (current_glyph_type == G_FUSED + && (type == GRE_TEXVERB_GLYPH || type == GRE_TEXVERB_ELEMENT)) { + current_note = next_note; + continue; + } + + if (current_glyph_type != G_UNDETERMINED) { + close_glyph(&last_glyph, next_glyph_type, + ¤t_glyph_first_note, liquescentia, + current_note->previous, punctum_inclinatum_orientation); + current_glyph_type = G_UNDETERMINED; + liquescentia = L_NO_LIQUESCENTIA; + } + + switch (type) { + case GRE_CLEF: + pitch = current_note->u.note.pitch; + *current_key = gregorio_calculate_new_key(current_note->u.clef); + break; + + case GRE_CUSTOS: + pitch = gabc_determine_custo_pitch(current_note->next, + *current_key, score); + break; + + case GRE_MANUAL_CUSTOS: + pitch = current_note->u.note.pitch; + type = GRE_CUSTOS; + force = true; + break; + + case GRE_BAR: + /* we calculate the signs of the bars */ + if (current_note->signs == _V_EPISEMA) { + sign = _V_EPISEMA; + } else { + sign = current_note->special_sign; + } + if (current_note->h_episema_above) { + if (sign == _V_EPISEMA) { + sign = _V_EPISEMA_BAR_H_EPISEMA; + } else { + sign = _BAR_H_EPISEMA; + } + } + break; + + case GRE_AUTOFUSE_START: + autofuse = true; + first_autofused_note = true; + add = false; + break; + + case GRE_AUTOFUSE_END: + autofuse = false; + add = false; + break; + + default: + /* do nothing */ + break; + } + + if (add) { + if (pitch == USELESS_VALUE) { + gregorio_add_unpitched_element_as_glyph(&last_glyph, type, + &(current_note->u.other), sign, + current_note->texverb); + } else if (type == GRE_CLEF) { + gregorio_add_clef_as_glyph(&last_glyph, + current_note->u.clef, current_note->texverb); + } else { + gregorio_add_pitched_element_as_glyph(&last_glyph, type, + pitch, force, current_note->texverb); + } + } + current_glyph_first_note = current_note->next; + current_note->texverb = 0; + gregorio_free_one_note(¤t_note); + last_pitch = USELESS_VALUE; + continue; + } + + /* first we do what must be done with liquescentia */ + head_liquescentia = current_note->u.note.liquescentia + & (L_INITIO_DEBILIS | L_FUSED); + if (head_liquescentia) { + /* initio debilis or fused */ + if (current_glyph_type != G_UNDETERMINED) { + /* if it is not the first glyph */ + close_glyph(&last_glyph, current_glyph_type, + ¤t_glyph_first_note, + liquescentia, current_note->previous, + punctum_inclinatum_orientation); + current_glyph_type = G_UNDETERMINED; + } + liquescentia = head_liquescentia; + } + + if (autofuse) { + if (current_glyph_first_note == current_note) { + if (first_autofused_note) { + first_autofused_note = false; + } else { + if (!(current_note->u.note.liquescentia & L_INITIO_DEBILIS)) { + current_note->u.note.liquescentia |= L_FUSED; + liquescentia |= L_FUSED; + } + } + } else { + /* only handle the multi-note case here; the single-note + * case will be added normally by the state machine */ + if (is_tail_liquescentia(current_note->u.note.liquescentia) + && current_glyph_type > G_PUNCTA_INCLINATA) { + /* once we hit a liquescent, that's the end of a string of + * fused notes */ + liquescentia |= current_note->u.note.liquescentia; + current_note = close_glyph(&last_glyph, G_FUSED, + ¤t_glyph_first_note, liquescentia, + current_note, punctum_inclinatum_orientation); + current_glyph_type = G_UNDETERMINED; + liquescentia = L_NO_LIQUESCENTIA; + add = false; + } else { + switch (current_note->u.note.shape) { + case S_PUNCTUM: + case S_ORISCUS_UNDETERMINED: + case S_ORISCUS_ASCENDENS: + case S_ORISCUS_DESCENDENS: + case S_ORISCUS_SCAPUS_UNDETERMINED: + case S_ORISCUS_SCAPUS_ASCENDENS: + case S_ORISCUS_SCAPUS_DESCENDENS: + case S_QUILISMA: + case S_QUADRATUM: + case S_QUILISMA_QUADRATUM: + /* these are fusible */ + if (current_glyph_type <= G_PUNCTA_INCLINATA + || current_note->u.note.shape != S_PUNCTUM) { + /* if we had some puncta inclinata, then end them */ + /* if the current shape is not a punctum, start a + * new shape */ + close_glyph(&last_glyph, current_glyph_type, + ¤t_glyph_first_note, + liquescentia, current_note->previous, + punctum_inclinatum_orientation); + if (!(current_note->u.note.liquescentia + & L_INITIO_DEBILIS)) { + current_note->u.note.liquescentia |= L_FUSED; + liquescentia |= L_FUSED; + } + } + next_glyph_type = current_glyph_type = G_FUSED; + add = false; + break; + + default: + /* not fusible; will be added normally by the state + * machine */ + break; + } + } + } + } + + if (add) { + next_glyph_type = add_note_to_a_glyph(current_glyph_type, + current_note->u.note.pitch, last_pitch, + current_note->u.note.shape, + current_note->u.note.liquescentia, + current_glyph_first_note, &end_of_glyph, + punctum_inclinatum_orientation); + } + + /* patch to have good shapes in the special cases of pes quadratum and + * pes quilisma quadratum. */ + switch (current_note->u.note.shape) { + case S_QUADRATUM: + current_note->u.note.shape = S_PUNCTUM; + break; + + case S_QUILISMA_QUADRATUM: + current_note->u.note.shape = S_QUILISMA; + break; + + default: + /* do nothing */ + break; + } + + if (add) { + /* see comments on add_note_to_a_glyph for the meaning of + * end_of_glyph */ + switch (end_of_glyph) { + case DET_NO_END: + current_glyph_type = next_glyph_type; + /* + * we deal with liquescentia + */ + if (is_tail_liquescentia(current_note->u.note.liquescentia)) { + /* special cases of oriscus descendens, treated like normal + * oriscus in some cases. */ + if (score->legacy_oriscus_orientation) { + if (current_note->u.note.shape == S_ORISCUS_DESCENDENS + && current_note->next + && current_note->next->type == GRE_NOTE + && current_note->next->u.note.pitch < + current_note->u.note.pitch) { + last_pitch = current_note->u.note.pitch; + current_note->u.note.shape = S_ORISCUS_ASCENDENS; + current_note->u.note.liquescentia = + L_NO_LIQUESCENTIA; + current_note = next_note; + continue; + } + } + /* special cases of the punctum inclinatum deminutus and + * auctus */ + if (is_punctum_inclinatum(current_note->u.note.shape, false)) { + if (current_note->u.note.liquescentia == L_DEMINUTUS) { + current_note->u.note.shape = + S_PUNCTUM_INCLINATUM_DEMINUTUS; + } + if (current_note->u.note.liquescentia + == L_AUCTUS_DESCENDENS + || current_note->u.note.liquescentia + == L_AUCTUS_ASCENDENS) { + current_note->u.note.shape = + S_PUNCTUM_INCLINATUM_AUCTUS; + } + + if (current_note->next + && current_note->next->type == GRE_NOTE + && is_punctum_inclinatum( + current_note->next->u.note.shape, false) + && current_note->next->u.note.liquescentia + == L_DEMINUTUS) { + last_pitch = current_note->u.note.pitch; + current_note = next_note; + continue; + } + } + liquescentia |= current_note->u.note.liquescentia; + /* once again, only works with the good values in the header + * file */ + current_note = close_glyph(&last_glyph, current_glyph_type, + ¤t_glyph_first_note, liquescentia, + current_note, punctum_inclinatum_orientation); + current_glyph_type = G_UNDETERMINED; + liquescentia = L_NO_LIQUESCENTIA; + } + break; + case DET_END_OF_PREVIOUS: + if (current_note->previous) + { + /* we don't want to close previous glyph twice */ + close_glyph(&last_glyph, current_glyph_type, + ¤t_glyph_first_note, liquescentia, + current_note->previous, + punctum_inclinatum_orientation); + } + current_glyph_type = next_glyph_type; + liquescentia = L_NO_LIQUESCENTIA; + last_pitch = USELESS_VALUE; + /* we deal with liquescentia */ + if (is_tail_liquescentia(current_note->u.note.liquescentia)) + /* not an initio debilis, because we considered it in the first + * part... */ + { + /* special cases of the punctum inclinatum deminutus and + * auctus */ + if (is_punctum_inclinatum(current_note->u.note.shape, false)) { + if (current_note->u.note.liquescentia == L_DEMINUTUS) { + current_note->u.note.shape = + S_PUNCTUM_INCLINATUM_DEMINUTUS; + } + if (current_note->u.note.liquescentia == + L_AUCTUS_DESCENDENS + || current_note->u.note.liquescentia == + L_AUCTUS_ASCENDENS) { + current_note->u.note.shape = + S_PUNCTUM_INCLINATUM_AUCTUS; + } + if (current_note->next + && current_note->next->type == GRE_NOTE + && is_punctum_inclinatum( + current_note->next->u.note.shape, false) + && current_note->next->u.note.liquescentia == + L_DEMINUTUS) { + current_note = next_note; + continue; + } + } + current_note = close_glyph(&last_glyph, current_glyph_type, + ¤t_glyph_first_note, + current_note->u.note.liquescentia, current_note, + punctum_inclinatum_orientation); + current_glyph_type = G_UNDETERMINED; + } + break; + case DET_END_OF_CURRENT: + liquescentia += current_note->u.note.liquescentia; + /* once again, only works with the good values in the header file */ + current_note = close_glyph(&last_glyph, next_glyph_type, + ¤t_glyph_first_note, liquescentia, current_note, + punctum_inclinatum_orientation); + current_glyph_type = G_UNDETERMINED; + liquescentia = L_NO_LIQUESCENTIA; + break; + case DET_END_OF_BOTH: + if (current_note->previous) + { + /* we don't want to close previous glyph twice */ + close_glyph(&last_glyph, current_glyph_type, + ¤t_glyph_first_note, liquescentia, + current_note->previous, + punctum_inclinatum_orientation); + } + current_glyph_type = G_UNDETERMINED; + liquescentia = L_NO_LIQUESCENTIA; + current_note = close_glyph(&last_glyph, next_glyph_type, + ¤t_glyph_first_note, + current_note->u.note.liquescentia, current_note, + punctum_inclinatum_orientation); + break; + } + } + + if (!next_note && current_glyph_type != G_UNDETERMINED) { + /* we must end the determination here */ + current_note = close_glyph(&last_glyph, current_glyph_type, + ¤t_glyph_first_note, liquescentia, current_note, + punctum_inclinatum_orientation); + } + + last_pitch = current_note->u.note.pitch; + current_note = next_note; + } + /* end of while */ + + gregorio_go_to_first_glyph(&last_glyph); + return last_glyph; +} |