From eefa44b8bd02425f08a53ba376e9c6efefa36e3c Mon Sep 17 00:00:00 2001 From: Seymour Shlien Date: Thu, 10 Dec 2020 15:42:29 -0500 Subject: [PATCH] 2020.12.10 --- VERSION | 2 +- abc.h | 16 ++ doc/CHANGES | 23 +++ doc/readme.txt | 6 +- drawtune.c | 150 +++++++++++++----- matchsup.c | 28 +++- midicopy.c | 4 +- parseabc.c | 403 ++++++++++++++++++++++++++++++++++++------------- parseabc.h | 19 ++- store.c | 44 ++++-- structs.h | 4 +- toabc.c | 282 ++++++++++++++++++++++++++-------- yapstree.c | 89 ++++++----- 13 files changed, 789 insertions(+), 281 deletions(-) diff --git a/VERSION b/VERSION index 3ad6eb8..6f1bbea 100644 --- a/VERSION +++ b/VERSION @@ -1,2 +1,2 @@ -2020 November 07 2020 +2020 December 10 2020 diff --git a/abc.h b/abc.h index 72baab0..b9893b6 100644 --- a/abc.h +++ b/abc.h @@ -8,6 +8,22 @@ ABC2ABC, YAPS, ABCMATCH} programname; +typedef enum { + TIMESIG_NORMAL, + TIMESIG_FREE_METER, + TIMESIG_COMMON, + TIMESIG_CUT, + TIMESIG_COMPLEX +} timesig_type_t; + +typedef struct timesig_details { + timesig_type_t type; + int num; + int denom; + int complex_values[8]; + int num_values; +} timesig_details_t; + /* define types of abc object */ typedef enum { /* types of bar sign */ diff --git a/doc/CHANGES b/doc/CHANGES index de45ec9..df42712 100644 --- a/doc/CHANGES +++ b/doc/CHANGES @@ -13997,4 +13997,27 @@ Lines 3006-3011 was replaced with }; +December 10 2020 + +James Allwright has upgraded the parser so it can now interpret +complex time signatures as shown in this example. + +X:1 +T: Example 1 using complex time signature +M:(3+2+2)/8 +L:1/8 +K:G +abc de fg|GGG aa bb|gfe GG FF| + +In addition the handling of the L: field was improved. +This impacts abc2midi, yaps, abc2abc, and abcmatch. +The implementation of these changes involved creating a new +typedef structure (timesig_details) in abc.h, and numerous new +functions or upgrades in parseabc.c (check_power_of_two, +read_complex_has_timesig, read_L_unitlen, copy_timesig, +interpret_voice_label, ... Lots of changes to parseabc.h. +Minor changes to store.c, toabc.c yapstree.c to link to the +new functions in parseabc.c + + diff --git a/doc/readme.txt b/doc/readme.txt index fc4adbd..df0da8a 100644 --- a/doc/readme.txt +++ b/doc/readme.txt @@ -1,10 +1,10 @@ abcMIDI : abc <-> MIDI conversion utilities midi2abc version 3.47 November 01 2020 -abc2midi version 4.44 October 19 2020 +abc2midi version 4.45 December 10 2020 abc2abc version 2.12 October 19 2020 -yaps version 1.85 November 07 2020 -abcmatch version 1.76 October 19 2020 +yaps version 1.86 December 10 2020 +abcmatch version 1.77 December 10 2020 midicopy version 1.37 October 10 2020 24th January 2002 diff --git a/drawtune.c b/drawtune.c index f5943d5..aff416d 100644 --- a/drawtune.c +++ b/drawtune.c @@ -39,6 +39,7 @@ #include "abc.h" #include "structs.h" #include "sizes.h" +#include "parseabc.h" #include "drawtune.h" /* external functions and variables */ @@ -695,21 +696,44 @@ static double size_keysig(char oldmap[], char newmap[]) return((double)n * 5.0); } -static double size_timesig(struct fract* meter) +static void get_complex_numerator(char buffer[], timesig_details_t *timesig) +{ + int i; + + buffer[0] = '\0'; + for (i = 0; i < timesig->num_values; i++) + { + char num_buff[20]; + + if (i > 0) + { + strcat(buffer, "+"); + } + snprintf(num_buff, 20, "%d", timesig->complex_values[i]); + num_buff[2] = '\0'; /* truncate to 2 digits */ + strcat(buffer, num_buff); + } +} + +static double size_timesig (timesig_details_t *timesig) /* compute width of the time signature */ { double len1, len2; - char temp[20]; + char temp[40]; - sprintf(temp, "%d", meter->num); - len1 = stringwidth(temp, 16.0, 2); - sprintf(temp, "%d", meter->denom); - len2 = stringwidth(temp, 16.0, 2); - if (len1 > len2) { - return(len1); + if (timesig->type == TIMESIG_COMPLEX) { + get_complex_numerator(temp, timesig); } else { - return(len2); - }; + sprintf (temp, "%d", timesig->num); + } + len1 = stringwidth (temp, 16.0, 2); + sprintf (temp, "%d", timesig->denom); + len2 = stringwidth (temp, 16.0, 2); + if (len1 > len2) { + return (len1); + } else { + return (len2); + } } static void draw_keysig(char oldmap[], char newmap[], int newmult[], @@ -782,10 +806,48 @@ static void draw_keysig(char oldmap[], char newmap[], int newmult[], fprintf(f, "\n"); } -static void draw_meter(struct fract* meter, double x) +void draw_csig (double x) +{ + fprintf (f, " %.1f csig\n", x); +} + +void draw_ctsig (double x) +{ + fprintf (f, " %.1f ctsig\n", x); +} + +void draw_tsig (double x, char *top, char *bot) +{ + fprintf (f, " %.1f (%s) (%s) tsig\n", x, top, bot); +} + +static void draw_meter (timesig_details_t *meter, double x) /* draw meter (time signature) at specified x value */ { - fprintf(f, "%.1f (%d) (%d) tsig\n", x, meter->num, meter->denom); + char num[40]; + char denom[10]; + + switch (meter->type) { + case TIMESIG_NORMAL: + snprintf (num, 10, "%d", meter->num); + snprintf (denom, 10, "%d", meter->denom); + draw_tsig (x, num, denom); + break; + case TIMESIG_COMMON: + draw_csig (x); + break; + case TIMESIG_CUT: + draw_ctsig (x); + break; + case TIMESIG_COMPLEX: + get_complex_numerator(num, meter); + snprintf (denom, 10, "%d", meter->denom); + draw_tsig (x, num, denom); + break; + default: + case TIMESIG_FREE_METER: + break; + } } static double maxstrwidth(struct llist* strings, double ptsize, int fontno) @@ -1324,17 +1386,18 @@ static void sizeclef(cleftype_t *theclef, struct feature *ft) static void sizevoice(struct voice* v, struct tune* t) /* compute width and height values for all elements in voice */ { - struct feature* ft; - struct note* anote; - struct key* akey; - cleftype_t* theclef; - char* astring; - struct fract* afract; - struct rest* arest; - struct note* lastnote; - struct chord* thischord; - struct feature* chordplace; - struct tuple* thistuple; + struct feature *ft; + struct note *anote; + struct key *akey; + cleftype_t *theclef; + timesig_details_t *timesig; + char *astring; + struct fract *afract; + struct rest *arest; + struct note *lastnote; + struct chord *thischord; + struct feature *chordplace; + struct tuple *thistuple; enum tail_type chordbeaming; int intuple, tuplecount; struct feature* tuplefeature; @@ -1402,13 +1465,18 @@ static void sizevoice(struct voice* v, struct tune* t) astring = ft->item.voidptr; case TEMPO: break; - case TIME: - afract = ft->item.voidptr; - if (afract == NULL) { - afract = &v->meter; - }; - ft->xleft = 0; - ft->xright = (float) size_timesig(afract); + case TIME: + { + double width; + + timesig = ft->item.voidptr; + if (timesig == NULL) { + timesig = &v->timesig; + } + width = size_timesig (timesig); + ft->xleft = (float)(width / 2.0); + ft->xright = (float)(width / 2.0); + } break; case KEY: ft->xleft = 0; @@ -2762,8 +2830,7 @@ static void resetvoice(struct tune* t, struct voice * v) set_keysig(v->keysig, t->keysig); /* v->keysig->sharps = t->keysig->sharps; */ }; - v->meter.num = t->meter.num; - v->meter.denom = t->meter.denom; + copy_timesig(&v->timesig, &t->timesig); } static void resettune(struct tune* t) @@ -2975,6 +3042,7 @@ static int printvoiceline(struct voice* v) struct feature* ft; struct note* anote; struct key* akey; + timesig_details_t *atimesig; char* astring; struct fract* afract; struct rest* arest; @@ -3118,17 +3186,17 @@ static int printvoiceline(struct voice* v) draw_tempo(ft->x, spacing->yinstruct, ft->item.voidptr); break; case TIME: - afract = ft->item.voidptr; - if (afract==NULL) { + atimesig = ft->item.voidptr; + if (atimesig == NULL) { if (v->line == midline) { - draw_meter(&v->meter, ft->x); - }; + draw_meter (&v->timesig, ft->x); + } } else { - setfract(&v->meter, afract->num, afract->denom); + copy_timesig(&v->timesig, atimesig); if (v->line == midline) { - draw_meter(afract, ft->x); - }; - }; + draw_meter (atimesig, ft->x); + } + } break; case KEY: akey = ft->item.voidptr; @@ -3614,7 +3682,7 @@ void printtune(struct tune* t) thisvoice = firstitem(&t->voices); while (thisvoice != NULL) { copy_clef (thisvoice->clef, &t->clef); - setfract(&thisvoice->meter, t->meter.num, t->meter.denom); + copy_timesig(&thisvoice->timesig, &t->timesig); thisvoice->place = thisvoice->first; thisvoice = nextitem(&t->voices); }; diff --git a/matchsup.c b/matchsup.c index 0270b36..542e426 100644 --- a/matchsup.c +++ b/matchsup.c @@ -790,6 +790,12 @@ int n; }; } +void event_default_length (n) +/* handles a missing L: field in the abc */ + int n; +{ +} + static void tempounits(t_num, t_denom) /* interprets Q: once default length is known */ int *t_num, *t_denom; @@ -806,19 +812,25 @@ char *post; { } - -void event_timesig(n, m, dochecking) +void event_timesig (timesig) /* handles an M: field M:n/m */ -int n, m, dochecking; + timesig_details_t *timesig; { + int dobarchecks; + + if (timesig->type == TIMESIG_FREE_METER) { + dobarchecks = 0; + } else { + dobarchecks = 1; + } if (dotune) { if (pastheader) { - addfeature(TIME, dochecking, n, m); - } else { - time_num = n; - time_denom = m; + addfeature (TIME, dobarchecks, timesig->num, timesig->denom); + } else { + time_num = timesig->num; + time_denom = timesig->denom; timesigset = 1; - barchecking = dochecking; + barchecking = dobarchecks; }; }; } diff --git a/midicopy.c b/midicopy.c index 63a9417..2466d24 100644 --- a/midicopy.c +++ b/midicopy.c @@ -385,7 +385,7 @@ copy_metatext (int type, int length, char *m) } void -copy_timesig (c1, c2, c3, c4) +midicopy_timesig (c1, c2, c3, c4) int c1, c2, c3, c4; { char data[4]; @@ -996,7 +996,7 @@ metaevent (int type) mf_write_meta_event (0x54, m, 5); break; case 0x58: - copy_timesig (m[0], m[1], m[2], m[3]); + midicopy_timesig (m[0], m[1], m[2], m[3]); break; case 0x59: copy_keysig (m[0], m[1]); diff --git a/parseabc.c b/parseabc.c index 4cae5d8..3e9f9f4 100644 --- a/parseabc.c +++ b/parseabc.c @@ -95,6 +95,10 @@ int num_voices = 0; /* [JA] 2020-10-12 */ int repcheck = 1; /* enables/ disables repeat checking */ /* abc2midi disables repeat checking because it does its own thing */ voice_context_t voicecode[MAX_VOICES]; +timesig_details_t master_timesig; /* [JA] 2020-12-10 */ +cleftype_t master_clef; +int has_timesig; +int master_unitlen; /* L: field value is 1/unitlen */ int voicenum; /* current voice number */ int has_voice_fields = 0; @@ -405,68 +409,138 @@ readsnump (p) } } -void -readsig (a, b, sig) - int *a, *b; - char **sig; -/* read time signature (meter) from M: field */ +/* [JA] 2020-12-10 */ +int check_power_of_two(int denom) { int t; - char c; /* [SS] 2015-08-19 */ + char error_message[80]; + t = denom; + while (t > 1) { + if (t % 2 != 0) { + snprintf(error_message, 80, "%d b is not a power of 2", denom); + event_error (error_message); + return 0; + } else { + t = t / 2; + } + } + return denom; +} +/* [JA] 2020-12-10 */ +/* read the numerator of a time signature in M: field + * + * abc standard 2.2 allows M:(a + b + c + ...)/d + * This indicates how note lenths within a bar are to be grouped. + * abc standard also allows a+b+c/d to mean the same thing but this + * goes against the convention that division takes precendence + * over addition i.e. a+b+c/d normally means a + b + (c/d). + */ +static int read_complex_has_timesig(char **place, timesig_details_t *timesig) +{ + int n; + int total; + int count; + int has_bracket = 0; + + if (**place == '(') { + *place = *place + 1; + has_bracket = 1; + skipspace(place); + } + count = 0; + total = 0; + skipspace(place); + while ((**place != '\0') && (isdigit(**place))) { + n = readnump(place); + timesig->complex_values[count] = n; + total = total + n; + count = count + 1; + if (count > 8) { + event_error("Too many parts to complex time (maximum 8)"); + return 0; + } + skipspace(place); + if (**place == '+') { + *place = *place + 1; + skipspace(place); + } + } + if (**place == ')') { + *place = *place + 1; /* advance over ')' */ + skipspace(place); + if (!has_bracket) { + event_warning("Missing ( in complex time"); + } + has_bracket = 0; + } + if (has_bracket) { + event_warning("Missing ) in complex time"); + } + /* we have reached the end of the numerator */ + timesig->num_values = count; + timesig->num = total; + if (timesig->num_values == 1) + { + timesig->type = TIMESIG_NORMAL; + } else { + timesig->type = TIMESIG_COMPLEX; + } + return 1; +} + +/* read time signature (meter) from M: field */ +void readsig (char **sig, timesig_details_t *timesig) +/* upgraded [JA] 2020-12-10 */ +{ + int valid_num; + + if ((strncmp (*sig, "none", 4) == 0) || + (strncmp (*sig, "None", 4) == 0)) { + timesig->num = 4; + timesig->denom = 4; + timesig->type = TIMESIG_FREE_METER; + return; + } /* [SS] 2012-08-08 cut time (C| or c|) is 2/2 not 4/4 */ - if ((*(*sig + 1) == '|') && ((**sig == 'C') || (**sig == 'c'))) - { - *a = 2; - *b = 2; - return; - } + if (((**sig == 'C') || (**sig == 'c')) && + (*(*sig + 1) == '|')) { + timesig->num = 2; + timesig->denom = 2; + timesig->type = TIMESIG_CUT; + return; + } + if ((**sig == 'C') || (**sig == 'c')) { + timesig->num = 4; + timesig->denom = 4; + timesig->type = TIMESIG_COMMON; + return; + } + valid_num = read_complex_has_timesig(sig, timesig); + if (!valid_num) { + /* An error message will have been generated by read_complex_has_timesig */ + timesig->num = 4; + timesig->denom = 4; + timesig->type = TIMESIG_FREE_METER; + return; + } - if ((**sig == 'C') || (**sig == 'c')) - { - *a = 4; - *b = 4; - return; - }; - *a = readnump (sig); - - /* [SS] 2015-08-19 */ - while ((int) **sig == '+') { + if ((int)**sig != '/') { + event_warning ("No / found, assuming denominator of 1"); + timesig->denom = 1; + } else { *sig = *sig + 1; - c = readnump (sig); - *a = *a + c; + skipspace(sig); + if (!isdigit(**sig)) { + event_warning ("Number not found for M: denominator"); } - - if ((int) **sig != '/') - { - event_error ("Missing / "); - } - else - { - *sig = *sig + 1; - }; - *b = readnump (sig); - if ((*a == 0) || (*b == 0)) - { - event_error ("Expecting fraction in form A/B"); - } - else - { - t = *b; - while (t > 1) - { - if (t % 2 != 0) - { - event_error ("divisor must be a power of 2"); - t = 1; - *b = 0; - } - else - { - t = t / 2; - }; - }; - }; + timesig->denom = readnump (sig); + } + if ((timesig->num == 0) || (timesig->denom == 0)) { + event_error ("Expecting fraction in form A/B"); + } else { + timesig->denom = check_power_of_two(timesig->denom); + } } void @@ -497,19 +571,32 @@ readlen (a, b, p) }; }; }; - t = *b; - while (t > 1) - { - if (t % 2 != 0) - { - event_warning ("divisor not a power of 2"); - t = 1; - } - else - { - t = t / 2; - }; - }; + *b = check_power_of_two(*b); +} + +/* [JA] 2020-12-10 */ +static void read_L_unitlen(int *num, int *denom, char **place) +{ + if (!isdigit(**place)) { + event_warning("No digit at the start of L: field"); + } + *num = readnump (place); + if (*num == 0) { + *num = 1; + } + if ((int)**place != '/') { + event_error ("Missing / "); + *denom = 1; + } else { + *place = *place + 1; + skipspace(place); + *denom = readnump (place); + } + if ((*num == 0) || (*denom == 0)) { + event_error ("Expecting fraction in form A/B"); + } else { + *denom = check_power_of_two(*denom); + } } void @@ -661,6 +748,16 @@ lcase (s) }; } +/* initialize a timesig structure to default values */ +void init_timesig(timesig_details_t *timesig) +{ + timesig->type = TIMESIG_FREE_METER; + timesig->num = 4; + timesig->denom = 4; + timesig->complex_values[0] = 4; + timesig->num_values = 1; +} + /* [JA] 2020-10-12 */ void init_voice_contexts (void) { @@ -677,11 +774,32 @@ void init_voice_contexts (void) } } +/* copy one timesig_details_t struct to another [JA] 2020-12-10 */ +void copy_timesig(timesig_details_t *destination, timesig_details_t *source) +{ + int i; + + destination->type = source->type; + destination->num = source->num; + destination->denom = source->denom; + for (i = 0; i < 8; i++) + { + destination->complex_values[i] = source->complex_values[i]; + } + destination->num_values = source->num_values; +} + /* [JA] 2020-10-12 */ /* called at the start of each tune */ static void reset_parser_status (void) { - voicenum = 0; + cleftype_t default_clef; + + init_timesig(&master_timesig); + get_standard_clef ("treble", &master_clef); /* default to treble clef */ + has_timesig = 0; + master_unitlen = -1; + voicenum = 1; has_voice_fields = 0; num_voices = 1; parserinchord = 0; @@ -708,7 +826,7 @@ print_voicecodes () } /* [JA] 2020-10-12 */ -int interpret_voice_label (char *s, int num) +int interpret_voice_label (char *s, int num, int *is_new) /* We expect a numeric value indicating the voice number. * The assumption is that these will ocuur in the order in which voices * appear, so that we have V:1, V:2, ... V:N if there are N voices. @@ -756,6 +874,7 @@ int interpret_voice_label (char *s, int num) /* declaring a new voice */ if (num == num_voices + 1) { + *is_new = 1; if (num_voices >= MAX_VOICES) { event_warning("Number of available voices exceeded"); @@ -763,6 +882,9 @@ int interpret_voice_label (char *s, int num) } num_voices = num_voices + 1; voicecode[num_voices - 1].label[0] = '\0'; + } else { + /* we are using a previously declared voice */ + *is_new = 0; } return num; } @@ -798,7 +920,10 @@ int interpret_voice_label (char *s, int num) */ if (has_voice_fields) { + *is_new = 1; num_voices++; + } else { + *is_new = 0; /* we have already started as voice 1 */ } strncpy (voicecode[num_voices - 1].label, code, 31); return num_voices; @@ -1222,7 +1347,12 @@ parsekey (str) parsed = parseclef (&s, word, &gotclef, clefstr, &newclef, &cgotoctave, &coctave); if (gotclef) { /* make clef an attribute of current voice */ - copy_clef (&voicecode[voicenum - 1].clef, &newclef); + if (inhead) { + copy_clef (&master_clef, &newclef); + } + if (inbody){ + copy_clef (&voicecode[voicenum - 1].clef, &newclef); + } } /* parseclef also scans the s string using readword(), placing */ /* the next token into the char array word[]. */ @@ -1420,6 +1550,15 @@ parsekey (str) return (gotkey); } +static void set_voice_from_master(int voice_num) +{ + voice_context_t *current_voice; + + current_voice = &voicecode[voice_num - 1]; + copy_timesig(¤t_voice->timesig, &master_timesig); + copy_clef(¤t_voice->clef, &master_clef); + current_voice->unitlen = master_unitlen; +} void parsevoice (s) @@ -1430,6 +1569,7 @@ parsevoice (s) char word[64]; /* 2017-10-11 */ int parsed; int coctave, cgotoctave; + int is_new = 0; vparams.transpose = 0; vparams.gottranspose = 0; @@ -1449,7 +1589,12 @@ parsevoice (s) if (isdigit(*s)) { /* [JA] 2020-10-12 */ num = readnump (&s); } - num = interpret_voice_label (s, num); + num = interpret_voice_label (s, num, &is_new); + if (is_new) { + /* declaring voice for the first time. + * initialize with values set in the tune header */ + set_voice_from_master(num); + } has_voice_fields = 1; skiptospace (&s); voicenum = num; @@ -2053,6 +2198,45 @@ free_abbreviations () }; } +/* function to resolve unit note length when the + * L: field is missing in the header [JA] 2020-12-10 + * + * From the abc standard 2.2: + * If there is no L: field defined, a unit note length is set by default, + * based on the meter field M:. This default is calculated by computing + * the meter as a decimal: if it is less than 0.75 the default unit note + * length is a sixteenth note; if it is 0.75 or greater, it is an eighth + * note. For example, 2/4 = 0.5, so, the default unit note length is a + * sixteenth note, while for 4/4 = 1.0, or 6/8 = 0.75, or 3/4= 0.75, + * it is an eighth note. For M:C (4/4), M:C| (2/2) and M:none (free meter), + * the default unit note length is 1/8. + * + */ +static void resolve_unitlen() +{ + if (master_unitlen == -1) + { + if (has_timesig == 0) + { + event_default_length(8); + master_unitlen = 8; + } + else + { + if (((4 * master_timesig.num)/master_timesig.denom) >= 3) + { + event_default_length(8); + master_unitlen = 8; + } + else + { + event_default_length(16); + master_unitlen = 16; + } + } + } +} + void parsefield (key, field) char key; @@ -2115,9 +2299,18 @@ parsefield (key, field) switch (key) { case 'K': + if (inhead) + { + /* First K: is the end of the header and the start of the body. + * make sure we set up default for unit length + * if L: fields was missing in the header. + */ + resolve_unitlen(); + /* set voice parameters using values from header */ + set_voice_from_master(1); + } foundkey = parsekey (place); - if (inhead || inbody) - { + if (inhead || inbody) { if (foundkey) { inbody = 1; @@ -2138,7 +2331,7 @@ parsefield (key, field) break; case 'M': { - int num, denom; + timesig_details_t timesig; /* strncpy (timesigstring, place, 16); [SS] 2011-08-19 */ #ifdef NO_SNPRINT @@ -2146,36 +2339,34 @@ parsefield (key, field) #else snprintf(timesigstring,sizeof(timesigstring),"%s",place); /* [SEG] 2020-06-07 */ #endif - if (strncmp (place, "none", 4) == 0) - /* converts 'M: none' to 'M: 4/4' otherwise a warning - * is returned if not a fraction [SS] */ - { - event_timesig (4, 4, 0); - } - else - { - readsig (&num, &denom, &place); - if ((*place == 's') || (*place == 'l')) - { - event_error ("s and l in M: field not supported"); - }; - if ((num != 0) && (denom != 0)) - { - /* [code contributed by Larry Myerscough 2015-11-5] - * Specify checkbars = 1 for numeric time signature - * or checkbars = 2 for 'common' time signature to - * remain faithful to style of input abc file. - */ - event_timesig (num, denom, 1 + ((*place == 'C') || (*place == 'c'))); - }; - }; - break; - }; + readsig (&place, ×ig); + if ((*place == 's') || (*place == 'l')) { + event_error ("s and l in M: field not supported"); + }; + if ((timesig.num == 0) || (timesig.denom == 0)) { + event_warning ("Invalid time signature ignored"); + } else { + if (inhead) { + /* copy timesig into master_timesig */ + copy_timesig(&master_timesig, ×ig); + } + if (inbody) { + /* update timesig in current voice */ + voice_context_t *current_voice; + + current_voice = &voicecode[voicenum - 1]; + copy_timesig(¤t_voice->timesig, ×ig); + } + event_timesig (×ig); + has_timesig = 1; + } + } + break; case 'L': { int num, denom; - readsig (&num, &denom, &place); + read_L_unitlen(&num, &denom, &place); if (num != 1) { event_error ("Default length must be 1/X"); @@ -2185,6 +2376,16 @@ parsefield (key, field) if (denom > 0) { event_length (denom); + if (inhead) + { + master_unitlen = denom; + } + if (inbody) { + voice_context_t *current_voice; + + current_voice = &voicecode[voicenum - 1]; + current_voice->unitlen = denom; + } } else { diff --git a/parseabc.h b/parseabc.h index 232f08a..35aa14b 100644 --- a/parseabc.h +++ b/parseabc.h @@ -39,7 +39,9 @@ typedef struct voice_context { char label[31]; int expect_repeat; int repeat_count; + timesig_details_t timesig; cleftype_t clef; + int unitlen; /* unitlen value currently active in voice */ } voice_context_t; #define MAX_VOICES 30 @@ -50,9 +52,6 @@ struct fraction { int denom; }; -extern int repcheck; /* allows backend to enable/disable repeat checking */ -extern voice_context_t voicecode[MAX_VOICES]; - #ifndef KANDR extern int readnump(char **p); extern int readsnump(char **p); @@ -69,6 +68,8 @@ extern int ismicrotone(char **p, int dir); extern void event_normal_tone(void); extern void print_inputline(void); extern void print_inputline_nolinefeed(void); +extern void init_timesig(timesig_details_t *timesig); +extern void copy_timesig(timesig_details_t *destination, timesig_details_t *source); #else extern int readnump(); extern int readsnump(); @@ -84,11 +85,19 @@ extern char *lookup_abbreviation(); extern int ismicrotone(); extern void event_normal_tone(); extern void print_inputline_nolinefeed(); +extern void init_timesig(); +extern void copy_timesig(); #endif extern void parseron(); extern void parseroff(); +/* variables exported by the parser */ extern int lineno; +extern int repcheck; /* allows backend to enable/disable repeat checking */ +extern voice_context_t voicecode[MAX_VOICES]; +extern timesig_details_t master_timesig; +extern int voicenum; +extern int parserinchord; /* event_X() routines - these are called from parseabc.c */ /* the program that uses the parser must supply these routines */ @@ -114,10 +123,11 @@ extern void event_part(char *s); extern void event_voice(int n, char *s, struct voice_params *params); extern void event_length(int n); +extern void event_default_length(int n); extern void event_blankline(void); extern void event_refno(int n); extern void event_tempo(int n, int a, int b, int rel, char *pre, char *post); -extern void event_timesig(int n, int m, int dochecking); +extern void event_timesig(timesig_details_t *timesig); extern void event_octave(int num, int local); extern void event_info_key(char *key, char *value); extern void event_info(char *s); @@ -183,6 +193,7 @@ extern void event_words(); extern void event_part(); extern void event_voice(); extern void event_length(); +extern void event_default_length(); extern void event_blankline(); extern void event_refno(); extern void event_tempo(); diff --git a/store.c b/store.c index d2d4d44..92ccced 100644 --- a/store.c +++ b/store.c @@ -93,6 +93,7 @@ void read_spec() void event_part() void event_voice() void event_length() +void event_default_length() void tempounits() int get_tempo_from_name() void event_tempo() @@ -185,7 +186,7 @@ int main() */ -#define VERSION "4.44 Ocober 19 2020 abc2midi" +#define VERSION "4.45 December 20 2020 abc2midi" /* enables reading V: indication in header */ #define XTEN1 1 @@ -2998,6 +2999,12 @@ int n; }; } +void event_default_length (n) +/* handles a missing L: field in the abc */ + int n; +{ +} + static void tempounits(t_num, t_denom) /* interprets Q: once default length is known */ int *t_num, *t_denom; @@ -3084,31 +3091,38 @@ char *post; }; } -void event_timesig(n, m, dochecking) +void event_timesig (timesig) /* handles an M: field M:n/m */ -int n, m, dochecking; + timesig_details_t *timesig; { + int dochecking; + + if (timesig->type == TIMESIG_FREE_METER) { + dochecking = 0; + } else { + dochecking = 1; + } if (dotune) { if (pastheader) { - addfeature(TIME, dochecking, n, m); - mtime_num = n; /* [SS] 2012-11-03 */ - mtime_denom = m; /* [SS] 2012-11-03 */ + addfeature (TIME, dochecking, timesig->num, timesig->denom); + mtime_num = timesig->num; /* [SS] 2012-11-03 */ + mtime_denom = timesig->denom; /* [SS] 2012-11-03 */ if (v != NULL) { - v->active_meter_num = n; /* [SS] 2012-11-08 */ - v->active_meter_denom = m; /* [SS] 2012-11-08 */ - } + v->active_meter_num = timesig->num; /* [SS] 2012-11-08 */ + v->active_meter_denom = timesig->denom; /* [SS] 2012-11-08 */ + } } else { - time_num = n; - time_denom = m; - mtime_num = n; /* [SS] 2012-11-03 */ - mtime_denom = m; /* [SS] 2012-11-03 */ + time_num = timesig->num; + time_denom = timesig->denom; + mtime_num = timesig->num; /* [SS] 2012-11-03 */ + mtime_denom = timesig->denom; /* [SS] 2012-11-03 */ /* [SS] 2015-03-11 */ /*if (v != NULL) { */ /* v->active_meter_num = n; [SS] 2012-11-08 */ /* v->active_meter_denom = m; [SS] 2012-11-08 */ /* } */ - meter_voice_update(n,m); /* [SS] 2015-03-11 */ + meter_voice_update (timesig->num, timesig->denom); /* [SS] 2015-03-11 */ timesigset = 1; barchecking = dochecking; }; @@ -4277,7 +4291,7 @@ int xoctave, n, m; int num, denom; int octave; int pitch; - int pitch_noacc; + int pitch_noacc = 0; int dummy; decotype[notes] = 0; /* [SS] 2012-07-02 no decoration */ diff --git a/structs.h b/structs.h index 982d9a0..dc1eac2 100644 --- a/structs.h +++ b/structs.h @@ -183,7 +183,7 @@ struct voice { /* following fields are initially inherited from tune */ cleftype_t* clef; struct key* keysig; - struct fract meter; + timesig_details_t timesig; struct atempo* tempo; /* place used while printing to keep track of progress through voice */ struct feature* lineplace; @@ -208,7 +208,7 @@ struct tune { char* origin; char* parts; struct llist notes; - struct fract meter; + timesig_details_t timesig; int barchecking; struct key* keysig; struct atempo* tempo; diff --git a/toabc.c b/toabc.c index 0365f56..e78cdae 100644 --- a/toabc.c +++ b/toabc.c @@ -21,7 +21,7 @@ /* back-end for outputting (possibly modified) abc */ -#define VERSION "2.12 October 19 2020 abc2abc" +#define VERSION "2.13 December 10 2020 abc2abc" /* for Microsoft Visual C++ 6.0 or higher */ #ifdef _MSC_VER @@ -58,6 +58,22 @@ struct fract { int denom; }; +typedef struct complex_barpoint { + struct fract break_here[8]; +} complex_barpoint_t; + +struct voicetype +{ /* information needed for each voice */ + int number; /* voice number from V: field */ + int barcount; + int foundbar; + struct abctext *currentline; + int bars_remaining; + int bars_complete; + int drumchan; + complex_barpoint_t bar_break; /* where to put spaces for complex time */ +} voice[MAX_VOICES]; + struct fract barlen; /* length of a bar as given by the time signature */ struct fract unitlen; /* unit length as given by the L: field */ struct fract count; /* length of bar so far */ @@ -65,6 +81,7 @@ struct fract prevcount; /* length of bar before last increment */ struct fract tuplefactor; /* factor associated with a tuple (N */ struct fract chordfactor; /* factor of the first note in a chord [PHDM] 2013-03-10 */ struct fract breakpoint; /* used to break bar into beamed sets of notes */ +complex_barpoint_t master_bar_break; int barno; /* number of bar within tune */ int newspacing; /* was -s option selected ? */ int barcheck; /* indicate -b and -r options selected */ @@ -73,7 +90,6 @@ int newbreaks; /* was -n option selected ? */ int nodouble_accidentals; int totalnotes, notecount; int bars_per_line; /* number supplied after -n option */ -int barcount; int tuplenotes, barend; int xinhead, xinbody; /* are we in head or body of abc tune ? */ int inmusic; /* are we in a line of notes (in the tune body) ? */ @@ -107,15 +123,6 @@ char* clef = ""; /* [SS] 2020-01-22 */ extern int nokey; /* signals no key signature assumed */ extern int nokeysig; /* signals -nokeys or -nokeysf option */ -struct voicetype { /* information needed for each voice */ - int number; /* voice number from V: field */ - int barcount; - int foundbar; - struct abctext* currentline; - int bars_remaining; - int bars_complete; - int drumchan; -} voice[MAX_VOICES]; int voicecount, this_voice, next_voice; enum abctype {field, bar, barline}; /* linestat is used by -n for deciding when to generate a newline */ @@ -442,6 +449,10 @@ int *a, *b; { int t, n, m; + if (*b == 0) { + printf("Error in reduce: %d / %d\n", *a, *b); + return; + } /* find HCF using Euclid's algorithm */ if (*a > *b) { n = *a; @@ -455,6 +466,10 @@ int *a, *b; n = m; m = t; }; + if (n == 0) { + printf("Error reducing %d / %d (n = 0) !!\n", *a, *b); + return; + } *a = *a/n; *b = *b/n; } @@ -1176,6 +1191,29 @@ char* s; inmusic = 0; } +/* initialize an abc2abc backend voice when we start using it */ +static void init_voice(int voice_index, int num) +{ + int i; + struct voicetype *v; + complex_barpoint_t *parent_breaks; + complex_barpoint_t *voice_breaks; + + v = &voice[voice_index]; + v->number = num; + v->drumchan = 0; + v->barcount = zero_barcount (&v->foundbar); + v->bars_complete = 0; + v->bars_remaining = bars_per_line; + parent_breaks = &master_bar_break; + voice_breaks = &v->bar_break; + for (i = 0; i < 8; i++) + { + voice_breaks->break_here[i].num = parent_breaks->break_here[i].num; + voice_breaks->break_here[i].denom = parent_breaks->break_here[i].denom; + } +} + int setvoice(num) int num; /* we need to keep track of current voice for new linebreak handling (-n) */ @@ -1198,11 +1236,7 @@ int num; } else { event_error("Number of voices exceeds static limit MAX_VOICES"); }; - voice[voice_index].number = num; - voice[voice_index].barcount = zero_barcount(&voice[voice_index].foundbar); - voice[voice_index].bars_complete = 0; - voice[voice_index].bars_remaining = bars_per_line; - voice[voice_index].drumchan = 0; + init_voice(voice_index, num); }; voice[voice_index].currentline = NULL; return(voice_index); @@ -1273,6 +1307,13 @@ int n; inmusic = 0; } +void event_default_length(n) + int n; +{ + unitlen.num = 1; + unitlen.denom = n; +} + void event_refno(n) int n; { @@ -1298,7 +1339,6 @@ int n; barlen.num = 0; barlen.denom = 1; inmusic = 0; - barcount = 0; } void event_tempo(n, a, b, relative, pre, post) @@ -1337,39 +1377,109 @@ char *post; inmusic = 0; } -void event_timesig(n, m, checkbars) -int n, m, checkbars; - -/* [code contributed by Larry Myerscough 2015-11-5] - * checkbars definition extended: - * 0=> no, - * 1=>yes, use numerical notation - * 2=>yes, use 'common' notation for 2/2 or 4/4 according to numerator - * */ +/* a complex time signature is something like M:(3+4)/8 + * this means the time signature is actually 7/8 or 7 eighth notes in a bar, + * but you should group the eighth notes as a set of 3, then a set of four. + * This function works out where to put spaces between notes for a + * complex time signature. + */ +static void set_complex_barpoint(timesig_details_t *timesig, + complex_barpoint_t *complex_barpoint) { - if (checkbars == 1) { - emit_int_sprintf("M:%d/", n); - emit_int(m); - } else if (checkbars == 2) { - emit_int_sprintf("M:C", n); - if (n != 4) emit_string("|"); - } else { - emit_string("M:none"); - barcheck = 0; - }; - barlen.num = n; - barlen.denom = m; - breakpoint.num = n; - breakpoint.denom = m; - if ((n == 9) || (n == 6)) { - breakpoint.num = 3; - breakpoint.denom = barlen.denom; - }; - if (n%2 == 0) { - breakpoint.num = barlen.num/2; - breakpoint.denom = barlen.denom; - }; - barend = n/breakpoint.num; + int i; + struct fract count; + + count.num = 0; + count.denom = 1; + for (i = 0; i < timesig->num_values; i++) { + int part_num; + int part_denom; + + /* get component of complex timesig as a fraction */ + part_num = timesig->complex_values[i]; + part_denom = timesig->denom; + /* add component of complex timesig to count */ + count.num = (count.num * part_denom) + (part_num * count.denom); + count.denom = (count.denom * part_denom); + reduce(&count.num, &count.denom); + /* set next suggested break to be at current count value */ + complex_barpoint->break_here[i].num = count.num; + complex_barpoint->break_here[i].denom = count.denom; + } +} + +/* M: field in the source. Support M:none, M:C and M:C| as well as + * normal M:n/m + */ +void event_timesig (timesig) + timesig_details_t *timesig; +{ + emit_string ( "M:"); + switch (timesig->type) { + default: + case TIMESIG_NORMAL: + emit_int ( timesig->num); + emit_string ( "/"); + emit_int ( timesig->denom); + break; + case TIMESIG_FREE_METER: + emit_string ( "none"); + barcheck = 0; + break; + case TIMESIG_COMMON: + emit_string ( "C"); + break; + case TIMESIG_CUT: + emit_string ( "C|"); + break; + case TIMESIG_COMPLEX: + { + int i; + + emit_char( '('); + for (i = 0; i < timesig->num_values; i++) + { + if (i > 0) { + emit_char( '+'); + } + emit_int ( timesig->complex_values[i]); + } + emit_char( ')'); + emit_string ( "/"); + emit_int ( timesig->denom); + if (xinhead) { + set_complex_barpoint( + &master_timesig, &master_bar_break); + } else if (xinbody) { + struct voicetype *toabc_voice; + voice_context_t *current_voice; + + current_voice = &voicecode[this_voice]; + toabc_voice = &voice[this_voice]; + set_complex_barpoint( + ¤t_voice->timesig, &toabc_voice->bar_break); + } + } + break; + } + barlen.num = timesig->num; + barlen.denom = timesig->denom; + if ((timesig->type == TIMESIG_NORMAL) || + (timesig->type == TIMESIG_COMMON) || + (timesig->type == TIMESIG_CUT)) { + + breakpoint.num = timesig->num; + breakpoint.denom = timesig->denom; + if ((timesig->num == 9) || (timesig->num == 6)) { + breakpoint.num = 3; + breakpoint.denom = barlen.denom; + }; + if (timesig->num % 2 == 0) { + breakpoint.num = barlen.num / 2; + breakpoint.denom = barlen.denom; + }; + barend = timesig->num / breakpoint.num; + } inmusic = 0; } @@ -1414,18 +1524,10 @@ static void start_tune() if (barlen.num == 0) { /* generate missing time signature */ event_linebreak(); - event_timesig(4, 4, 1); + event_timesig (&master_timesig); inmusic = 0; }; - if (unitlen.num == 0) { - if ((float) barlen.num / (float) barlen.denom < 0.75) { - unitlen.num = 1; - unitlen.denom = 16; - } else { - unitlen.num = 1; - unitlen.denom = 8; - }; - }; + /* missing unitlen handled by event_default_length */ voicecount = 0; this_voice = setvoice(1); next_voice = this_voice; @@ -1824,10 +1926,13 @@ int type, n; void event_tuple(n, q, r) int n, q, r; { - emit_int_sprintf("(%d", n); if (tuplenotes != 0) { event_error("tuple within tuple not allowed"); }; + if (newspacing) { + emit_char(' '); + } + emit_int_sprintf("(%d", n); if (q != 0) { emit_int_sprintf(":%d", q); tuplefactor.num = q; @@ -1839,6 +1944,11 @@ int n, q, r; tuplenotes = n; }; } else { + if (r != 0) { + /* cope with the case when q is zero, but r is not zero. */ + emit_string ("::"); + emit_int (r); + } tuplenotes = n; tuplefactor.denom = n; if ((n == 2) || (n == 4) || (n == 8)) tuplefactor.num = 3; @@ -2239,8 +2349,50 @@ int *octave, *mult; } } +/* decide whether or not to put a space after note. + * part of the logic for newspacing option */ +static void consider_break_after_note(int previous_tuplenotes) +{ + struct fract barpoint; + voice_context_t *current_voice; + current_voice = &voicecode[voicenum - 1]; + if (previous_tuplenotes == 1) { + /* if the note just output was the last one in a tuple, generate space */ + emit_string (" "); + return; + } + if (tuplenotes > 0) { + /* never break beaming within a tuple */ + return; + } + if (!parserinchord) { + if (current_voice->timesig.type == TIMESIG_COMPLEX) { + struct voicetype *v; + int i; + v = &voice[voicenum - 1]; + /* try all the pre-calculated breakpoints */ + for (i = 0; i < current_voice->timesig.num_values - 1; i++) { + if ((v->bar_break.break_here[i].num == + count.num) && + (v->bar_break.break_here[i].denom == + count.denom)) + { + emit_string (" "); + } + } + } else { + barpoint.num = count.num * breakpoint.denom; + barpoint.denom = breakpoint.num * count.denom; + reduce (&barpoint.num, &barpoint.denom); + if ((barpoint.denom == 1) && (barpoint.num != 0) && + (barpoint.num != barend)) { + emit_string (" "); + } + } + } +} void event_note1(decorators, clef, xaccidental, xmult, xnote, xoctave, n, m) int decorators[DECSIZE]; @@ -2255,6 +2407,7 @@ int xoctave, n, m; int mult; char accidental, note; int octave; + int prev_tuplenotes; mult = 0; /* [SS] 2006-10-27 */ if (transpose == 0 || drumchan) { @@ -2347,17 +2500,12 @@ int xoctave, n, m; chordfactor.denom = m; } }; + prev_tuplenotes = tuplenotes; if ((!ingrace) && (!inchord)) { addunits(n, m); }; if (newspacing) { - barpoint.num = count.num * breakpoint.denom; - barpoint.denom = breakpoint.num * count.denom; - reduce(&barpoint.num, &barpoint.denom); - if ((barpoint.denom == 1) && (barpoint.num != 0) && - (barpoint.num != barend)) { - emit_string(" "); - }; + consider_break_after_note(prev_tuplenotes); }; } diff --git a/yapstree.c b/yapstree.c index ca2020f..c193b79 100644 --- a/yapstree.c +++ b/yapstree.c @@ -22,7 +22,7 @@ /* yapstree.c - back-end for abc parser. */ /* generates a data structure suitable for typeset music */ -#define VERSION "1.85 November 07 2020 yaps" +#define VERSION "1.86 December 10 2020 yaps" #include #ifdef USE_INDEX #define strchr index @@ -129,6 +129,16 @@ static struct fract* newfract(int a, int b) return(f); } +static timesig_details_t *newtimesig(void) +/* create an initialized time signature */ +{ + timesig_details_t *new_timesig; + + new_timesig = (timesig_details_t *)checkmalloc (sizeof (timesig_details_t)); + init_timesig(new_timesig); + return new_timesig; +} + static struct slurtie* newslurtie() /* create a new slur/tie data structure */ { @@ -437,7 +447,7 @@ static void beamitem(featuretype mytype, void* newitem, struct feature* x) /* deal with beaming here */ if (cv->ingrace) { - if (mytype == NOTE) { + if ((mytype == NOTE) && (newitem != NULL)) { n = newitem; n->stemup = 1; if (cv->gracebeamroot == NULL) { @@ -820,7 +830,7 @@ static struct voice* newvoice(int n) v->slurcount = 0; v->barno = 0; v->barchecking = thetune.barchecking; - setfract(&v->barlen, thetune.meter.num, thetune.meter.denom); + setfract (&v->barlen, thetune.timesig.num, thetune.timesig.denom); v->clef = newclef(&thetune.clef); if (thetune.keysig == NULL) { printf("Trying to set up voice with no key signature\n"); @@ -831,7 +841,7 @@ static struct voice* newvoice(int n) }; v->tempo = NULL; setfract(&v->barcount, 0, 1); - setfract(&v->meter, thetune.meter.num, thetune.meter.denom); + copy_timesig(&v->timesig, &thetune.timesig); v->lastnote = NULL; v->laststart = NULL; v->lastend = NULL; @@ -895,7 +905,7 @@ static void init_tune(struct tune* t, int x) t->parts = NULL; init_llist(&t->notes); init_llist(&t->voices); - setfract(&t->meter, 0, 1); + init_timesig(&t->timesig); setfract(&t->unitlen, 0, 1); t->cv = NULL; t->keysig = NULL; @@ -1411,6 +1421,9 @@ static void tidy_ties() void event_startmusicline() /* We are at the start of a line of abc notes */ { + voice_context_t *parser_voice; + + parser_voice = &voicecode[voicenum - 1]; cv->linestart = addnumberfeature(MUSICLINE, 0); if (cv->more_lyrics != 0) { event_error("Missing continuation w: field"); @@ -1421,7 +1434,11 @@ void event_startmusicline() addfeature(KEY, newkey(cv->keysig->name, cv->keysig->sharps, cv->keysig->map, cv->keysig->mult)); if ((cv->line == header)||(cv->changetime)) { - addfeature(TIME, newfract(cv->meter.num, cv->meter.denom)); + timesig_details_t *timesig; + + timesig = newtimesig(); + copy_timesig(timesig, &parser_voice->timesig); + addfeature (TIME, timesig); cv->changetime = 0; }; cv->line = midline; @@ -1861,6 +1878,13 @@ int n; }; } +void event_default_length (n) + int n; +/* Handles a missing L: field */ +{ + event_length(n); +} + void event_refno(n) int n; /* A reference field (X: ) has been encountered. This indicates the start */ @@ -1920,29 +1944,34 @@ char *post; /* text after tempo */ }; } -void event_timesig(n, m, checkbars) -int n, m, checkbars; +void event_timesig (timesig) + timesig_details_t *timesig; /* A time signature (M: ) has been encountered in the abc */ { + int checkbars; + + if (timesig->type == TIMESIG_FREE_METER) { + checkbars = 0; + } else { + checkbars = 1; + } if (xinhead) { - setfract(&thetune.meter, n, m); + copy_timesig(&thetune.timesig, timesig); thetune.barchecking = checkbars; } else { if (xinbody) { - if (checkbars == 1) { - addfeature(TIME, newfract(n,m)); - setfract(&cv->meter, n, m); - setfract(&cv->barlen, n, m); - if (cv->line != midline) { - cv->changetime = 1; - }; - cv->barchecking = 1; - } else { - cv->barchecking = 0; - }; + timesig_details_t *time_timesig; + + time_timesig = newtimesig(); + copy_timesig(time_timesig, timesig); + addfeature (TIME, time_timesig); + setfract (&cv->barlen, timesig->num, timesig->denom); + if (cv->line != midline) { + cv->changetime = 1; + } } else { if (!suppress) { - event_warning("M: field outside tune ignored"); + event_warning ("M: field outside tune ignored"); }; }; }; @@ -2029,22 +2058,8 @@ static void start_body() /* default values for anything not explicitly declared */ { parseron(); - if (thetune.meter.num == 0) { - event_warning("no M: field, assuming 4/4"); - /* generate missing time signature */ - event_timesig(4, 4, 1); - event_linebreak(); - }; - if (thetune.unitlen.num == 0) { - event_warning("no L: field, using default rule"); - if ((double) thetune.meter.num / (double) thetune.meter.denom < 0.75) { - /*setfract(&thetune.unitlen, 1, 16); [SS] 2004-09-06 */ - event_length(16); - } else { - /* setfract(&thetune.unitlen, 1, 8); */ - event_length(8); - }; - }; + /* missing M: is handled by setting default M:none */ + /* missing L: is handled by event_default_length */ if (thetune.tempo != NULL) { resolve_tempo(thetune.tempo, &thetune.unitlen); };