summaryrefslogtreecommitdiff
path: root/Build/source/texk/gregorio/gregorio-src/src/gabc/gabc-elements-determination.c
blob: 16ca2dfa60cbaaf73fdff73c035701bf5b4ae11c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
/*
 * 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 <http://www.gnu.org/licenses/>. 
 */

#include "config.h"
#include <stdio.h>
#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(&current_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(&current_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(&current_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, &current_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(&current_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(&current_glyph);
            continue;
        }

        if (is_fused(current_glyph->u.notes.liquescentia)) {
            do_not_cut = true;
        } else if (force_cut) {
            cut_before(current_glyph, &first_glyph, &current_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, &current_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, &current_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(&current_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;
}