diff options
Diffstat (limited to 'Build/source/texk/gregorio/gregorio-src/src/gabc/gabc-score-determination.c')
-rw-r--r-- | Build/source/texk/gregorio/gregorio-src/src/gabc/gabc-score-determination.c | 474 |
1 files changed, 350 insertions, 124 deletions
diff --git a/Build/source/texk/gregorio/gregorio-src/src/gabc/gabc-score-determination.c b/Build/source/texk/gregorio/gregorio-src/src/gabc/gabc-score-determination.c index b6659c18499..433c29f7df0 100644 --- a/Build/source/texk/gregorio/gregorio-src/src/gabc/gabc-score-determination.c +++ b/Build/source/texk/gregorio/gregorio-src/src/gabc/gabc-score-determination.c @@ -3,7 +3,7 @@ * This file implements the score parser. * * Gregorio score determination from gabc utilities. - * Copyright (C) 2016 The Gregorio Project (see CONTRIBUTORS.md) + * Copyright (C) 2016-2017 The Gregorio Project (see CONTRIBUTORS.md) * * This file is part of Gregorio. * @@ -24,6 +24,7 @@ #include "config.h" #include <stdio.h> #include <stdlib.h> +#include <string.h> #include <assert.h> #include "bool.h" #include "struct.h" @@ -31,64 +32,116 @@ #include "gabc.h" #include "gabc-score-determination.h" #include "messages.h" +#include "support.h" -void fix_custos(gregorio_score *score_to_check) +void gabc_suppress_extra_custos_at_linebreak(gregorio_score *score) +{ + gregorio_syllable *syllable; + gregorio_element **custos = NULL; + + for (syllable = score->first_syllable; syllable; + syllable = syllable->next_syllable) { + gregorio_element **element; + for (element = syllable->elements; element && *element; + element = &((*element)->next)) { + switch ((*element)->type) { + case GRE_CUSTOS: + if (!((*element)->u.misc.pitched.force_pitch)) { + /* save the encountered non-forced custos */ + custos = element; + } else { + /* forget the (previous) custos */ + custos = NULL; + } + break; + case GRE_CLEF: + case GRE_BAR: + /* remember the custos if only these appear before linebreak */ + break; + case GRE_END_OF_LINE: + if (custos) { + /* suppress the custos when linebreak follows */ + gregorio_free_one_element(custos); + } + /* fall through */ + default: + /* forget the custos */ + custos = NULL; + break; + } + } + } +} + +void gabc_fix_custos_pitches(gregorio_score *score_to_check) { gregorio_syllable *current_syllable; gregorio_element *current_element; - gregorio_element *custo_element; - char pitch = 0; - char pitch_difference = 0; + gregorio_element *custos_element; int newkey; int current_key; + if (!score_to_check || !score_to_check->first_syllable || !score_to_check->first_voice_info) { return; } + current_key = gregorio_calculate_new_key( score_to_check->first_voice_info->initial_clef); - current_syllable = score_to_check->first_syllable; - while (current_syllable) { - current_element = (current_syllable->elements)[0]; - while (current_element) { - if (current_element->type == GRE_CUSTOS) { - custo_element = current_element; - pitch = custo_element->u.misc.pitched.pitch; - /* we look for the key */ - while (current_element) { - if (current_element->type == GRE_CLEF) { - pitch = gregorio_determine_next_pitch(current_syllable, - current_element, NULL, NULL); - newkey = gregorio_calculate_new_key( - current_element->u.misc.clef); - pitch_difference = (char) newkey - (char) current_key; - pitch -= pitch_difference; - current_key = newkey; - } - if (!custo_element->u.misc.pitched.force_pitch) { - while (pitch < LOWEST_PITCH) { - pitch += 7; - } - while (pitch > score_to_check->highest_pitch) { - pitch -= 7; - } - custo_element->u.misc.pitched.pitch = pitch; - } - assert(custo_element->u.misc.pitched.pitch >= LOWEST_PITCH - && custo_element->u.misc.pitched.pitch - <= score_to_check->highest_pitch); - current_element = current_element->next; - } + for (current_syllable = score_to_check->first_syllable; current_syllable; + current_syllable = current_syllable->next_syllable) { + for (current_element = (current_syllable->elements)[0]; current_element; + current_element = current_element->next) { + if (current_element->type == GRE_CLEF) { + newkey = gregorio_calculate_new_key( + current_element->u.misc.clef); + current_element->u.misc.clef.pitch_difference = + (signed char) newkey - (signed char) current_key; + current_key = newkey; } - if (current_element) { - if (current_element->type == GRE_CLEF) { - current_key = gregorio_calculate_new_key( - current_element->u.misc.clef); + } + } + + custos_element = NULL; + for (current_syllable = score_to_check->first_syllable; current_syllable; + current_syllable = current_syllable->next_syllable) { + for (current_element = (current_syllable->elements)[0]; current_element; + current_element = current_element->next) { + switch (current_element->type) { + case GRE_CUSTOS: + if (current_element->u.misc.pitched.force_pitch) { + /* forget about the preceding custos if a forced one is + * encountered */ + custos_element = NULL; + } else { + /* the pitch is not forced, so it may need to be adjusted */ + custos_element = current_element; + custos_element->u.misc.pitched.pitch = + gregorio_determine_next_pitch(current_syllable, + current_element, NULL, NULL); + } + break; + + case GRE_ELEMENT: + /* if it's an element, forget any preceding custos */ + custos_element = NULL; + break; + + case GRE_CLEF: + if (custos_element) { + /* adjust the preceding custos for the clef */ + custos_element->u.misc.pitched.pitch = + gregorio_adjust_pitch_into_staff(score_to_check, + custos_element->u.misc.pitched.pitch + - current_element->u.misc.clef.pitch_difference); } - current_element = current_element->next; + break; + + default: + /* to prevent the warning */ + break; } } - current_syllable = current_syllable->next_syllable; } } @@ -96,7 +149,7 @@ void fix_custos(gregorio_score *score_to_check) * A function that checks the score integrity. */ -bool check_score_integrity(gregorio_score *score_to_check) +bool gabc_check_score_integrity(gregorio_score *score_to_check) { bool good = true; @@ -144,7 +197,7 @@ bool check_score_integrity(gregorio_score *score_to_check) * Another function to be improved: this one checks the validity of the voice_infos. */ -bool check_infos_integrity(gregorio_score *score_to_check) +bool gabc_check_infos_integrity(gregorio_score *score_to_check) { if (!score_to_check->name) { gregorio_message(_("no name specified, put `name:...;' at the " @@ -154,100 +207,100 @@ bool check_infos_integrity(gregorio_score *score_to_check) return true; } -/* data must be (gregorio_note **) */ -static void oriscus_orientation_visit( - const gregorio_note_iter_position *const p, void *const data) +static void set_oriscus_descending(const gregorio_note_iter_position *const p, + void *const ignored __attribute__((unused))) { - gregorio_note *const note = p->note; - gregorio_note **const oriscus_ptr = (gregorio_note **const)data; - gregorio_note *const oriscus = *oriscus_ptr; - /* making oriscus const ensures we don't attempt to change *oriscus_ptr - * via oriscus */ - - if (oriscus) { - if (note->u.note.pitch <= oriscus->u.note.pitch) { - /* descending or unison */ - switch(oriscus->u.note.shape) { - case S_ORISCUS_UNDETERMINED: - oriscus->u.note.shape = S_ORISCUS_DESCENDENS; - break; - case S_ORISCUS_SCAPUS_UNDETERMINED: - oriscus->u.note.shape = S_ORISCUS_SCAPUS_DESCENDENS; - break; - case S_ORISCUS_CAVUM_UNDETERMINED: - oriscus->u.note.shape = S_ORISCUS_CAVUM_DESCENDENS; - break; - default: - /* not reachable unless there's a - * programming error */ - /* LCOV_EXCL_START */ - gregorio_fail(oriscus_orientation_visit, "bad_shape"); - break; - /* LCOV_EXCL_STOP */ - } - } else { /* ascending */ - switch(oriscus->u.note.shape) { - case S_ORISCUS_UNDETERMINED: - oriscus->u.note.shape = S_ORISCUS_ASCENDENS; - break; - case S_ORISCUS_SCAPUS_UNDETERMINED: - oriscus->u.note.shape = S_ORISCUS_SCAPUS_ASCENDENS; - break; - case S_ORISCUS_CAVUM_UNDETERMINED: - oriscus->u.note.shape = S_ORISCUS_CAVUM_ASCENDENS; - break; - default: - /* not reachable unless there's a - * programming error */ - /* LCOV_EXCL_START */ - gregorio_fail(oriscus_orientation_visit, "bad_shape"); - break; - /* LCOV_EXCL_STOP */ - } + switch(p->note->u.note.shape) { + case S_ORISCUS_UNDETERMINED: + p->note->u.note.shape = S_ORISCUS_DESCENDENS; + break; + case S_ORISCUS_SCAPUS_UNDETERMINED: + p->note->u.note.shape = S_ORISCUS_SCAPUS_DESCENDENS; + gregorio_assert_only(p->glyph->u.notes.glyph_type + != G_PES_DESCENDENS_ORISCUS, set_oriscus_descending, + "glyph type should not be G_PES_DESCENDENS_ORISCUS"); + if (p->glyph->u.notes.glyph_type == G_PES_ASCENDENS_ORISCUS) { + p->glyph->u.notes.glyph_type = G_PES_DESCENDENS_ORISCUS; } - *oriscus_ptr = NULL; + break; + default: + break; } +} - switch (note->u.note.shape) { +static void set_oriscus_ascending(const gregorio_note_iter_position *const p, + void *const ignored __attribute__((unused))) +{ + switch(p->note->u.note.shape) { case S_ORISCUS_UNDETERMINED: + p->note->u.note.shape = S_ORISCUS_ASCENDENS; + break; case S_ORISCUS_SCAPUS_UNDETERMINED: - case S_ORISCUS_CAVUM_UNDETERMINED: - *oriscus_ptr = note; + p->note->u.note.shape = S_ORISCUS_SCAPUS_ASCENDENS; + gregorio_assert_only(p->glyph->u.notes.glyph_type + != G_PES_DESCENDENS_ORISCUS, set_oriscus_ascending, + "glyph type should not be G_PES_DESCENDENS_ORISCUS"); break; - default: break; } } -void determine_oriscus_orientation(const gregorio_score *const score) +/* data must be (gregorio_note_iter_position *) */ +static void oriscus_orientation_visit( + const gregorio_note_iter_position *const p, void *const data) { - gregorio_note *oriscus = NULL; + gregorio_note *const note = p->note; + gregorio_note_iter_position *const oriscus = + (gregorio_note_iter_position *const)data; - gregorio_for_each_note(score, oriscus_orientation_visit, &oriscus); + if (oriscus->note && note->u.note.pitch != oriscus->note->u.note.pitch) { + if (note->u.note.pitch <= oriscus->note->u.note.pitch) { + /* descending (or undetermined oriscus in unison) */ + gregorio_from_note_to_note(oriscus, p, false, + set_oriscus_descending, NULL, GRESTRUCT_NONE, NULL); + } else { + /* ascending */ + gregorio_from_note_to_note(oriscus, p, false, + set_oriscus_ascending, NULL, GRESTRUCT_NONE, NULL); + } + oriscus->syllable = NULL, + oriscus->element = NULL, + oriscus->note = NULL; + oriscus->glyph = NULL; + } - if (oriscus) { - /* oriscus at the end of the score */ - switch(oriscus->u.note.shape) { + if (!oriscus->note) { + switch (note->u.note.shape) { case S_ORISCUS_UNDETERMINED: - oriscus->u.note.shape = S_ORISCUS_DESCENDENS; - break; case S_ORISCUS_SCAPUS_UNDETERMINED: - oriscus->u.note.shape = S_ORISCUS_SCAPUS_DESCENDENS; - break; - case S_ORISCUS_CAVUM_UNDETERMINED: - oriscus->u.note.shape = S_ORISCUS_CAVUM_DESCENDENS; + *oriscus = *p; break; + default: - /* not reachable unless there's a programming error */ - /* LCOV_EXCL_START */ - gregorio_fail(determine_oriscus_orientation, "bad_shape"); break; - /* LCOV_EXCL_STOP */ } } } +void gabc_determine_oriscus_orientation(const gregorio_score *const score) +{ + gregorio_note_iter_position oriscus = { + /* .syllable = */ NULL, + /* .element = */ NULL, + /* .glyph = */ NULL, + /* .note = */ NULL + }; + + gregorio_for_each_note(score, oriscus_orientation_visit, NULL, + GRESTRUCT_NONE, &oriscus); + + if (oriscus.note) { + gregorio_from_note_to_note(&oriscus, NULL, true, + set_oriscus_descending, NULL, GRESTRUCT_NONE, NULL); + } +} + typedef struct { gregorio_note_iter_position first, previous; gregorio_shape orientation; @@ -298,8 +351,6 @@ static void punctum_inclinatum_orientation_visit( case S_PUNCTUM_INCLINATUM_DESCENDENS: case S_PUNCTUM_INCLINATUM_DEMINUTUS: case S_PUNCTUM_INCLINATUM_AUCTUS: - case S_PUNCTUM_CAVUM_INCLINATUM: - case S_PUNCTUM_CAVUM_INCLINATUM_AUCTUS: v->orientation = S_PUNCTUM_INCLINATUM_DESCENDENS; is_punctum_inclinatum = true; break; @@ -324,8 +375,8 @@ static void punctum_inclinatum_orientation_visit( ? S_PUNCTUM_INCLINATUM_ASCENDENS : S_PUNCTUM_INCLINATUM_DESCENDENS; } - gregorio_from_note_to_note(&v->first, &v->previous, set_shape, - &v->orientation); + gregorio_from_note_to_note(&v->first, &v->previous, true, set_shape, + NULL, GRESTRUCT_NONE, &v->orientation); v->first.syllable = NULL; v->first.element = NULL; v->first.glyph = NULL; @@ -341,7 +392,7 @@ static void punctum_inclinatum_orientation_visit( v->previous = *p; } -void determine_punctum_inclinatum_orientation( +void gabc_determine_punctum_inclinatum_orientation( const gregorio_score *const score) { punctum_inclinatum_vars v = { @@ -361,13 +412,188 @@ void determine_punctum_inclinatum_orientation( /* .running = */ 0, }; - gregorio_for_each_note(score, punctum_inclinatum_orientation_visit, &v); - + gregorio_for_each_note(score, punctum_inclinatum_orientation_visit, NULL, + GRESTRUCT_NONE, &v); + if (v.first.note) { v.orientation = (v.running > 0) ? S_PUNCTUM_INCLINATUM_ASCENDENS : S_PUNCTUM_INCLINATUM_DESCENDENS; - gregorio_from_note_to_note(&v.first, &v.previous, set_shape, - &v.orientation); + gregorio_from_note_to_note(&v.first, &v.previous, true, set_shape, NULL, + GRESTRUCT_NONE, &v.orientation); } } + +typedef struct note_stack { + gregorio_note *note; + struct note_stack *prev; +} note_stack; + +static void note_stack_push(note_stack **const stack, gregorio_note *note) { + note_stack *item = gregorio_malloc(sizeof(note_stack)); + item->note = note; + item->prev = *stack; + *stack = item; +} + +static gregorio_note *note_stack_pop(note_stack **const stack) { + note_stack *item = *stack; + if (item) { + gregorio_note *note = item->note; + note_stack *prev = item->prev; + free(item); + *stack = prev; + return note; + } + return NULL; +} + +static void note_stack_clear(note_stack **const stack) { + note_stack *item = *stack; + while (item) { + note_stack *prev = item->prev; + free(item); + item = prev; + } + *stack = NULL; +} + +typedef struct { + note_stack *high, *low; + gregorio_note *prev_note; + signed char high_ledger_line_pitch; + bool running_high, running_low; +} ledger_line_vars; + +static __inline void clear_ledger_line_vars(ledger_line_vars *const v) { + note_stack_clear(&v->high); + note_stack_clear(&v->low); + v->prev_note = NULL; + v->running_high = false; + v->running_low = false; +} + +static __inline void adjust_ledger(const gregorio_note_iter_position *const p, + const gregorio_ledger_specificity specificity, bool ledger_line, + note_stack **const stack, bool *const running, gregorio_note *prev_note, + const signed char high_ledger_line_pitch, + bool (*extend_ledger)(gregorio_note *, const gregorio_note *, + const gregorio_note *, signed char)) +{ + if (specificity & LEDGER_DRAWN) { + if (ledger_line) { + gregorio_note *after = p->note; + gregorio_note *note; + /* process from this ledger backwards */ + while ((note = note_stack_pop(stack))) { + if (!extend_ledger(note, NULL, after, high_ledger_line_pitch)) { + /* ledger has ended */ + break; + } + after = note; + } + *running = true; + } else { + *running = false; + } + note_stack_clear(stack); + } else { + if (*running) { + if (!extend_ledger(p->note, prev_note, NULL, + high_ledger_line_pitch)) { + /* ledger has ended */ + note_stack_push(stack, p->note); + *running = false; + } + /* else stack should be empty, keep it that way */ + } else { + note_stack_push(stack, p->note); + } + } +} + +static bool extend_high_ledger(gregorio_note *const note, + const gregorio_note *const note_before, + const gregorio_note *const note_after, + const signed char high_ledger_line_pitch) +{ + bool extend = false; + + if (note_before) { + extend = note_before->u.note.pitch < note->u.note.pitch + || (note_before->u.note.pitch > high_ledger_line_pitch + && note->u.note.pitch < high_ledger_line_pitch); + } else if (note_after) { + extend = note_after->u.note.pitch < note->u.note.pitch + || (note_after->u.note.pitch > high_ledger_line_pitch + && note->u.note.pitch < high_ledger_line_pitch); + } + + if (extend) { + note->high_ledger_line = true; + note->high_ledger_specificity = LEDGER_DRAWN; + } + return extend; +} + +static bool extend_low_ledger(gregorio_note *const note, + const gregorio_note *const note_before, + const gregorio_note *const note_after, + const signed char high_ledger_line_pitch __attribute__((__unused__))) +{ + bool extend = false; + + if (note_before) { + extend = note_before->u.note.pitch < note->u.note.pitch + || (note_before->u.note.pitch > LOW_LEDGER_LINE_PITCH + && note->u.note.pitch < LOW_LEDGER_LINE_PITCH); + } else if (note_after) { + extend = note_after->u.note.pitch < note->u.note.pitch + || (note_after->u.note.pitch > LOW_LEDGER_LINE_PITCH + && note->u.note.pitch < LOW_LEDGER_LINE_PITCH); + } + + if (extend) { + note->low_ledger_line = true; + note->low_ledger_specificity = LEDGER_DRAWN; + } + return extend; +} + +/* data must be (ledger_line_vars *) */ +static void ledger_line_visit(const gregorio_note_iter_position *const p, + void *const data) +{ + ledger_line_vars *const v = (ledger_line_vars *)data; + + adjust_ledger(p, p->note->high_ledger_specificity, p->note->high_ledger_line, + &v->high, &v->running_high, v->prev_note, v->high_ledger_line_pitch, + &extend_high_ledger); + adjust_ledger(p, p->note->low_ledger_specificity, p->note->low_ledger_line, + &v->low, &v->running_low, v->prev_note, v->high_ledger_line_pitch, + &extend_low_ledger); + + v->prev_note = p->note; +} + +/* data must be (ledger_line_vars *) */ +static void ledger_line_end_item( + const gregorio_note_iter_position *const p __attribute__((__unused__)), + const gregorio_note_iter_item_type item_type, void *const data) +{ + if (item_type == GRESTRUCT_ELEMENT) { + clear_ledger_line_vars((ledger_line_vars *)data); + } +} + +void gabc_determine_ledger_lines(const gregorio_score *const score) +{ + ledger_line_vars v; + memset(&v, 0, sizeof v); + v.high_ledger_line_pitch = score->high_ledger_line_pitch; + + gregorio_for_each_note(score, ledger_line_visit, ledger_line_end_item, + GRESTRUCT_ELEMENT, &v); + + /* stacks should be cleared by ledger_line_end_item */ +} |