/*
* Gregorio is a program that translates gabc files to GregorioTeX
* This file provides functions for determining glyphs from notes.
*
* Copyright (C) 2006-2021 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 "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_FLAT_PAREN:
case S_SHARP:
case S_SHARP_PAREN:
case S_NATURAL:
case S_NATURAL_PAREN:
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;
}