/*
* Gregorio is a program that translates gabc files to GregorioTeX.
* This file provides functions for determining elements 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 .
*/
#include "config.h"
#include
#include "bool.h"
#include "struct.h"
#include "messages.h"
#include "gabc.h"
#define SINGLE_NOTE_GLYPH \
G_PUNCTUM: \
case G_VIRGA: \
case G_BIVIRGA: \
case G_TRIVIRGA: \
case G_VIRGA_REVERSA: \
case G_STROPHA: \
case G_STROPHA_AUCTA: \
case G_DISTROPHA: \
case G_DISTROPHA_AUCTA: \
case G_TRISTROPHA: \
case G_TRISTROPHA_AUCTA
#define PUNCTA_INCLINATA_ASCENDENS_GLYPH \
G_2_PUNCTA_INCLINATA_ASCENDENS: \
case G_3_PUNCTA_INCLINATA_ASCENDENS: \
case G_4_PUNCTA_INCLINATA_ASCENDENS: \
case G_5_PUNCTA_INCLINATA_ASCENDENS
#define PUNCTA_INCLINATA_DESCENDENS_GLYPH \
G_2_PUNCTA_INCLINATA_DESCENDENS: \
case G_3_PUNCTA_INCLINATA_DESCENDENS: \
case G_4_PUNCTA_INCLINATA_DESCENDENS: \
case G_5_PUNCTA_INCLINATA_DESCENDENS
static __inline signed char glyph_note_ambitus(
const gregorio_glyph *const current_glyph,
const gregorio_glyph *const previous_glyph)
{
return current_glyph->u.notes.first_note->u.note.pitch -
gregorio_glyph_last_note(previous_glyph)->u.note.pitch;
}
/*
*
* A function that will be called several times: it adds an element to the
* current element current_element: the current_element in the determination,
* it will be updated to the element that we will add first_glyph: the
* first_glyph of the element that we will add current_glyph: the last glyph
* that will be in the element
*
*/
static void close_element(gregorio_element **current_element,
gregorio_glyph **const first_glyph,
const gregorio_glyph *const current_glyph)
{
gregorio_add_element(current_element, *first_glyph);
if (*first_glyph && (*first_glyph)->previous) {
(*first_glyph)->previous->next = NULL;
(*first_glyph)->previous = NULL;
}
*first_glyph = current_glyph->next;
}
/*
*
* inline function to automatically do two or three things
*
*/
static __inline void cut_before(gregorio_glyph *current_glyph,
gregorio_glyph **first_glyph,
gregorio_element **current_element)
{
if (*first_glyph != current_glyph) {
close_element(current_element, first_glyph, current_glyph);
/* yes, this is changing value close_element sets for first_glyph */
*first_glyph = current_glyph;
}
}
/*
*
* The big function of the file, but rather simple I think.
*
*/
static gregorio_element *gabc_det_elements_from_glyphs(
gregorio_glyph *current_glyph)
{
/* the last element we have successfully added to the list of elements */
gregorio_element *current_element = NULL;
/* the first element, that we will return at the end. We have to consider
* it because the gregorio_element struct does not have previous_element
* element. */
gregorio_element *first_element = NULL;
/* the first_glyph of the element that we are currently determining */
gregorio_glyph *first_glyph = current_glyph;
/* the last real (GRE_GLYPH) that we have processed */
gregorio_glyph *previous_glyph = NULL;
/* boolean necessary to determine some cases */
bool do_not_cut = false;
bool force_cut = false;
gregorio_not_null(current_glyph, gabc_det_elements_from_glyphs, return NULL);
/* first we go to the first glyph in the chained list of glyphs (maybe to
* suppress ?) */
gregorio_go_to_first_glyph(¤t_glyph);
while (current_glyph) {
if (current_glyph->type != GRE_GLYPH) {
force_cut = false;
/* we must not cut after a glyph-level space */
if (current_glyph->type == GRE_SPACE) {
switch (current_glyph->u.misc.unpitched.info.space) {
case SP_ZERO_WIDTH:
case SP_HALF_SPACE:
case SP_INTERGLYPH_SPACE:
if (!current_glyph->next) {
close_element(¤t_element, &first_glyph, current_glyph);
}
current_glyph = current_glyph->next;
do_not_cut = true;
continue;
default:
/* any other space should be handled normally */
break;
}
} else if (current_glyph->type == GRE_TEXVERB_GLYPH) {
/* we must not cut after a texverb */
if (!current_glyph->next) {
close_element(¤t_element, &first_glyph, current_glyph);
}
current_glyph = current_glyph->next;
do_not_cut = true;
continue;
}
/* clef change or space or end of line */
cut_before(current_glyph, &first_glyph, ¤t_element);
/* if statement to make neumatic cuts not appear in elements, as
* there is always one between elements, unless the next element
* is a space */
if (current_glyph->type != GRE_SPACE
|| current_glyph->u.misc.unpitched.info.space
!= SP_NEUMATIC_CUT
|| (current_glyph->next
&& current_glyph->next->type == GRE_SPACE)) {
/* clef change or space other than neumatic cut */
if (!first_element) {
first_element = current_element;
}
gregorio_add_misc_element(¤t_element, current_glyph->type,
&(current_glyph->u.misc),
current_glyph->texverb);
}
first_glyph = current_glyph->next;
previous_glyph = NULL;
current_glyph->texverb = 0;
gregorio_free_one_glyph(¤t_glyph);
continue;
}
if (is_fused(current_glyph->u.notes.liquescentia)) {
do_not_cut = true;
} else if (force_cut) {
cut_before(current_glyph, &first_glyph, ¤t_element);
previous_glyph = NULL;
}
force_cut = false;
switch (current_glyph->u.notes.glyph_type) {
case PUNCTA_INCLINATA_ASCENDENS_GLYPH:
case G_PUNCTUM_INCLINATUM:
case G_ALTERATION:
if (!do_not_cut) {
cut_before(current_glyph, &first_glyph, ¤t_element);
do_not_cut = true;
}
break;
case G_STROPHA_AUCTA:
case G_STROPHA:
if (current_glyph->u.notes.liquescentia
& (L_AUCTUS_ASCENDENS | L_AUCTUS_DESCENDENS)) {
force_cut = true;
}
/* fall through */
case PUNCTA_INCLINATA_DESCENDENS_GLYPH:
/* we don't cut before, so we don't do anything else */
if (do_not_cut) {
do_not_cut = false;
}
break;
default:
if (previous_glyph && previous_glyph->type == GRE_GLYPH
&& !is_tail_liquescentia(
previous_glyph->u.notes.liquescentia)) {
bool break_early = false;
signed char ambitus;
switch (previous_glyph->u.notes.glyph_type) {
case SINGLE_NOTE_GLYPH:
/* we determine the last pitch */
ambitus = glyph_note_ambitus(current_glyph, previous_glyph);
if (ambitus == 0) {
do_not_cut = false;
break_early = true;
}
break;
case PUNCTA_INCLINATA_DESCENDENS_GLYPH:
case G_PUNCTUM_INCLINATUM:
switch (current_glyph->u.notes.glyph_type) {
case SINGLE_NOTE_GLYPH:
ambitus = glyph_note_ambitus(current_glyph, previous_glyph);
if (ambitus > -2 && ambitus < 2) {
do_not_cut = false;
break_early = true;
}
break;
default:
/* do nothing in particular */
break;
}
break;
case G_PODATUS:
if (current_glyph->u.notes.glyph_type == G_VIRGA) {
ambitus = glyph_note_ambitus(current_glyph, previous_glyph);
if (ambitus >= 0) {
do_not_cut = false;
break_early = true;
}
}
break;
default:
/* do nothing in particular */
break;
}
if (break_early) {
break;
}
}
if (do_not_cut) {
do_not_cut = false;
} else {
cut_before(current_glyph, &first_glyph, ¤t_element);
}
}
if (gregorio_glyph_last_note(current_glyph)->signs
& (_PUNCTUM_MORA | _AUCTUM_DUPLEX)) {
force_cut = true;
}
/* we must determine the first element, that we will return */
if (!first_element && current_element) {
first_element = current_element;
}
if (!current_glyph->next) {
close_element(¤t_element, &first_glyph, current_glyph);
}
previous_glyph = current_glyph;
current_glyph = current_glyph->next;
} /* end of while */
/*
* we must determine the first element, that we will return
*/
if (!first_element && current_element) {
first_element = current_element;
}
return first_element;
}
/*
*
* Two "hat" functions, they permit to have a good API. They amost don't do
* anything except calling the det_elements_from_glyphs.
*
* All those functions change the current_key, according to the possible new
* values (with key changes)
*
*/
static gregorio_element *gabc_det_elements_from_notes(
gregorio_note *current_note, int *current_key,
gregorio_shape *const punctum_inclinatum_orientation,
const gregorio_score *const score)
{
gregorio_element *final = NULL;
gregorio_glyph *tmp = gabc_det_glyphs_from_notes(current_note, current_key,
punctum_inclinatum_orientation, score);
final = gabc_det_elements_from_glyphs(tmp);
return final;
}
gregorio_element *gabc_det_elements_from_string(char *const str,
int *const current_key, char *macros[10],
gregorio_scanner_location *const loc,
gregorio_shape *const punctum_inclinatum_orientation,
const gregorio_score *const score)
{
gregorio_element *final;
gregorio_note *tmp;
tmp = gabc_det_notes_from_string(str, macros, loc, score);
final = gabc_det_elements_from_notes(tmp, current_key,
punctum_inclinatum_orientation, score);
return final;
}