
| Current Path : /proc/thread-self/root/usr/local/lib/python3.8/dist-packages/iftlib/abc/abcmidi/ |
Linux ift1.ift-informatik.de 5.4.0-216-generic #236-Ubuntu SMP Fri Apr 11 19:53:21 UTC 2025 x86_64 |
| Current File : //proc/thread-self/root/usr/local/lib/python3.8/dist-packages/iftlib/abc/abcmidi/store.c |
/*
* abc2midi - program to convert abc files to MIDI files.
* Copyright (C) 1999 James Allwright
* e-mail: J.R.Allwright@westminster.ac.uk
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
/* store.c
* This file handles all event_X() calls from parseabc.c and stores them
* in various arrays and data structures. The midi is then generated by
* calling mfwrite() which in turn uses the routine writetrack().
* This file is part of abc2midi.
*
* James Allwright
*
* Macintosh Port 30th July 1996
* Wil Macaulay (wil@syndesis.com)
*/
#include "abc.h"
#include "parseabc.h"
#include "parser2.h"
#include "midifile.h"
#include "genmidi.h"
#include <stdio.h>
#ifdef __MWERKS__
#define __MACINTOSH__ 1
#endif /* __MWERKS__ */
#ifdef __MACINTOSH__
int setOutFileCreator(char *fileName,unsigned long theType,
unsigned long theCreator);
#endif /* __MACINTOSH__ */
/* define USE_INDEX if your C libraries have index() instead of strchr() */
#ifdef USE_INDEX
#define strchr index
#endif
#ifdef ANSILIBS
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#else
extern char* strchr();
extern void reduce();
#endif
#define MAXLINE 500
#define INITTEXTS 20
#define INITWORDS 20
#define MAXCHANS 16
/* global variables grouped roughly by function */
FILE *fp;
/* parsing stage */
int tuplecount, tfact_num, tfact_denom, tnote_num, tnote_denom;
int specialtuple;
int gracenotes;
int headerpartlabel;
int dotune, pastheader;
int hornpipe, last_num, last_denom;
int timesigset;
int retain_accidentals;
int ratio_a, ratio_b;
struct voicecontext {
/* maps of accidentals for each stave line */
char basemap[7], workmap[7];
int basemul[7], workmul[7];
int default_length;
int voiceno;
int indexno;
int hasgchords;
int haswords;
int inslur;
int ingrace;
int octaveshift;
/* chord handling */
int inchord, chordcount;
int chord_num, chord_denom;
/* details of last 2 notes/chords to apply length-modifiers to */
int laststart, lastend, thisstart, thisend;
/* broken rythm handling */
int brokentype, brokenmult, brokenpending;
int broken_stack[7];
struct voicecontext* next;
};
struct voicecontext global;
struct voicecontext* v;
struct voicecontext* head;
int voicecount;
/* storage structure for strings */
int maxtexts = INITTEXTS;
char** atext;
int ntexts = 0;
/* Named guitar chords */
char chordname[MAXCHORDNAMES][8];
int chordnotes[MAXCHORDNAMES][6];
int chordlen[MAXCHORDNAMES];
int chordsnamed = 0;
/* general purpose storage structure */
int maxnotes;
int *pitch, *num, *denom;
featuretype *feature;
int notes;
int verbose = 0;
int titlenames = 0;
int got_titlename;
int namelimit;
int xmatch;
int sf, mi;
int gchordvoice, wordvoice, drumvoice;
int gchordtrack, drumtrack;
/* Part handling */
struct vstring part;
extern int parts, partno, partlabel;
extern int part_start[26], part_count[26];
int voicesused;
/* Tempo handling (Q: field) */
int time_num, time_denom;
int mtime_num, mtime_denom;
long tempo;
int tempo_num, tempo_denom;
int relative_tempo, Qtempo;
extern int division;
extern int div_factor;
/* output file generation */
int userfilename = 0;
char *outname = NULL;
char *outbase = NULL;
int check;
int ntracks;
/* bar length checking */
extern int bar_num, bar_denom;
int barchecking;
/* generating MIDI output */
int middle_c;
extern int channels[MAXCHANS + 3];
extern int global_transpose;
extern int additive;
int gfact_num, gfact_denom;
/* karaoke handling */
int karaoke, wcount;
char** words;
int maxwords = INITWORDS;
extern long writetrack();
static struct voicecontext* newvoice(n)
/* allocate and initialize the data for a new voice */
int n;
{
struct voicecontext *s;
int i;
s = (struct voicecontext*) checkmalloc(sizeof(struct voicecontext));
voicecount = voicecount + 1;
s->voiceno = n;
s->indexno = voicecount;
s->default_length = global.default_length;
s->hasgchords = 0;
s->haswords = 0;
s->inslur = 0;
s->ingrace = 0;
s->inchord = 0;
s->chordcount = 0;
s->laststart = -1;
s->lastend = -1;
s->thisstart = -1;
s->thisend = -1;
s->brokenpending = -1;
s->next = NULL;
for (i=0; i<7; i++) {
s->basemap[i] = global.basemap[i];
s->basemul[i] = global.basemul[i];
s->workmap[i] = global.workmap[i];
s->workmul[i] = global.workmul[i];
};
s->octaveshift = global.octaveshift;
return(s);
}
static struct voicecontext* getvoicecontext(n)
/* find the data structure for a given voice number */
int n;
{
struct voicecontext *p;
struct voicecontext *q;
p = head;
q = NULL;
while ((p != NULL) && (p->voiceno != n)) {
q = p;
p = p->next;
};
if (p == NULL) {
p = newvoice(n);
if (q != NULL) {
q->next = p;
};
};
if (head == NULL) {
head = p;
};
return(p);
}
static void clearvoicecontexts()
/* free up all the memory allocated to voices */
{
struct voicecontext *p;
struct voicecontext *q;
p = head;
while (p != NULL) {
q = p->next;
free(p);
p = q;
};
head = NULL;
}
static int getchordnumber(s)
/* looks through list of known chords for chord name given in s */
char *s;
{
int i;
int chordnumber;
chordnumber = 0;
i = 1;
while ((i <= chordsnamed) && (chordnumber == 0)) {
if (strcmp(s, chordname[i]) == 0) {
chordnumber = i;
} else {
i = i + 1;
};
};
return(chordnumber);
}
static void addchordname(s, len, notes)
/* adds chord name and note set to list of known chords */
char *s;
int notes[];
int len;
{
int i, j, done;
if (strlen(s) > 7) {
event_error("Chord name cannot exceed 7 characters");
return;
};
if (len > 6) {
event_error("Named chord cannot have more than 6 notes");
return;
};
i = 0;
done = 0;
while ((i<chordsnamed) && (!done)) {
if (strcmp(s, chordname[i]) == 0) {
/* change chord */
chordlen[i] = len;
for (j=0; j<len; j++) {
chordnotes[i][j] = notes[j];
};
done = 1;
} else {
i = i + 1;
};
};
if (!done) {
if (chordsnamed >= MAXCHORDNAMES-1) {
event_error("Too many Guitar Chord Names used");
} else {
chordsnamed = chordsnamed + 1;
strcpy(chordname[chordsnamed], s);
chordlen[chordsnamed] = len;
for (j=0; j<len; j++) {
chordnotes[chordsnamed][j] = notes[j];
};
};
};
}
static void setup_chordnames()
/* set up named guitar chords */
{
static int list_Maj[3] = {0, 4, 7};
static int list_m[3] = {0, 3, 7};
static int list_7[4] = {0, 4, 7, 10};
static int list_m7[4] = {0, 3, 7, 10};
static int list_maj7[4] = {0, 4, 7, 11};
static int list_M7[4] = {0, 4, 7, 11};
static int list_6[4] = {0, 4, 7, 9};
static int list_m6[4] = {0, 3, 7, 9};
static int list_aug[3] = {0, 4, 8};
static int list_plus[3] = {0, 4, 8};
static int list_aug7[4] = {0, 4, 8, 10};
static int list_dim[3] = {0, 3, 6};
static int list_dim7[4] = {0, 3, 6, 9};
static int list_9[5] = {0, 4, 7, 10, 2};
static int list_m9[5] = {0, 3, 7, 10, 2};
static int list_maj9[5] = {0, 4, 7, 11, 2};
static int list_M9[5] = {0, 4, 7, 11, 2};
static int list_11[6] = {0, 4, 7, 10, 2, 5};
static int list_dim9[5] = {0, 4, 7, 10, 13};
static int list_sus[3] = {0, 5, 7};
static int list_sus9[3] = {0, 2, 7};
static int list_7sus4[4] = {0, 5, 7, 10};
static int list_7sus9[4] = {0, 2, 7, 10};
static int list_5[2] = {0, 7};
addchordname("", 3, list_Maj);
addchordname("m", 3, list_m);
addchordname("7", 4, list_7);
addchordname("m7", 4, list_m7);
addchordname("maj7", 4, list_maj7);
addchordname("M7", 4, list_M7);
addchordname("6", 4, list_6);
addchordname("m6", 4, list_m6);
addchordname("aug", 3, list_aug);
addchordname("+", 3, list_plus);
addchordname("aug7", 4, list_aug7);
addchordname("dim", 3, list_dim);
addchordname("dim7", 4, list_dim7);
addchordname("9", 5, list_9);
addchordname("m9", 5, list_m9);
addchordname("maj9", 5, list_maj9);
addchordname("M9", 5, list_M9);
addchordname("11", 6, list_11);
addchordname("dim9", 5, list_dim9);
addchordname("sus", 3, list_sus);
addchordname("sus9", 3, list_sus9);
addchordname("7sus4", 4, list_7sus4);
addchordname("7sus9", 4, list_7sus9);
addchordname("5", 2, list_5);
}
void event_init(argc, argv, filename)
/* this routine is called first by parseabc.c */
int argc;
char* argv[];
char **filename;
{
int j;
/* look for code checking option */
if (getarg("-c", argc, argv) != -1) {
check = 1;
} else {
check = 0;
};
/* look for filename-from-tune-titles option */
namelimit = 252;
titlenames = 0;
if (getarg("-t", argc, argv) != -1) {
titlenames = 1;
namelimit = 8;
};
/* look for verbose option */
if (getarg("-v", argc, argv) != -1) {
verbose = 1;
} else {
verbose = 0;
};
maxnotes = 500;
/* allocate space for notes */
pitch = checkmalloc(maxnotes*sizeof(int));
num = checkmalloc(maxnotes*sizeof(int));
denom = checkmalloc(maxnotes*sizeof(int));
feature = (featuretype*) checkmalloc(maxnotes*sizeof(featuretype));
/* and for text */
atext = (char**) checkmalloc(maxtexts*sizeof(char*));
words = (char**) checkmalloc(maxwords*sizeof(char*));
if ((getarg("-h", argc, argv) != -1) || (argc < 2)) {
printf("abc2midi version 1.24\n");
printf("Usage : abc2midi <abc file> [reference number] [-c] [-v] ");
printf("[-o filename]\n");
printf(" [-t] [-n <value>]\n");
printf(" [reference number] selects a tune\n");
printf(" -c selects checking only\n");
printf(" -v selects verbose option\n");
printf(" -o <filename> selects output filename\n");
printf(" -t selects filenames derived from tune titles\n");
printf(" -n <limit> set limit for length of filename stem\n");
printf(" The default action is to write a MIDI file for each abc tune\n");
printf(" with the filename <stem>N.mid, where <stem> is the filestem\n");
printf(" of the abc file and N is the tune reference number. If the -o\n");
printf(" option is used, only one file is written. This is the tune\n");
printf(" specified by the reference number or, if no reference number\n");
printf(" is given, the first tune in the file.\n");
exit(0);
} else {
xmatch = 0;
if ((argc >= 3) && (isdigit(*argv[2]))) {
xmatch = readnumf(argv[2]);
};
*filename = argv[1];
outbase = addstring(argv[1]);
for (j = 0; j<strlen(outbase); j++) {
if (outbase[j] == '.') outbase[j] = '\0';
};
};
/* look for filename stem limit */
j = getarg("-n", argc, argv);
if (j != -1) {
if (argc >= j+1) {
namelimit = 0;
sscanf(argv[j], "%d", &namelimit);
if ((namelimit < 3) || (namelimit > 252)) {
event_fatal_error("filename stem limit must be in the range 3 - 252");
};
} else {
event_error("No number given, ignoring -n option");
};
};
/* look for user-supplied output filename */
j = getarg("-o", argc, argv);
if (j != -1) {
if (argc >= j+1) {
outname = addstring(argv[j]);
userfilename = 1;
if (xmatch == 0) {
xmatch = -1;
};
if (titlenames == 1) {
event_warning("-o option over-rides -t option");
titlenames = 0;
};
} else {
event_error("No filename given, ignoring -o option");
};
};
dotune = 0;
parseroff();
setup_chordnames();
}
void event_text(s)
/* text found in abc file */
char *s;
{
char msg[200];
sprintf(msg, "Ignoring text: %s", s);
event_warning(msg);
}
void event_x_reserved(p)
/* reserved character H-Z found in abc file */
char p;
{
char msg[200];
sprintf(msg, "Ignoring reserved character %c", p);
event_warning(msg);
}
void event_abbreviation(symbol, string, container)
/* abbreviation encountered - this is handled within the parser */
char symbol;
char *string;
char container;
{
}
void event_tex(s)
/* TeX command found - ignore it */
char *s;
{
}
void event_fatal_error(s)
/* print error message and halt */
char *s;
{
event_error(s);
exit(1);
}
void event_error(s)
/* generic error handler */
char *s;
{
#ifdef NOFTELL
extern int nullpass;
if (nullpass != 1) {
printf("Error in line %d : %s\n", lineno, s);
};
#else
printf("Error in line %d : %s\n", lineno, s);
#endif
}
void event_warning(s)
/* generic warning handler - for flagging possible errors */
char *s;
{
#ifdef NOFTELL
extern int nullpass;
if (nullpass != 1) {
printf("Warning in line %d : %s\n", lineno, s);
};
#else
printf("Warning in line %d : %s\n", lineno, s);
#endif
}
static int autoextend(maxnotes)
/* increase the number of abc elements the program can cope with */
int maxnotes;
{
int newlimit;
int *ptr;
featuretype *fptr;
int i;
if (verbose) {
event_warning("Extending note capacity");
};
newlimit = maxnotes*2;
fptr = (featuretype*) checkmalloc(newlimit*sizeof(featuretype));
for(i=0;i<maxnotes;i++){
fptr[i] = feature[i];
};
free(feature);
feature = fptr;
ptr = checkmalloc(newlimit*sizeof(int));
for(i=0;i<maxnotes;i++){
ptr[i] = pitch[i];
};
free(pitch);
pitch = ptr;
ptr = checkmalloc(newlimit*sizeof(int));
for(i=0;i<maxnotes;i++){
ptr[i] = num[i];
};
free(num);
num = ptr;
ptr = checkmalloc(newlimit*sizeof(int));
for(i=0;i<maxnotes;i++){
ptr[i] = denom[i];
};
free(denom);
denom = ptr;
return(newlimit);
}
static int textextend(maxstrings, stringarray)
/* resize an array of pointers to strings */
/* used with arrays words and atext */
int maxstrings;
char*** stringarray;
{
int i, newlimit;
char** ptr;
newlimit = maxstrings*2;
if (verbose) {
event_warning("Extending text capacity");
};
ptr = (char**) checkmalloc(newlimit*sizeof(char*));
for(i=0;i<maxstrings;i++){
ptr[i] = (*stringarray)[i];
};
free(*stringarray);
*stringarray = ptr;
return(newlimit);
}
static void addfeature(f, p, n, d)
/* place feature in internal table */
int f, p, n, d;
{
feature[notes] = f;
pitch[notes] = p;
num[notes] = n;
denom[notes] = d;
if ((f == NOTE) || (f == REST) || (f == CHORDOFF)) {
reduce(&num[notes], &denom[notes]);
};
notes = notes + 1;
if (notes >= maxnotes) {
maxnotes = autoextend(maxnotes);
};
}
void event_linebreak()
/* reached end of line in abc */
{
addfeature(LINENUM, lineno, 0, 0);
}
void event_startmusicline()
/* starting to parse line of abc music */
{
addfeature(MUSICLINE, 0, 0, 0);
}
void event_endmusicline(endchar)
/* finished parsing line of abc music */
char endchar;
{
addfeature(MUSICSTOP, 0, 0, 0);
}
static void textfeature(type, s)
/* called while parsing abc - stores an item which requires an */
/* associared string */
int type;
char* s;
{
atext[ntexts] = addstring(s);
addfeature(type, ntexts, 0, 0);
ntexts = ntexts + 1;
if (ntexts >= maxtexts) {
maxtexts = textextend(maxtexts, &atext);
};
}
void event_comment(s)
/* comment found in abc */
char *s;
{
if (dotune) {
if (pastheader) {
textfeature(TEXT, s);
} else {
textfeature(TEXT, s);
};
};
}
void event_specific(package, s)
/* package-specific command found i.e. %%NAME */
/* only %%MIDI commands are actually handled */
char *package, *s;
{
char msg[200], command[40];
char *p;
if (strcmp(package, "MIDI") == 0) {
int ch;
int done;
int trans, rtrans;
p = s;
done = 0;
skipspace(&p);
readstr(command, &p, 40);
if (strcmp(command, "channel") == 0) {
skipspace(&p);
ch = readnump(&p) - 1;
addfeature(CHANNEL, ch, 0, 0);
done = 1;
};
trans = strcmp(command, "transpose");
rtrans = strcmp(command, "rtranspose");
if ((trans == 0) || (rtrans == 0)) {
int neg, val;
skipspace(&p);
neg = 0;
if (*p == '+') p = p + 1;
if (*p == '-') {
p = p + 1;
neg = 1;
};
skipspace(&p);
val = readnump(&p);
if (neg) val = - val;
if (pastheader) {
if (trans == 0) {
addfeature(TRANSPOSE, val, 0, 0);
} else {
addfeature(RTRANSPOSE, val, 0, 0);
};
} else {
if (trans == 0) {
global_transpose = val;
} else {
global_transpose = global_transpose + val;
};
};
done = 1;
};
if (strcmp(command, "C") == 0) {
int val;
skipspace(&p);
val = readnump(&p);
middle_c = val;
done = 1;
};
if (strcmp(command, "nobarlines") == 0) {
retain_accidentals = 0;
done = 1;
};
if (strcmp(command, "barlines") == 0) {
retain_accidentals = 1;
done = 1;
};
if (strcmp(command, "ratio") == 0) {
int a, b;
skipspace(&p);
b = readnump(&p);
skipspace(&p);
a = readnump(&p);
if ((a > 0) && (b > 0)) {
ratio_a = a;
ratio_b = b;
if (ratio_a + ratio_b % 2 == 1) {
ratio_a = 2 * a;
ratio_b = 2 * b;
};
} else {
event_error("Invalid ratio");
};
done = 1;
};
if (strcmp(command, "grace") == 0) {
int a, b;
char msg[40];
skipspace(&p);
a = readnump(&p);
if (*p != '/') {
event_error("Need / in MIDI grace command");
} else {
p = p + 1;
};
b = readnump(&p);
if ((a < 1) || (b < 1) || (a >= b)) {
sprintf(msg, "%d/%d is not a suitable fraction", a, b);
event_error(msg);
} else {
if (pastheader) {
addfeature(SETGRACE, a, 0, b);
} else {
gfact_num = a;
gfact_denom = b;
};
};
done = 1;
};
if (strcmp(command, "gchordon") == 0) {
addfeature(GCHORDON, 0, 0, 0);
done = 1;
};
if (strcmp(command, "gchordoff") == 0) {
addfeature(GCHORDOFF, 0, 0, 0);
done = 1;
};
if (strcmp(command, "chordname") == 0) {
char name[20];
int i, notes[6];
skipspace(&p);
i = 0;
while ((i<19) && (*p != ' ') && (*p != '\0')) {
name[i] = *p;
p = p + 1;
i = i + 1;
};
name[i] = '\0';
if (*p != ' ') {
event_error("Bad format for chordname command");
} else {
i = 0;
while ((i<=6) && (*p == ' ')) {
skipspace(&p);
notes[i] = readnump(&p);
i = i + 1;
};
addchordname(name, i, notes);
};
done = 1;
};
if (done == 0) {
/* add as a command to be interpreted later */
textfeature(DYNAMIC, s);
};
} else {
strcpy(msg, "%");
strcat(msg, package);
strcat(msg, s);
event_comment(msg);
};
}
void event_startinline()
/* start of in-line field in abc music line */
{
}
void event_closeinline()
/* end of in-line field in abc music line */
{
}
void extract_filename(char *f)
/* work out filename stem from tune title */
/* name length cannot exceed namelimit characters */
{
char buffer[256];
int i;
char *p;
i = 0;
p = f;
skipspace(&p);
/* avoid initial 'The' or 'the' */
if ((strncmp(p, "The", 3) == 0) || (strncmp(p, "the", 3) == 0)) {
p = p + 3;
skipspace(&p);
};
while ((*p != '\0') && (i < namelimit)) {
if (isalnum(*p)) {
buffer[i] = *p;
i = i + 1;
};
p = p + 1;
};
buffer[i] = '\0';
if (i == 0) {
strcpy(buffer, "notitle");
buffer[namelimit] = '\0';
};
strcpy(&buffer[strlen(buffer)], ".mid");
if (outname != NULL) {
free(outname);
};
outname = addstring(buffer);
got_titlename = 1;
}
void event_field(k, f)
/* Handles R: T: and any other field not handled elsewhere */
char k;
char *f;
{
if (dotune) {
switch (k) {
case 'T':
textfeature(TITLE, f);
if (titlenames && (!got_titlename)) {
extract_filename(f);
};
break;
case 'R':
{
char* p;
p = f;
skipspace(&p);
if ((strncmp(p, "Hornpipe", 8) == 0) ||
(strncmp(p, "hornpipe", 8) == 0)) {
hornpipe = 1;
};
};
break;
default:
{
char buff[100];
if (strlen(f) < 98) {
sprintf(buff, "%c:%s", k, f);
textfeature(TEXT, buff);
};
};
};
} else {
if (k == 'T') {
event_warning("T: outside tune body - possible missing X:");
};
};
}
void event_words(p, continuation)
/* handles a w: field in the abc */
char* p;
int continuation;
{
int l;
karaoke = 1;
v->haswords = 1;
if ((wordvoice != 0) && (wordvoice != v->indexno)) {
event_warning("More than one voice with words in");
};
wordvoice = v->indexno;
words[wcount] = addstring(p);
addfeature(WORDLINE, wcount, 0, 0);
if (continuation == 0) {
addfeature(WORDSTOP, 0, 0, 0);
};
wcount = wcount + 1;
if (wcount >= maxwords) {
maxwords = textextend(maxwords, &words);
};
}
static void checkbreak()
/* check that we are in not in chord, grace notes or tuple */
/* called at voice change */
{
if (tuplecount != 0) {
event_error("Previous voice has an unfinished tuple");
tuplecount = 0;
};
if (v->inchord != 0) {
event_error("Previous voice has incomplete chord");
event_chordoff();
};
if (v->ingrace != 0) {
event_error("Previous voice has unfinished grace notes");
v->ingrace = 0;
};
}
static void char_out(part, ch)
/* routine for building up part list */
struct vstring* part;
char ch;
{
/*
if (*out - list >= MAXPARTS) {
event_error("Expanded part is too large");
} else {
**out = ch;
*out = *out + 1;
parts = parts + 1;
};
*/
addch(ch, part);
parts = parts + 1;
}
static void read_spec(spec, part)
/* converts a P: field to a list of part labels */
/* e.g. P:A(AB)3(CD)2 becomes P:AABABABCDCD */
/* A '+' indicates 'additive' behaviour (a part may include repeats). */
/* A '-' indicates 'non-additive' behaviour (repeat marks in the music */
/* are ignored and only repeats implied by the part order statement */
/* are played). */
char spec[];
struct vstring* part;
{
char* in;
int i, j;
int stackptr;
char* stack[10];
char lastch;
stackptr = 0;
in = spec;
while (((*in >= 'A') && (*in <= 'Z')) || (*in == '(') || (*in == '.') ||
(*in == ')') || (*in == '+') || (*in == '-') ||
((*in >= '0') && (*in <= '9'))) {
if (*in == '.') {
in = in + 1;
};
if (*in == '+') {
additive = 1;
in = in + 1;
};
if (*in == '-') {
additive = 0;
in = in + 1;
};
if ((*in >= 'A') && (*in <= 'Z')) {
char_out(part, *in);
lastch = *in;
in = in + 1;
};
if (*in == '(') {
if (stackptr < 10) {
stack[stackptr] = part->st + strlen(part->st);
stackptr = stackptr + 1;
} else {
event_error("nesting too deep in part specification");
};
in = in + 1;
};
if (*in == ')') {
in = in + 1;
if (stackptr > 0) {
int repeats;
char* start;
char* stop;
if ((*in >= '0') && (*in <= '9')) {
repeats = readnump(&in);
} else {
repeats = 1;
};
stackptr = stackptr - 1;
start = stack[stackptr];
stop = part->st + strlen(part->st);
for (i=1; i<repeats; i++) {
for (j=0; j<((int) (stop-start)); j++) {
char_out(part, *(start+j));
};
};
} else {
event_error("Too many )'s in part specification");
};
};
if ((*in >= '0') && (*in <= '9')) {
int repeats;
repeats = readnump(&in);
if (part->len > 0) {
for (i = 1; i<repeats; i++) {
char_out(part, lastch);
};
} else {
event_error("No part to repeat in part specification");
};
};
};
if (stackptr != 0) {
event_error("Too many ('s in part specification");
};
}
void event_part(s)
/* handles a P: field in the abc */
char* s;
{
char* p;
if (dotune) {
p = s;
skipspace(&p);
if (pastheader) {
if (((int)*p < 'A') || ((int)*p > 'Z')) {
event_error("Part must be one of A-Z");
return;
};
if ((headerpartlabel == 1) && (part.st[0] == *p)) {
/* P: field in header is not a label */
headerpartlabel = 0;
/* remove speculative part label */
feature[part_start[(int)*p - (int)'A']] = NONOTE;
} else {
if (part_start[(int)*p - (int)'A'] != -1) {
event_error("Part defined more than once");
};
};
part_start[(int)*p - (int)'A'] = notes;
addfeature(PART, (int)*p, 0, 0);
checkbreak();
v = getvoicecontext(1);
} else {
parts = 0;
read_spec(p, &part);
if (parts == 1) {
/* might be a label not a specificaton */
headerpartlabel = 1;
};
};
};
}
void event_voice(n, s)
/* handles a V: field in the abc */
int n;
char *s;
{
if (pastheader) {
voicesused = 1;
checkbreak();
v = getvoicecontext(n);
addfeature(VOICE, v->indexno, 0, 0);
} else {
event_warning("V: in header ignored");
};
}
void event_length(n)
/* handles an L: field in the abc */
int n;
{
if (pastheader) {
v->default_length = n;
} else {
global.default_length = n;
};
}
static void tempounits(t_num, t_denom)
/* interprets Q: once default length is known */
int *t_num, *t_denom;
{
/* calculate unit for tempo */
if (tempo_num == 0) {
*t_num = 1;
*t_denom = global.default_length;
} else {
if (relative_tempo) {
*t_num = tempo_num;
*t_denom = tempo_denom*global.default_length;
} else {
*t_num = tempo_num;
*t_denom = tempo_denom;
};
};
}
void event_tempo(n, a, b, rel, pre, post)
/* handles a Q: field e.g. Q: a/b = n or Q: Ca/b = n */
/* strings before and after are ignored */
int n;
int a, b, rel;
char *pre;
char *post;
{
int t_num, t_denom;
int new_div;
long new_tempo;
int tempo_l, tempo_h;
if ((n == 0) || ((a!=0) && (b == 0))) {
event_error("malformed Q: field ignored");
} else {
if (dotune) {
if (pastheader) {
tempo_num = a;
tempo_denom = b;
relative_tempo = rel;
tempounits(&t_num, &t_denom);
new_tempo = (long) 60*1000000*t_denom/(n*4*t_num);
/* split up into short ints */
tempo_l = new_tempo & 0xffff;
tempo_h = new_tempo >> 16;
new_div = (int) ((float)DIV*(float)new_tempo/(float)tempo + 0.5);
addfeature(TEMPO, new_div, tempo_h, tempo_l);
} else {
Qtempo = n;
tempo_num = a;
tempo_denom = b;
relative_tempo = rel;
};
};
};
}
void event_timesig(n, m, dochecking)
/* handles an M: field M:n/m */
int n, m, dochecking;
{
if (dotune) {
if (pastheader) {
addfeature(TIME, dochecking, n, m);
} else {
time_num = n;
time_denom = m;
timesigset = 1;
barchecking = dochecking;
};
};
}
void event_octave(num)
/* used internally by other routines when octave=N is encountered */
/* in I: or K: fields */
int num;
{
if (dotune) {
if (pastheader) {
v->octaveshift = num;
} else {
global.octaveshift = num;
};
};
}
void event_info_key(key, value)
char* key;
char* value;
{
int num;
if (strcmp(key, "octave")==0) {
num = readsnumf(value);
event_octave(num);
};
}
static void stack_broken(v)
/* store away broken rhythm context on encountering grace notes */
struct voicecontext* v;
{
v->broken_stack[0] = v->laststart;
v->broken_stack[1] = v->lastend;
v->broken_stack[2] = v->thisstart;
v->broken_stack[3] = v->thisend;
v->broken_stack[4] = v->brokentype;
v->broken_stack[5] = v->brokenmult;
v->broken_stack[6] = v->brokenpending;
v->laststart = -1;
v->lastend = -1;
v->thisstart = -1;
v->thisend = -1;
v->brokenpending = -1;
}
static void restore_broken(v)
/* remember any broken rhythm context after grace notes */
struct voicecontext* v;
{
if (v->brokenpending != -1) {
event_error("Unresolved broken rhythm in grace notes");
};
v->laststart = v->broken_stack[0];
v->lastend = v->broken_stack[1];
v->thisstart = v->broken_stack[2];
v->thisend = v->broken_stack[3];
v->brokentype = v->broken_stack[4];
v->brokenmult = v->broken_stack[5];
v->brokenpending = v->broken_stack[6];
}
void event_graceon()
/* a { in the abc */
{
if (gracenotes) {
event_error("Nested grace notes not allowed");
} else {
if (v->inchord) {
event_error("Grace notes not allowed in chord");
} else {
gracenotes = 1;
addfeature(GRACEON, 0, 0, 0);
v->ingrace = 1;
stack_broken(v);
};
};
}
void event_graceoff()
/* a } in the abc */
{
if (!gracenotes) {
event_error("} without matching {");
} else {
gracenotes = 0;
addfeature(GRACEOFF, 0, 0, 0);
v->ingrace = 0;
restore_broken(v);
};
}
void event_rep1()
/* [1 in the abc */
{
addfeature(PLAY_ON_REP, 0, 0, 1);
/*
if ((notes == 0) || (feature[notes-1] != SINGLE_BAR)) {
event_error("[1 must follow a single bar");
} else {
feature[notes-1] = BAR1;
};
*/
}
void event_rep2()
/* [2 in the abc */
{
addfeature(PLAY_ON_REP, 0, 0, 2);
/*
if ((notes == 0) || (feature[notes-1] != REP_BAR)) {
event_error("[2 must follow a :| ");
} else {
feature[notes-1] = REP_BAR2;
};
*/
}
void event_playonrep(s)
char* s;
/* [X in the abc, where X is a list of numbers */
{
int num, converted;
char seps[2];
converted = sscanf(s, "%d%1[,-]", &num, seps);
if (converted == 0) {
event_error("corrupted variant ending");
} else {
if ((converted == 1) && (num != 0)) {
addfeature(PLAY_ON_REP, 0, 0, num);
} else {
textfeature(PLAY_ON_REP, s);
};
};
}
static void slurtotie()
/* converts a pair of identical slurred notes to tied notes */
{
int last1, slurtie, last2, failed;
int j;
if ((!v->ingrace) && (!v->inchord)) {
j = notes-1;
failed = 0;
last1 = -1;
while ((j>=0) && (!failed) && (last1 == -1)) {
if (feature[j] == NOTE) {
last1 = j;
};
if ((j<=0) || (feature[j] == REST) || (feature[j] == CHORDOFF) ||
(feature[j] == GRACEOFF) || (feature[j] == SLUR_ON)) {
failed = 1;
};
j = j - 1;
};
slurtie = -1;
while ((j>=0) && (!failed) && (slurtie == -1)) {
if (feature[j] == SLUR_TIE) {
slurtie = j;
};
if ((j<=0) || (feature[j] == REST) || (feature[j] == CHORDOFF) ||
(feature[j] == GRACEOFF) || (feature[j] == SLUR_ON) ||
(feature[j] == NOTE)) {
failed = 1;
};
j = j - 1;
};
last2 = -1;
while ((j>=0) && (!failed) && (last2 == -1)) {
if (feature[j] == NOTE) {
last2 = j;
};
if ((j<=0) || (feature[j] == REST) || (feature[j] == CHORDOFF) ||
(feature[j] == GRACEOFF) || (feature[j] == SLUR_ON)) {
failed = 1;
};
j = j - 1;
};
if ((!failed) && (pitch[last1] == pitch[last2])) {
/* promote SLUR_TIE to tie */
feature[slurtie] = TIE;
event_warning("Slur in abc taken to mean a tie");
} else {
if (verbose) {
event_warning("Slur ignored");
};
};
};
}
void event_sluron(t)
/* called when ( is encountered in the abc */
int t;
{
if (t == 1) {
addfeature(SLUR_ON, 0, 0, 0);
v->inslur = 1;
};
}
void event_sluroff(t)
/* called when ) is encountered */
int t;
{
if (t == 0) {
slurtotie();
addfeature(SLUR_OFF, 0, 0, 0);
v->inslur = 0;
};
}
void event_tie()
/* a tie - has been encountered in the abc */
{
addfeature(TIE, 0, 0, 0);
}
void event_space()
/* space character in the abc is ignored by abc2midi */
{
/* ignore */
/* printf("Space event\n"); */
}
void event_lineend(ch, n)
/* called when \ or ! or * or ** is encountered at the end of a line */
char ch;
int n;
{
/* ignore */
}
void event_broken(type, mult)
/* handles > >> >>> < << <<< in the abc */
int type, mult;
{
if (v->inchord) {
event_error("Broken rhythm not allowed in chord");
} else {
if (v->ingrace) {
event_error("Broken rhythm not allowed in grace notes");
} else {
if ((hornpipe) && (feature[notes-1] == GT)) {
/* remove any superfluous hornpiping */
notes = notes - 1;
};
/* addfeature(type, mult, 0, 0); */
v->brokentype = type;
v->brokenmult = mult;
v->brokenpending = 0;
};
};
}
void event_tuple(n, q, r)
/* handles triplets (3 and general tuplets (n:q:r in the abc */
int n, q, r;
{
if (tuplecount > 0) {
event_error("nested tuples");
} else {
if (r == 0) {
specialtuple = 0;
tuplecount = n;
} else {
specialtuple = 1;
tuplecount = r;
};
if (q != 0) {
tfact_num = q;
tfact_denom = n;
} else {
if ((n < 2) || (n > 9)) {
event_error("Only tuples (2 - (9 allowed");
tfact_num = 1;
tfact_denom = 1;
tuplecount = 0;
} else {
/* deduce tfact_num using standard abc rules */
if ((n == 2) || (n == 4) || (n == 8)) tfact_num = 3;
if ((n == 3) || (n == 6)) tfact_num = 2;
if ((n == 5) || (n == 7) || (n == 9)) {
if ((time_num % 3) == 0) {
tfact_num = 3;
} else {
tfact_num = 2;
};
};
tfact_denom = n;
};
};
tnote_num = 0;
tnote_denom = 0;
};
}
void event_chord()
/* a + has been encountered in the abc */
{
if (v->inchord) {
event_chordoff();
} else {
event_chordon();
};
}
static void lenmul(n, a, b)
/* multiply note length by a/b */
int n, a, b;
{
if ((feature[n] == NOTE) || (feature[n] == REST) ||
(feature[n] == CHORDOFF)) {
num[n] = num[n] * a;
denom[n] = denom[n] * b;
reduce(&num[n], &denom[n]);
};
}
static void applybroken(place, type, n)
int place, type, n;
/* adjust lengths of broken notes */
{
int num1, num2, denom12;
int j;
int forechord, forestart, foreend, backchord, backstart, backend;
int failed, lastnote;
j = place;
failed = 0;
forestart = -1;
foreend = -1;
forechord = 0;
/* find following note or chord */
while ((!failed) && (forestart == -1)) {
if ((feature[j] == NOTE) || (feature[j] == REST)) {
forestart = j;
if (forechord) {
lastnote = forestart;
} else {
foreend = forestart;
};
};
if ((feature[j] == GRACEON) || (feature[j] == TIE)) {
event_error("Unexpected item following broken rhythm");
};
if (feature[j] == CHORDON) {
forechord = 1;
};
j = j + 1;
if (j>=notes) {
failed = 1;
};
};
/* look for extend of chord if there is one */
while ((!failed) && (foreend == -1)) {
if ((feature[j] == NOTE) || (feature[j] == REST)) {
lastnote = j;
};
if ((feature[j] == GRACEON) || (feature[j] == TIE)) {
event_error("Unexpected item following broken rhythm");
};
if (feature[j] == CHORDOFF) {
foreend = j;
};
j = j + 1;
if (j>=notes) {
failed = 1;
};
};
/* look for note or chord before broken rhythm symbol */
j = place;
backend = -1;
backstart = -1;
backchord = 0;
while ((!failed) && (backend == -1)) {
if ((feature[j] == NOTE) || (feature[j] == REST)) {
backend = j;
if (backchord) {
lastnote = backend;
} else {
backstart = backend;
};
};
if ((feature[j] == GRACEOFF) || (feature[j] == TIE)) {
event_error("Unexpected item preceding broken rhythm");
};
if (feature[j] == CHORDOFF) {
backchord = 1;
backend = j;
};
j = j - 1;
if (j<0) {
failed = 1;
};
};
/* look for extent of chord if there is one */
while ((!failed) && (backstart == -1)) {
if ((feature[j] == NOTE) || (feature[j] == REST)) {
lastnote = j;
};
if ((feature[j] == GRACEON) || (feature[j] == TIE)) {
event_error("Unexpected item following broken rhythm");
};
if (feature[j] == CHORDON) {
backstart = lastnote;
};
j = j - 1;
if (j<0) {
failed = 1;
};
};
switch(n) {
case 1:
num1 = 4;
num2 = 2;
break;
case 2:
num1 = 7;
num2 = 1;
break;
case 3:
num1 = 15;
num2 = 1;
break;
};
denom12 = (num1 + num2)/2;
if (type == LT) {
j = num1;
num1 = num2;
num2 = j;
};
/* check for same length notes */
if ((num[backstart]*denom[forestart]) !=
(num[forestart]*denom[backstart])) {
failed = 1;
};
if (failed) {
event_error("Cannot apply broken rhythm");
} else {
for (j=backstart; j<=backend; j++) {
lenmul(j, num1, denom12);
};
for (j=forestart; j<=foreend; j++) {
lenmul(j, num2, denom12);
};
};
}
static void brokenadjust()
/* adjust lengths of broken notes */
{
int num1, num2, denom12;
int j;
int failed;
switch(v->brokenmult) {
case 1:
num1 = ratio_b;
num2 = ratio_a;
break;
case 2:
num1 = 7;
num2 = 1;
break;
case 3:
num1 = 15;
num2 = 1;
break;
};
denom12 = (num1 + num2)/2;
if (v->brokentype == LT) {
j = num1;
num1 = num2;
num2 = j;
};
failed = 0;
if ((v->laststart == -1) || (v->lastend == -1) ||
(v->thisstart == -1) || (v->thisend == -1)) {
failed = 1;
} else {
/* check for same length notes */
if ((num[v->laststart]*denom[v->thisstart]) !=
(num[v->thisstart]*denom[v->laststart])) {
failed = 1;
};
};
if (failed) {
event_error("Cannot apply broken rhythm");
} else {
/*
printf("Adjusting %d to %d and %d to %d\n",
v->laststart, v->lastend, v->thisstart, v->thisend);
*/
for (j=v->laststart; j<=v->lastend; j++) {
lenmul(j, num1, denom12);
};
for (j=v->thisstart; j<=v->thisend; j++) {
lenmul(j, num2, denom12);
};
};
}
static void marknotestart()
/* voice data structure keeps a record of last few notes encountered */
/* in order to process broken rhythm. This is called at the start of */
/* a note or chord */
{
v->laststart = v->thisstart;
v->lastend = v->thisend;
v->thisstart = notes-1;
}
static void marknoteend()
/* voice data structure keeps a record of last few notes encountered */
/* in order to process broken rhythm. This is called at the end of */
/* a note or chord */
{
v->thisend = notes-1;
if (v->brokenpending != -1) {
v->brokenpending = v->brokenpending + 1;
if (v->brokenpending == 1) {
brokenadjust();
v->brokenpending = -1;
};
};
}
static void marknote()
/* when handling a single note, not a chord, marknotestart() and */
/* marknoteend() can be called together */
{
marknotestart();
marknoteend();
}
void event_rest(n,m)
/* rest of n/m in the abc */
int n, m;
{
int num, denom;
num = n;
denom = m;
if (v == NULL) {
event_fatal_error("Internal error : no voice allocated");
};
if (v->inchord) v->chordcount = v->chordcount + 1;
if (tuplecount > 0) {
num = num * tfact_num;
denom = denom * tfact_denom;
if (tnote_num == 0) {
tnote_num = num;
tnote_denom = denom;
} else {
if (tnote_num * denom != num * tnote_denom) {
if (!specialtuple) {
event_warning("Different length notes in tuple");
};
};
};
if ((!gracenotes) &&
((!v->inchord) || ((v->inchord) && (v->chordcount == 1)))) {
tuplecount = tuplecount - 1;
};
};
if (v->chordcount == 1) {
v->chord_num = num*4;
v->chord_denom = denom*(v->default_length);
};
if ((!v->ingrace) && ((!v->inchord)||(v->chordcount==1))) {
addunits(num, denom*(v->default_length));
};
last_num = 3; /* hornpiping (>) cannot follow rest */
addfeature(REST, 0, num*4, denom*(v->default_length));
if (!v->inchord ) {
marknote();
};
}
void event_mrest(n,m)
/* multiple bar rest of n/m in the abc */
/* we check for m == 1 in the parser */
int n, m;
{
int i;
for (i=0; i<n; i++) {
event_rest(time_num*(v->default_length), time_denom);
if (i != n-1) {
event_bar(SINGLE_BAR, "");
};
};
}
void event_chordon()
/* handles a chord start [ in the abc */
{
if (v->inchord) {
event_error("Attempt to nest chords");
} else {
addfeature(CHORDON, 0, 0, 0);
v->inchord = 1;
v->chordcount = 0;
v->chord_num = 0;
v->chord_denom = 1;
marknotestart();
};
}
void event_chordoff()
/* handles a chord close ] in the abc */
{
if (!v->inchord) {
event_error("Chord already finished");
} else {
addfeature(CHORDOFF, 0, v->chord_num, v->chord_denom);
v->inchord = 0;
v->chordcount = 0;
marknoteend();
};
}
void event_finger(p)
/* a 1, 2, 3, 4 or 5 has been found in a guitar chord field */
char *p;
{
/* does nothing */
}
static int pitchof(note, accidental, mult, octave, propogate_accs)
/* finds MIDI pitch value for note */
/* if propogate_accs is 1, apply any accidental to all instances of */
/* that note in the bar. If propogate_accs is 0, accidental does not */
/* apply to other notes */
char note, accidental;
int mult, octave;
int propogate_accs;
{
int p;
char acc;
int mul, noteno;
static int scale[7] = {0, 2, 4, 5, 7, 9, 11};
char *anoctave = "cdefgab";
p = (int) ((long) strchr(anoctave, note) - (long) anoctave);
p = scale[p];
acc = accidental;
mul = mult;
noteno = (int)note - 'a';
if (acc == ' ') {
acc = v->workmap[noteno];
mul = v->workmul[noteno];
} else {
if ((retain_accidentals) && (propogate_accs)) {
v->workmap[noteno] = acc;
v->workmul[noteno] = mul;
};
};
if (acc == '^') p = p + mul;
if (acc == '_') p = p - mul;
return p + 12*octave + middle_c;
}
static void doroll(note, octave, n, m, pitch)
/* applies a roll to a note */
char note;
int octave, n, m;
int pitch;
{
char up, down;
int t;
int upoct, downoct, pitchup, pitchdown;
char *anoctave = "cdefgab";
upoct = octave;
downoct = octave;
t = (int) ((long) strchr(anoctave, note) - (long) anoctave);
up = *(anoctave + ((t+1) % 7));
down = *(anoctave + ((t+6) % 7));
if (up == 'c') upoct = upoct + 1;
if (down == 'b') downoct = downoct - 1;
pitchup = pitchof(up, v->basemap[(int)up - 'a'], 1, upoct, 0);
pitchdown = pitchof(down, v->basemap[(int)down - 'a'], 1, downoct, 0);
addfeature(NOTE, pitch, n*4, m*(v->default_length)*5);
marknotestart();
addfeature(NOTE, pitchup, n*4, m*(v->default_length)*5);
addfeature(NOTE, pitch, n*4, m*(v->default_length)*5);
addfeature(NOTE, pitchdown, n*4, m*(v->default_length)*5);
addfeature(NOTE, pitch, n*4, m*(v->default_length)*5);
marknoteend();
}
static void dotrill(note, octave, n, m, pitch)
/* applies a trill to a note */
char note;
int octave, n, m;
int pitch;
{
char up;
int i, t;
int upoct, pitchup;
char *anoctave = "cdefgab";
int a, b, count;
upoct = octave;
t = (int) ((long) strchr(anoctave, note) - (long) anoctave);
up = *(anoctave + ((t+1) % 7));
if (up == 'c') upoct = upoct + 1;
pitchup = pitchof(up, v->basemap[(int)up - 'a'], 1, upoct, 0);
a = 4;
b = m*(v->default_length);
count = n;
while ((tempo*a)/((long)b) > 100000L) {
count = count*2;
b = b*2;
};
i = 0;
while (i < count) {
if (i == count - 1) {
marknotestart();
};
if (i%2 == 0) {
addfeature(NOTE, pitchup, a, b);
} else {
addfeature(NOTE, pitch, a, b);
};
i = i + 1;
};
marknoteend();
}
static void hornp(num, denom)
/* If we have used R:hornpipe, this routine modifies the rhythm by */
/* applying appropriate broken rhythm */
int num, denom;
{
if ((hornpipe) && (notes > 0) && (feature[notes-1] != GT)) {
if ((num*last_denom == last_num*denom) && (num == 1) &&
(denom*time_num == 32)) {
if (((time_num == 4) && (bar_denom == 8)) ||
((time_num == 2) && (bar_denom == 16))) {
/* addfeature(GT, 1, 0, 0); */
v->brokentype = GT;
v->brokenmult = 1;
v->brokenpending = 0;
};
};
last_num = num;
last_denom = denom;
};
}
void event_note(decorators, accidental, mult, note, xoctave, n, m)
/* handles a note in the abc */
int decorators[DECSIZE];
int mult;
char accidental, note;
int xoctave, n, m;
{
int pitch;
int num, denom;
int octave;
if (v == NULL) {
event_fatal_error("Internal error - no voice allocated");
};
octave = xoctave + v->octaveshift;
num = n;
denom = m;
if (v->inchord) v->chordcount = v->chordcount + 1;
if (tuplecount > 0) {
num = num * tfact_num;
denom = denom * tfact_denom;
if (tnote_num == 0) {
tnote_num = num;
tnote_denom = denom;
} else {
if (tnote_num * denom != num * tnote_denom) {
if (!specialtuple) {
event_warning("Different length notes in tuple");
};
};
};
if ((!gracenotes) &&
((!v->inchord) || ((v->inchord) && (v->chordcount == 1)))) {
tuplecount = tuplecount - 1;
};
};
if ((!v->ingrace) && (!v->inchord)) {
hornp(num, denom*(v->default_length));
} else {
last_num = 3; /* hornpiping (>) cannot follow chord or grace notes */
};
if ((!v->ingrace) && ((!v->inchord)||(v->chordcount==1))) {
addunits(num, denom*(v->default_length));
};
pitch = pitchof(note, accidental, mult, octave, 1);
if (decorators[FERMATA]) {
num = num*2;
};
if (v->chordcount == 1) {
v->chord_num = num*4;
v->chord_denom = denom*(v->default_length);
};
if ((decorators[ROLL]) || (decorators[ORNAMENT]) || (decorators[TRILL])) {
if (v->inchord) {
event_error("Rolls and trills not supported in chords");
} else {
if (decorators[TRILL]) {
dotrill(note, octave, num, denom, pitch);
} else {
doroll(note, octave, num, denom, pitch);
};
};
} else {
if (decorators[STACCATO]) {
if (v->inchord) {
if (v->chordcount == 1) {
addfeature(REST, pitch, num*4, denom*(v->default_length));
};
addfeature(NOTE, pitch, num*4, denom*2*(v->default_length));
} else {
addfeature(NOTE, pitch, num*4, denom*2*(v->default_length));
marknotestart();
addfeature(REST, pitch, num*4, denom*2*(v->default_length));
marknoteend();
};
} else {
addfeature(NOTE, pitch, num*4, denom*(v->default_length));
if (!v->inchord) {
marknote();
};
if ((v->inslur) && (!v->ingrace)) {
addfeature(SLUR_TIE, 0, 0, 0);
};
};
};
}
char *get_accidental(place, accidental)
/* read in accidental - used by event_handle_gchord() */
char *place; /* place in string being parsed */
char *accidental; /* pointer to char variable */
{
char *p;
p = place;
*accidental = '=';
if (*p == '#') {
*accidental = '^';
p = p + 1;
};
if (*p == 'b') {
*accidental = '_';
p = p + 1;
};
return(p);
}
void event_handle_gchord(s)
/* handler for the guitar chords */
char* s;
{
int basepitch;
char accidental, note;
char* p;
char name[9];
int i;
int chordno;
int bassnote;
int inversion;
if ((*s >= '0') && (*s <= '5')) {
event_finger(s);
return;
};
p = s;
if ((*p >= 'A') && (*p <= 'G')) {
note = *p - (int) 'A' + (int) 'a';
bassnote = 0;
p = p + 1;
} else {
if ((*p >= 'a') && (*p <= 'g')) {
note = *p;
bassnote = 1;
p = p + 1;
} else {
/* Experimental feature supports "_ignored words" */
if (strchr("_^<>@", (int)*p) == NULL) {
event_error("Guitar chord does not start with A-G or a-g");
};
return;
};
};
p = get_accidental(p, &accidental);
basepitch = pitchof(note, accidental, 1, 0, 0) - middle_c;
i = 0;
while ((i<9) && (*p != ' ') && (*p != '\0') && (*p != '(') && (*p != '/')) {
name[i] = *p;
i = i+1;
p = p + 1;
};
inversion = -1;
if (*p == '/') {
p = p + 1;
if ((*p < 'A') || (*p > 'G')) {
event_error(" / must be followed by A-G in chord");
} else {
note = (int)*p - 'A' + 'a';
p = p + 1;
p = get_accidental(p, &accidental);
inversion = pitchof(note, accidental, 1, 0, 0) - middle_c;
};
};
name[i] = '\0';
chordno = getchordnumber(name);
if (chordno == 0) {
char msg[40];
sprintf(msg, "Unrecognized chord name \"%s\"", name);
event_error(msg);
chordno = 1; /* defaults to major */
} else {
/* only record voice as having chords if we recognize chord type */
v->hasgchords = 1;
if ((gchordvoice != 0) && (gchordvoice != v->indexno)) {
event_warning("More than one voice with guitar chords in");
};
gchordvoice = v->indexno;
};
if (bassnote) {
chordno = -1;
};
addfeature(GCHORD, basepitch, inversion, chordno);
}
void event_handle_instruction(s)
/* handler for ! ! instructions */
/* does ppp pp p mp mf f ff fff */
/* also does !drum! and !nodrum! */
char* s;
{
char buff[MAXLINE];
char* p;
char* q;
int done;
p = s;
/* remove any leading spaces */
skipspace(&p);
/* remove any trailing spaces */
q = p;
while ((*q != '\0') && (*q != ' ')) {
q = q + 1;
};
if (*q == ' ') {
*q = '\0';
};
done = 0;
if (strcmp(p, "ppp") == 0) {
event_specific("MIDI", "beat 30 20 10 1");
done = 1;
};
if (strcmp(p, "pp") == 0) {
event_specific("MIDI", "beat 45 35 20 1");
done = 1;
};
if (strcmp(p, "p") == 0) {
event_specific("MIDI", "beat 60 50 35 1");
done = 1;
};
if (strcmp(p, "mp") == 0) {
event_specific("MIDI", "beat 75 65 50 1");
done = 1;
};
if (strcmp(p, "mf") == 0) {
event_specific("MIDI", "beat 90 80 65 1");
done = 1;
};
if (strcmp(p, "f") == 0) {
event_specific("MIDI", "beat 105 95 80 1");
done = 1;
};
if (strcmp(p, "ff") == 0) {
event_specific("MIDI", "beat 120 110 95 1");
done = 1;
};
if (strcmp(p, "fff") == 0) {
event_specific("MIDI", "beat 127 125 110 1");
done = 1;
};
if (strcmp(p, "drum") == 0) {
addfeature(DRUMON, 0, 0, 0);
if ((drumvoice != 0) && (drumvoice != v->indexno)) {
event_warning("Implementation limit: drums only supported in one voice");
};
drumvoice = v->indexno;
done = 1;
};
if (strcmp(p, "nodrum") == 0) {
addfeature(DRUMOFF, 0, 0, 0);
done = 1;
};
if (done == 0) {
sprintf(buff, "instruction !%s! ignored", s);
event_warning(buff);
};
}
static void setmap(sf, map, mult)
/* work out accidentals to be applied to each note */
int sf; /* number of sharps in key signature -7 to +7 */
char map[7];
int mult[7];
{
int j;
for (j=0; j<7; j++) {
map[j] = '=';
mult[j] = 1;
};
if (sf >= 1) map['f'-'a'] = '^';
if (sf >= 2) map['c'-'a'] = '^';
if (sf >= 3) map['g'-'a'] = '^';
if (sf >= 4) map['d'-'a'] = '^';
if (sf >= 5) map['a'-'a'] = '^';
if (sf >= 6) map['e'-'a'] = '^';
if (sf >= 7) map['b'-'a'] = '^';
if (sf <= -1) map['b'-'a'] = '_';
if (sf <= -2) map['e'-'a'] = '_';
if (sf <= -3) map['a'-'a'] = '_';
if (sf <= -4) map['d'-'a'] = '_';
if (sf <= -5) map['g'-'a'] = '_';
if (sf <= -6) map['c'-'a'] = '_';
if (sf <= -7) map['f'-'a'] = '_';
}
static void altermap(v, modmap, modmul)
/* apply modifiers to a set of accidentals */
struct voicecontext* v;
char modmap[7];
int modmul[7];
{
int i;
for (i=0; i<7; i++) {
if (modmap[i] != ' ') {
v->basemap[i] = modmap[i];
v->basemul[i] = modmul[i];
};
};
}
static void copymap(v)
/* sets up working map at the start of each bar */
struct voicecontext* v;
{
int j;
for (j=0; j<7; j++) {
v->workmap[j] = v->basemap[j];
v->workmul[j] = v->basemul[j];
};
}
/* workaround for problems with PCC compiler */
/* data may be written to an internal buffer */
int myputc(c)
char c;
{
return (putc(c,fp));
}
static void addfract(xnum, xdenom, a, b)
/* add a/b to the count of units in the bar */
int *xnum;
int *xdenom;
int a, b;
{
*xnum = (*xnum)*b + a*(*xdenom);
*xdenom = (*xdenom) * b;
reduce(xnum, xdenom);
}
static void dotie(j, xinchord)
/* called in preprocessing stage to handle ties */
int j, xinchord;
{
int tienote, place;
int tietodo, done;
int lastnote, lasttie;
int inchord;
int tied_num, tied_denom;
/* find note to be tied */
tienote = j;
while ((tienote > 0) && (feature[tienote] != NOTE) &&
(feature[tienote] != REST)) {
tienote = tienote - 1;
};
if (feature[tienote] != NOTE) {
event_error("Cannot find note before tie");
} else {
inchord = xinchord;
/* change NOTE + TIE to TNOTE + REST */
feature[tienote] = TNOTE;
feature[j] = REST;
num[j] = num[tienote];
denom[j] = denom[tienote];
place = j;
tietodo = 1;
lasttie = j;
tied_num = num[tienote];
tied_denom = denom[tienote];
lastnote = -1;
done = 0;
while ((place < notes) && (tied_num >=0) && (done == 0)) {
switch (feature[place]) {
case NOTE:
lastnote = place;
if ((tied_num == 0) && (tietodo == 0)) {
done = 1;
};
if ((pitch[place] == pitch[tienote]) && (tietodo == 1)) {
/* tie in note */
if (tied_num != 0) {
event_error("Time mismatch at tie");
};
tietodo = 0;
/* add time to tied time */
addfract(&tied_num, &tied_denom, num[place], denom[place]);
/* add time to tied note */
addfract(&num[tienote], &denom[tienote], num[place], denom[place]);
/* change note to a rest */
feature[place] = REST;
/* get rid of tie */
if (lasttie != j) {
feature[lasttie] = OLDTIE;
};
};
if (inchord == 0) {
/* subtract time from tied time */
addfract(&tied_num, &tied_denom, -num[place], denom[place]);
};
break;
case REST:
if ((tied_num == 0) && (tietodo == 0)) {
done = 1;
};
if (inchord == 0) {
/* subtract time from tied time */
addfract(&tied_num, &tied_denom, -num[place], denom[place]);
};
break;
case TIE:
if (lastnote == -1) {
event_error("Bad tie: possibly two ties in a row");
} else {
if (pitch[lastnote] == pitch[tienote]) {
lasttie = place;
tietodo = 1;
};
};
break;
case CHORDON:
inchord = 1;
break;
case CHORDOFF:
inchord = 0;
/* subtract time from tied time */
addfract(&tied_num, &tied_denom, -num[place], denom[place]);
break;
default:
break;
};
place = place + 1;
};
if (tietodo == 1) {
event_error("Could not find note to be tied");
};
};
}
static void tiefix()
/* connect up tied notes */
{
int j;
int inchord;
int chord_num, chord_denom;
j = 0;
inchord = 0;
while (j<notes) {
switch (feature[j]) {
case CHORDON:
inchord = 1;
chord_num = -1;
j = j + 1;
break;
case CHORDOFF:
if (!((!inchord) || (chord_num == -1))) {
num[j] = chord_num;
denom[j] = chord_denom;
};
inchord = 0;
j = j + 1;
break;
case NOTE:
if ((inchord) && (chord_num == -1)) {
chord_num = num[j];
chord_denom = denom[j];
};
j = j + 1;
break;
case REST:
if ((inchord) && (chord_num == -1)) {
chord_num = num[j];
chord_denom = denom[j];
};
j = j + 1;
break;
case TIE:
dotie(j, inchord);
j = j + 1;
break;
case LINENUM:
lineno = pitch[j];
j = j + 1;
break;
default:
j = j + 1;
break;
};
};
}
static void applygrace(place)
int place;
/* assign lengths to grace notes before generating MIDI */
{
int start, end, p;
int next_num, next_denom;
int fact_num, fact_denom;
int grace_num, grace_denom;
int j;
int nextinchord;
int hostnotestart, hostnoteend;
j = place;
start = -1;
while ((j < notes) && (start == -1)) {
if (feature[j] == GRACEON) {
start = j;
};
if (feature[j] == GRACEOFF) {
event_error("} with no matching {");
};
j = j + 1;
};
/* now find end of grace notes */
end = -1;
while ((j < notes) && (end == -1)) {
if (feature[j] == GRACEOFF) {
end = j;
};
if ((feature[j] == GRACEON) && (j != start - 1)) {
event_error("nested { not allowed");
};
j = j + 1;
};
/* now find following note */
nextinchord = 0;
hostnotestart = -1;
while ((hostnotestart == -1) && (j < notes)) {
if ((feature[j] == NOTE) || (feature[j] == REST)) {
hostnotestart = j;
};
if (feature[j] == GRACEON) {
event_error("Intervening note needed between grace notes");
};
if (feature[j] == CHORDON) {
nextinchord = 1;
};
j = j + 1;
};
hostnoteend = -1;
if (nextinchord) {
while ((hostnoteend == -1) && (j < notes)) {
if (feature[j] == CHORDOFF) {
hostnotestart = j;
};
j = j + 1;
};
} else {
hostnoteend = hostnotestart;
};
if (hostnotestart == -1) {
event_error("No note found to follow grace notes");
} else {
/* count up grace units */
grace_num = 0;
grace_denom = 1;
p = start;
while (p <= end) {
if ((feature[p] == NOTE) || (feature[p] == REST)) {
grace_num = grace_num * denom[p] + grace_denom * num[p];
grace_denom = grace_denom * denom[p];
reduce(&grace_num, &grace_denom);
};
p = p + 1;
};
/* adjust host note or notes */
p = hostnotestart;
while (p <= hostnoteend) {
if ((feature[p] == NOTE) || (feature[p] == REST) ||
(feature[p] == CHORDOFF)) {
next_num = num[p];
next_denom = denom[p];
num[p] = num[p] * (gfact_denom - gfact_num);
denom[p] = next_denom * gfact_denom;
reduce(&num[p], &denom[p]);
};
p = p + 1;
};
fact_num = next_num * grace_denom * gfact_num;
fact_denom = next_denom * grace_num * gfact_denom;
reduce(&fact_num, &fact_denom);
/* adjust length of grace notes */
p = start;
while (p <= end) {
lenmul(p, fact_num, fact_denom);
p = p + 1;
};
};
}
static void dograce()
/* assign lengths to grace notes before generating MIDI */
{
int j;
j = 0;
while (j < notes) {
if (feature[j] == GRACEON) {
applygrace(j);
};
if (feature[j] == SETGRACE) {
gfact_num = pitch[j];
gfact_denom = denom[j];
};
if (feature[j] == LINENUM) {
lineno = pitch[j];
};
j = j + 1;
};
}
static void zerobar()
/* start a new count of beats in the bar */
{
bar_num = 0;
bar_denom = 1;
}
void event_bar(type, replist)
/* handles bar lines of various types in the abc */
int type;
char* replist;
{
int newtype;
newtype = type;
if ((type == THIN_THICK) || (type == THICK_THIN)) {
newtype = DOUBLE_BAR;
};
if (type == BAR1) {
newtype = SINGLE_BAR;
};
if (type == REP_BAR2) {
newtype = REP_BAR;
};
addfeature(newtype, 0, 0, 0);
copymap(v);
zerobar();
if (strlen(replist) > 0) {
event_playonrep(replist);
};
/*
if (type == BAR1) {
addfeature(PLAY_ON_REP, 0, 0, 1);
};
if (type == REP_BAR2) {
addfeature(PLAY_ON_REP, 0, 0, 2);
};
*/
}
static void delendrep(j)
int j;
/* remove bogus repeat */
{
event_error("spurious repeat after second ending");
switch(feature[j]) {
case REP_BAR:
feature[j] = DOUBLE_BAR;
break;
case DOUBLE_REP:
feature[j] = BAR_REP;
break;
default:
break;
};
}
static void placeendrep(j)
/* patch up missing repeat */
int j;
{
event_warning("Assuming repeat");
switch(feature[j]) {
case DOUBLE_BAR:
feature[j] = REP_BAR;
break;
case SINGLE_BAR:
feature[j] = REP_BAR;
break;
case BAR_REP:
feature[j] = DOUBLE_REP;
break;
default:
event_error("Internal error - please report");
break;
};
}
static void placestartrep(j)
/* patch up missing repeat */
int j;
{
event_warning("Assuming repeat");
switch(feature[j]) {
case DOUBLE_BAR:
feature[j] = BAR_REP;
break;
case SINGLE_BAR:
feature[j] = BAR_REP;
break;
case REP_BAR:
feature[j] = DOUBLE_REP;
break;
case BAR_REP:
event_error("Too many end repeats");
break;
case DOUBLE_REP:
event_error("Too many end repeats");
break;
default:
event_error("Internal error - please report");
break;
};
}
static void fixreps()
/* find and correct missing repeats in music */
/* abc2midi places an extra || at the start of the music */
/* This can be converted to |: if necessary. */
{
int j;
int rep_point; /* where to assume a repeat starts */
int expect_repeat;
int expect_norepeat;
int use_next;
expect_repeat = 0;
expect_norepeat = 0;
use_next = 0;
j = 0;
while (j < notes) {
switch(feature[j]) {
case SINGLE_BAR:
if (use_next) {
rep_point = j;
use_next = 0;
};
break;
case DOUBLE_BAR:
rep_point = j;
use_next = 0;
break;
case BAR_REP:
if (expect_repeat) {
event_error("|: found when end repeat expected");
placeendrep(j);
};
expect_repeat = 1;
expect_norepeat = 0;
use_next = 0;
break;
case REP_BAR:
if (expect_norepeat) {
delendrep(j);
} else {
if (!expect_repeat) {
placestartrep(rep_point);
};
};
expect_repeat = 0;
expect_norepeat = 0;
rep_point = j;
use_next = 0;
break;
case PLAY_ON_REP:
if (denom[j] == 1) {
/* case REP1: */
if (!expect_repeat) {
placestartrep(rep_point);
};
expect_repeat = 1;
expect_norepeat = 0;
break;
};
if (denom[j] == 2) {
/* case REP2: */
if (expect_repeat) {
event_error("require :|2 for second ending");
};
expect_repeat = 0;
expect_norepeat = 1;
break;
};
case REP_BAR2:
/* cannot happen! */
if (!expect_repeat) {
placestartrep(rep_point);
};
expect_repeat = 0;
use_next = 1;
break;
case DOUBLE_REP:
if (expect_norepeat) {
delendrep(j);
} else {
if (!expect_repeat) {
placestartrep(rep_point);
};
};
expect_repeat = 1;
expect_norepeat = 0;
break;
default:
break;
};
j = j + 1;
};
}
static void startfile()
/* called at the beginning of an abc tune by event_refno */
/* This sets up all the default values */
{
int j;
if (verbose) {
printf("scanning tune\n");
};
/* set up defaults */
sf = 0;
mi = 0;
setmap(0, global.basemap, global.basemul);
copymap(&global);
global.octaveshift = 0;
voicecount = 0;
head = NULL;
v = NULL;
got_titlename = 0;
time_num = 4;
time_denom = 4;
mtime_num = 4;
mtime_denom = 4;
timesigset = 0;
barchecking = 1;
global.default_length = -1;
event_tempo(120, 1, 4, 0, NULL, NULL);
notes = 0;
ntexts = 0;
gfact_num = 1;
gfact_denom = 3;
global_transpose = 0;
hornpipe = 0;
karaoke = 0;
retain_accidentals = 1;
ratio_a = 2;
ratio_b = 4;
wcount = 0;
parts = -1;
middle_c = 60;
for (j=0; j<26; j++) {
part_start[j] = -1;
};
headerpartlabel = 0;
additive = 1;
initvstring(&part);
for (j=0; j<16;j++) {
channels[j] = 0;
};
set_gchords("z");
gchordvoice = 0;
set_drums("z");
drumvoice = 0;
wordvoice = 0;
}
static void setbeat()
/* default accompaniment patterns for various time signatures */
{
/* set up chord/fundamental sequence if not already set */
if ((time_num == 2) && (time_denom == 2)) {
set_gchords("fzczfzcz");
};
if (((time_num == 2) || (time_num == 4)) && (time_denom == 4)) {
set_gchords("fzczfzcz");
};
if ((time_num == 3) && (time_denom == 4)) {
set_gchords("fzczcz");
};
if ((time_num == 6) && (time_denom == 8)) {
set_gchords("fzcfzc");
};
if ((time_num == 9) && (time_denom == 8)) {
set_gchords("fzcfzcfzc");
};
}
static void headerprocess()
/* called after the K: field has been reached, signifying the end of */
/* the header and the start of the tune */
{
int t_num, t_denom;
if (headerpartlabel == 1) {
part_start[(int)part.st[0] - (int)'A'] = notes;
addfeature(PART, part.st[0], 0, 0);
};
addfeature(DOUBLE_BAR, 0, 0, 0);
pastheader = 1;
gracenotes = 0; /* not in a grace notes section */
if (!timesigset) {
event_warning("No M: in header, using default");
};
/* calculate time for a default length note */
if (global.default_length == -1) {
if (((float) time_num)/time_denom < 0.75) {
global.default_length = 16;
} else {
global.default_length = 8;
};
};
bar_num = 0;
bar_denom = 1;
set_meter(time_num, time_denom);
if (hornpipe) {
if ((time_denom != 4) || ((time_num != 2) && (time_num != 4))) {
event_error("Hornpipe must be in 2/4 or 4/4 time");
hornpipe = 0;
};
};
tempounits(&t_num, &t_denom);
/* make tempo in terms of 1/4 notes */
tempo = (long) 60*1000000*t_denom/(Qtempo*4*t_num);
div_factor = division;
setbeat();
voicesused = 0;
}
void event_key(sharps, s, minor, modmap, modmul, gotkey, gotclef, clefname,
octave, transpose, gotoctave, gottranspose)
/* handles a K: field */
int sharps; /* sharps is number of sharps in key signature */
int minor; /* a boolean 0 or 1 */
char *s; /* original string following K: */
char modmap[7]; /* array of accidentals to be applied */
int modmul[7]; /* array giving multiplicity of each accent (1 or 2) */
int gotkey, gotclef;
int octave, transpose, gotoctave, gottranspose;
char* clefname;
{
if ((dotune) && gotkey) {
if (pastheader) {
setmap(sharps, v->basemap, v->basemul);
altermap(v, modmap, modmul);
copymap(v);
addfeature(KEY, sharps, 0, minor);
if (gottranspose) {
addfeature(TRANSPOSE, transpose, 0, 0);
};
} else {
if (gottranspose) {
global_transpose = transpose;
};
setmap(sharps, global.basemap, global.basemul);
altermap(&global, modmap, modmul);
copymap(&global);
sf = sharps;
mi = minor;
headerprocess();
v = newvoice(1);
head = v;
};
if (gotoctave) {
event_octave(octave);
};
};
}
static void finishfile()
/* end of tune has been reached - write out MIDI file */
{
extern int nullputc();
clearvoicecontexts();
if (!pastheader) {
event_error("No valid K: field found at start of tune");
} else {
int i;
if (parts > -1) {
addfeature(PART, ' ', 0, 0);
};
if (headerpartlabel == 1) {
event_error("P: field in header should go after K: field");
};
if (verbose) {
printf("handling grace notes\n");
};
dograce();
tiefix();
if ((parts == -1) && (voicecount == 1)) {
if (verbose) {
printf("fixing repeats\n");
};
fixreps();
};
if ((voicesused == 0) && (!karaoke) && (gchordvoice == 0) &&
(drumvoice == 0)) {
ntracks = 1;
} else {
ntracks = voicecount + karaoke + 1;
if (gchordvoice != 0) {
gchordtrack = ntracks;
ntracks = ntracks + 1;
};
if (drumvoice != 0) {
drumtrack = ntracks;
ntracks = ntracks + 1;
};
};
if (check) {
Mf_putc = nullputc;
if (ntracks == 1) {
writetrack(0);
} else {
for (i=0; i<ntracks; i++) {
writetrack(i);
};
};
} else {
if ((fp = fopen(outname, "wb")) == NULL) {
event_fatal_error("File open failed");
};
printf("writing MIDI file %s\n", outname);
Mf_putc = myputc;
Mf_writetrack = writetrack;
if (ntracks == 1) {
mfwrite(0, 1, division, fp);
} else {
mfwrite(1, ntracks, division, fp);
};
fclose(fp);
#ifdef __MACINTOSH__
(void) setOutFileCreator(outname,'Midi','ttxt');
#endif /* __MACINTOSH__ */
};
for (i=0; i<ntexts; i++) {
free(atext[i]);
};
for (i=0; i<wcount; i++) {
free(words[i]);
};
freevstring(&part);
};
}
void event_blankline()
/* blank line found in abc signifies the end of a tune */
{
if (dotune) {
finishfile();
parseroff();
dotune = 0;
};
}
void event_refno(n)
/* handles an X: field (which indicates the start of a tune) */
int n;
{
char numstr[23]; /* Big enough for a 64-bit int! */
char newname[256];
if (dotune) {
finishfile();
parseroff();
dotune = 0;
};
if (verbose) {
printf("Reference X: %d\n", n);
};
if ((n == xmatch) || (xmatch == 0) || (xmatch == -1)) {
if (xmatch == -1) {
xmatch = -2;
};
parseron();
dotune = 1;
pastheader = 0;
if (userfilename == 0) {
if (outname != NULL) {
free(outname);
};
sprintf(numstr, "%d", n);
if (strlen(numstr) > namelimit - 1) {
numstr[namelimit - 1] = '\0';
};
if (strlen(outbase) + strlen(numstr) > namelimit) {
strncpy(newname, outbase, namelimit - strlen(numstr));
strcpy(&newname[namelimit - strlen(numstr)], numstr);
strcpy(&newname[strlen(newname)], ".mid");
} else {
sprintf(newname, "%s%s.mid", outbase, numstr);
};
outname = addstring(newname);
/* outname = (char*)checkmalloc(strlen(outbase) + 22 + strlen(".mid")); */
/* sprintf(outname, "%s%d.mid", outbase, n); */
};
startfile();
};
}
void event_eof()
/* end of abc file encountered */
{
if (dotune) {
dotune = 0;
parseroff();
finishfile();
};
if (verbose) {
printf("End of File reached\n");
};
free(pitch);
free(num);
free(denom);
free(feature);
free(words);
free(outname);
free(outbase);
}