From e9c2aee88be64534211b893a490043f89028e1ec Mon Sep 17 00:00:00 2001 From: Seymour Shlien Date: Tue, 12 Oct 2021 09:09:45 -0400 Subject: [PATCH] 2021.10.11 --- VERSION | 2 +- doc/CHANGES | 47 ++++++++++++ doc/readme.txt | 4 +- parseabc.c | 203 ++++++++++++++++++++++++++++++++++++++++++++++++- store.c | 2 +- 5 files changed, 252 insertions(+), 6 deletions(-) diff --git a/VERSION b/VERSION index 4844d25..ed634b2 100644 --- a/VERSION +++ b/VERSION @@ -1,2 +1,2 @@ -2021 September 15 2021 +2021 October 11 2021 diff --git a/doc/CHANGES b/doc/CHANGES index 7792b11..febcfcd 100644 --- a/doc/CHANGES +++ b/doc/CHANGES @@ -14379,3 +14379,50 @@ The chord associated with the 'b' gchord code was missing. b and f codes were indistinguishable. Fix: removed the break in the switch statement for case b: + +October 11 2021 + +abc2midi new feature: +In compliance with the ABC draft standard 2.2, I introduced additional +K: and V: options for transposition. You can now indicate the number +of semitones to transpose by giving the original note and the +corresponding transposed note in the K: or V: field using either +shift = note1note2 +sound = note1note2 +instrument = note1/note2 +The number of semitones is determined by the difference note2 - note1. + +Abcm2ps and abc2svg recognize this command, but abc2abc, yaps, and +abcmatch ignore this new option. + +http://abcnotation.com/wiki/abc:standard:v2.2#transposition + +Here is a test sample: + +X:1 +T: standard 2.2 transposition +M: 4/4 +L: 1/4 +K: C +V:1 +Bcde|Bcde| +V:2 +Bcde|Bcde| +V:1 shift = Bc +Bcde|Bcde| +V:2 +Bcde|Bcde| +V:1 sound = dc +Bcde|Bcde| +V:2 +Bcde|Bcde| +V:1 instrument = c/^D +Bcde|Bcde| +V:2 +Bcde|Bcde| +V:1 transpose = 0 +Bcde|Bcde| +V:2 shift=DE, +Bcde|Bcde| + + diff --git a/doc/readme.txt b/doc/readme.txt index 57a4051..8939921 100644 --- a/doc/readme.txt +++ b/doc/readme.txt @@ -1,7 +1,7 @@ abcMIDI : abc <-> MIDI conversion utilities midi2abc version 3.48 June 27 2021 -abc2midi version 4.60 September 15 2021 +abc2midi version 4.61 October 11 2021 abc2abc version 2.15 May 25 2021 yaps version 1.87 May 25 2021 abcmatch version 1.79 May 25 2021 @@ -14,7 +14,7 @@ J.R.Allwright@westminster.ac.uk University of Westminster, London, UK -June 2021 +October 2021 Seymour Shlien Ottawa, Canada diff --git a/parseabc.c b/parseabc.c index b5c36c3..a49aa5d 100644 --- a/parseabc.c +++ b/parseabc.c @@ -81,6 +81,7 @@ extern char *malloc (); extern char *strchr (); #endif +int note2midi (char** s); int lineno; int parsing_started = 0; int parsing, slur; @@ -939,8 +940,8 @@ int interpret_voice_label (char *s, int num, int *is_new) return num_voices; } -/* The following three functions parseclefs, parsetranspose, - * parseoctave are used to parse the K: field which not +/* The following four functions parseclefs, parsetranspose, + * parsesound, parseoctave are used to parse the K: field which not * only specifies the key signature but also other descriptors * used for producing a midi file or postscript file. * @@ -1019,6 +1020,49 @@ parsetranspose (s, word, gottranspose, transpose) return 1; }; +/* [SS] 2021-10-11 */ +int +parsesound (s, word, gottranspose, transpose) +/* parses string sound = + shift = + instrument = note1note2 or note1/note2 + for parsekey() or parsevoice() + and returns the transpose value +*/ + char **s; + char *word; + int *gottranspose; + int *transpose; + { + int p1,p2; + if (casecmp(word,"sound") != 0 + && casecmp(word,"shift") != 0 + && casecmp(word,"instrument") != 0 + ) + return 0; + skipspace (s); + if (**s != '=') + { + event_error ("sound must be followed by '='"); + return 0; + } else { + *s = *s + 1; + skipspace (s); + p1 = note2midi (s); + /* printf("midi note = %d\n",p1); */ + p2 = note2midi (s); + if (p2 == p1) { + *gottranspose = 0; + *transpose = 0; + } else { + /* printf("midi note = %d\n",p2); */ + *transpose = p2 - p1; + /* printf("transpose = %d\n",*transpose); */ + *gottranspose = 1; + } + } + return 1; + } int parseoctave (s, word, gotoctave, octave) @@ -1382,6 +1426,9 @@ parsekey (str) if (!parsed) parsed = parseoctave (&s, word, &gotoctave, &octave); + if (!parsed) + parsed = parsesound (&s, word, &gottranspose, &transpose); + if ((parsed == 0) && (casecmp (word, "Hp") == 0)) { sf = 2; @@ -1623,6 +1670,8 @@ parsevoice (s) &vparams.transpose); if (!parsed) parsed = parseoctave (&s, word, &vparams.gotoctave, &vparams.octave); + if (!parsed) + parsed = parsesound (&s, word, &vparams.gottranspose, &vparams.transpose); if (!parsed) parsed = parsename (&s, word, &vparams.gotname, vparams.namestring, @@ -2661,6 +2710,156 @@ static void check_and_call_bar(int bar_type, char *replist) event_bar (bar_type, replist); } + +/* [SS] 2021-10-11 */ +static int pitch2midi(note, accidental, mult, octave ) +/* computes MIDI pitch for note. +*/ +char note, accidental; +int mult, octave; +{ + int p; + char acc; + int mul, noteno; + int pitch; + + static int scale[7] = {0, 2, 4, 5, 7, 9, 11}; + + static const char *anoctave = "cdefgab"; + acc = accidental; + mul = mult; + noteno = (int)note - 'a'; + + p = (int) ((long) strchr(anoctave, note) - (long) anoctave); + p = scale[p]; + if (acc == '^') p = p + mul; + if (acc == '_') p = p - mul; + p = p + 12*octave + 60; +return p; +} + + + +/* [SS] 2021-10-11 */ +int +note2midi (char** s) +{ +/* for implementing sound=, shift= and instrument= key signature options */ + +/* check for accidentals */ +char note, accidental; +char msg[80]; +int octave; +int mult; +int pitch; +/*printf("note2midi: %c\n",**s); */ +if (**s == '\0') return 72; +mult = 1; +accidental = ' '; +switch (**s) + { + case '_': + accidental = **s; + *s = *s + 1; + if (**s == '_') + { + *s = *s + 1; + mult = 2; + }; + break; + case '^': + accidental = **s; + *s = *s + 1; + if (**s == '^') + { + *s = *s + 1; + mult = 2; + }; + break; + case '=': + accidental = **s; + *s = *s + 1; + break; + default: + break; + }; + + if ((**s >= 'a') && (**s <= 'g')) + { + note = **s; + octave = 1; + *s = *s + 1; + while ((**s == '\'') || (**s == ',') || (**s == '/')) + { + if (**s == '\'') + { + octave = octave + 1; + *s = *s + 1; + }; + if (**s == ',') + { + sprintf (msg, "Bad pitch specifier , after note %c", note); + event_error (msg); + octave = octave - 1; + *s = *s + 1; + }; + if (**s == '/') + { + /* skip / which occurs in instrument = command */ + *s = *s + 1; +/* printf("note = %c accidental = %c mult = %d octave= %d \n",note,accidental,mult,octave); */ + pitch = pitch2midi(note, accidental, mult, octave); + return pitch; + } + }; + } + else + { + octave = 0; + if ((**s >= 'A') && (**s <= 'G')) + { + note = **s + 'a' - 'A'; + *s = *s + 1; + while ((**s == '\'') || (**s == ',') || (**s == '/')) + { + if (**s == ',') + { + octave = octave - 1; + *s = *s + 1; + }; + if (**s == '\'') + { + sprintf (msg, "Bad pitch specifier ' after note %c", + note + 'A' - 'a'); + event_error (msg); + octave = octave + 1; + *s = *s + 1; + }; + if (**s == '/') + { + /* skip / which occurs in instrument = command */ + *s = *s + 1; +/* printf("note = %c accidental = %c mult = %d octave= %d \n",note,accidental,mult,octave); */ + pitch = pitch2midi(note, accidental, mult, octave); + return pitch; + }; + }; + }; + /* printf("note = %c accidental = %c mult = %d octave= %d \n",note,accidental,mult,octave); */ + } + if (note == ' ') + { + event_error ("Malformed note : expecting a-g or A-G"); + } + pitch = pitch2midi(note, accidental, mult, octave); + return pitch; +} + + + + + + void parsemusic (field) char *field; diff --git a/store.c b/store.c index 35e546c..0c85388 100644 --- a/store.c +++ b/store.c @@ -186,7 +186,7 @@ int main() */ -#define VERSION "4.60 September 15 2021 abc2midi" +#define VERSION "4.61 October 11 2021 abc2midi" /* enables reading V: indication in header */ #define XTEN1 1