mirror of
https://github.com/sshlien/abcmidi.git
synced 2025-12-06 06:55:06 +00:00
3005 lines
68 KiB
C
3005 lines
68 KiB
C
/*
|
|
* yaps - program to convert abc files to PostScript.
|
|
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*
|
|
*/
|
|
|
|
/* yapstree.c - back-end for abc parser. */
|
|
/* generates a data structure suitable for typeset music */
|
|
|
|
#define VERSION "1.87 May 25 2021 yaps"
|
|
#include <stdio.h>
|
|
#ifdef USE_INDEX
|
|
#define strchr index
|
|
#endif
|
|
|
|
/* for Microsoft VC 6++ or higher */
|
|
#ifdef _MSC_VER
|
|
#define ANSILIBS
|
|
#define snprintf _snprintf
|
|
#endif
|
|
|
|
#ifdef ANSILIBS
|
|
#include <ctype.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#else
|
|
char* strchr();
|
|
#endif
|
|
#include "abc.h"
|
|
#include "parseabc.h"
|
|
#include "parser2.h"
|
|
#include "structs.h"
|
|
#include "drawtune.h"
|
|
|
|
extern void setscaling(char *s);
|
|
extern void font_command();
|
|
extern void setup_fonts();
|
|
extern void printtune(struct tune *t);
|
|
extern void set_keysig(struct key *k, struct key *newval);
|
|
|
|
/* forward definition */
|
|
void event_clef(char* clefstr, cleftype_t * new_clef);
|
|
|
|
programname fileprogram = YAPS;
|
|
extern int oldchordconvention; /* for handling +..+ chords */
|
|
|
|
struct voice* cv;
|
|
struct tune thetune;
|
|
|
|
char outputname[MAX_OUTPUTNAME + 1]; /* [JA] 2020-11-01 */
|
|
char outputroot[MAX_OUTPUTROOT + 1];
|
|
char matchstring[256];
|
|
int fileopen;
|
|
|
|
int xinhead;
|
|
int xinbody;
|
|
int suppress;
|
|
int debugging;
|
|
int pagenumbering;
|
|
int separate_voices;
|
|
int print_xref;
|
|
int landscape;
|
|
int barnums,nnbars;
|
|
extern int gchords_above;
|
|
extern int decorators_passback[DECSIZE]; /* a kludge for passing
|
|
information from the event_handle_instruction to parsenote
|
|
in parseabc.c */
|
|
|
|
enum linestattype {fresh, midmusic, endmusicline, postfield};
|
|
enum linestattype linestat;
|
|
|
|
int dummydecorator[DECSIZE]; /* used in event_chord */
|
|
|
|
void setfract(f, a, b)
|
|
struct fract* f;
|
|
int a, b;
|
|
/* assign value to fraction */
|
|
{
|
|
f->num = a;
|
|
f->denom = b;
|
|
}
|
|
|
|
void reducef(f)
|
|
struct fract* f;
|
|
/* reducef fraction to smallest terms */
|
|
{
|
|
int t, n, m;
|
|
|
|
/* find HCF using Euclid's algorithm */
|
|
if (f->num > f->denom) {
|
|
n = f->num;
|
|
m = f->denom;
|
|
} else {
|
|
n = f->denom;
|
|
m = f->num;
|
|
};
|
|
while (m != 0) {
|
|
t = n % m;
|
|
n = m;
|
|
m = t;
|
|
};
|
|
f->num = f->num/n;
|
|
f->denom = f->denom/n;
|
|
}
|
|
|
|
static struct fract* newfract(int a, int b)
|
|
/* create an initialized fraction */
|
|
{
|
|
struct fract* f;
|
|
|
|
f = (struct fract*)checkmalloc(sizeof(struct fract));
|
|
f->num = a;
|
|
f->denom = 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 */
|
|
{
|
|
struct slurtie* f;
|
|
|
|
f = (struct slurtie*)checkmalloc(sizeof(struct slurtie));
|
|
f->begin = NULL;
|
|
f->end = NULL;
|
|
f->crossline = 0;
|
|
return(f);
|
|
}
|
|
|
|
static struct atempo* newtempo(int count, int n, int m, int relative,
|
|
char *pre, char *post)
|
|
/* create an initialized tempo data structure */
|
|
{
|
|
struct atempo* t;
|
|
|
|
t = (struct atempo*)checkmalloc(sizeof(struct atempo));
|
|
t->count = count;
|
|
t->basenote.num = n;
|
|
t->basenote.denom = m;
|
|
t->relative = relative;
|
|
if (pre == NULL) {
|
|
t->pre = NULL;
|
|
} else {
|
|
t->pre = addstring(pre);
|
|
};
|
|
if (post == NULL) {
|
|
t->post = NULL;
|
|
} else {
|
|
t->post = addstring(post);
|
|
};
|
|
return(t);
|
|
}
|
|
|
|
static struct vertspacing* newvertspacing()
|
|
/* create new vertspacing structure */
|
|
{
|
|
struct vertspacing* p;
|
|
|
|
p = (struct vertspacing*)checkmalloc(sizeof(struct vertspacing));
|
|
p->height = 0.0;
|
|
p->descender = 0.0;
|
|
p->yend = 0.0;
|
|
p->yinstruct = 0.0;
|
|
p->ygchord = 0.0;
|
|
p->ywords = 0.0;
|
|
return(p);
|
|
}
|
|
|
|
static struct tuple* newtuple(int n, int q, int r, int label)
|
|
/* create tuple data structure */
|
|
{
|
|
struct tuple* f;
|
|
|
|
f = (struct tuple*)checkmalloc(sizeof(struct tuple));
|
|
f->n = n;
|
|
f->q = q;
|
|
f->r = r;
|
|
f->label = label;
|
|
f->beamed = 1;
|
|
return(f);
|
|
}
|
|
|
|
static struct chord* newchord()
|
|
/* create chord data structure */
|
|
{
|
|
struct chord* f;
|
|
|
|
f = (struct chord*)checkmalloc(sizeof(struct chord));
|
|
f->ytop = 0;
|
|
f->ybot = 0;
|
|
return(f);
|
|
}
|
|
|
|
cleftype_t *newclef (cleftype_t * source_clef)
|
|
/* create and initialize clef data structure */
|
|
{
|
|
cleftype_t *f; /* [JA] 2020-10-19 */
|
|
|
|
f = (cleftype_t *) checkmalloc (sizeof (cleftype_t));
|
|
copy_clef (f, source_clef);
|
|
return (f);
|
|
}
|
|
|
|
struct key* newkey(char* name, int sharps, char accidental[], int mult[])
|
|
/* create and initialize key signature */
|
|
{
|
|
struct key* k;
|
|
int i;
|
|
|
|
k = (struct key*)checkmalloc(sizeof(struct key));
|
|
k->name = addstring(name);
|
|
k->sharps = sharps;
|
|
for (i=0; i<7; i++) {
|
|
k->map[i] = accidental[i];
|
|
k->mult[i] = mult[i];
|
|
};
|
|
return(k);
|
|
}
|
|
|
|
void init_llist(struct llist* l)
|
|
/* initialize a linked list */
|
|
{
|
|
l->first = NULL;
|
|
l->last = NULL;
|
|
l->place = NULL;
|
|
}
|
|
|
|
void addtolist(struct llist* p, void* item)
|
|
/* append an item to a linked list */
|
|
{
|
|
struct el* x;
|
|
|
|
x = (struct el*)checkmalloc(sizeof(struct el));
|
|
x->next = NULL;
|
|
x->datum = item;
|
|
if (p->first == NULL) {
|
|
p->first = x;
|
|
p->last = x;
|
|
} else {
|
|
p->last->next = x;
|
|
p->last = x;
|
|
};
|
|
}
|
|
|
|
void* firstitem(struct llist* p)
|
|
/* find the first item in the list */
|
|
/* also initialize for a traversal using nextitem() */
|
|
{
|
|
if (p == NULL) {
|
|
return(NULL);
|
|
};
|
|
p->place = p->first;
|
|
if (p->place == NULL) {
|
|
return(NULL);
|
|
} else {
|
|
return(p->place->datum);
|
|
};
|
|
}
|
|
|
|
void* nextitem(struct llist* p)
|
|
/* return 'next' item in the list. Successive calls return successive */
|
|
/* items or NULL after the end of the list has been reached. firstitem() */
|
|
/* must be called prior to the first call to nextitem() */
|
|
{
|
|
if (p->place == NULL) {
|
|
return(NULL);
|
|
} else {
|
|
p->place = p->place->next;
|
|
if (p->place == NULL) {
|
|
return(NULL);
|
|
} else {
|
|
return(p->place->datum);
|
|
};
|
|
};
|
|
}
|
|
|
|
void freellist(struct llist* l)
|
|
/* frees up all dynamically allocated memory used to build the linked list */
|
|
{
|
|
void* p;
|
|
struct el* e;
|
|
struct el* olde;
|
|
|
|
/* printf("freellist\n"); */
|
|
if (l != NULL) {
|
|
p = firstitem(l);
|
|
while (p != NULL) {
|
|
free(p);
|
|
p = nextitem(l);
|
|
};
|
|
e = l->first;
|
|
while (e != NULL) {
|
|
olde = e;
|
|
e = e->next;
|
|
free(olde);
|
|
};
|
|
init_llist(l);
|
|
};
|
|
}
|
|
|
|
static void closebeam(struct voice* v)
|
|
/* called after a run of notes to be beamed together */
|
|
{
|
|
struct note* n;
|
|
struct feature* ft;
|
|
int stemup;
|
|
int ingrace;
|
|
|
|
if (cv->tuplenotes > 0) {
|
|
cv->thistuple->beamed = 0;
|
|
};
|
|
if (v->beamroot == v->beamend) {
|
|
ft = v->beamroot;
|
|
n = ft->item.voidptr;
|
|
n->beaming = single;
|
|
v->beamroot = NULL;
|
|
return;
|
|
};
|
|
if (v->beammax + v->beammin > 2*4) {
|
|
stemup = 0;
|
|
} else {
|
|
stemup = 1;
|
|
};
|
|
ft = v->beamroot;
|
|
ingrace = 0;
|
|
while ((ft != NULL) && (ft != v->beamend)) {
|
|
switch (ft->type) {
|
|
case NOTE:
|
|
if (ingrace == 0) {
|
|
n = ft->item.voidptr;
|
|
n->stemup = stemup;
|
|
};
|
|
break;
|
|
case GRACEON:
|
|
ingrace = 1;
|
|
break;
|
|
case GRACEOFF:
|
|
ingrace = 0;
|
|
break;
|
|
default:
|
|
break;
|
|
};
|
|
ft = ft->next;
|
|
};
|
|
if (ft == v->beamend) {
|
|
n = ft->item.voidptr;
|
|
n->stemup = stemup;
|
|
n->beaming = endbeam;
|
|
} else {
|
|
printf("closebeam: internal data error\n");
|
|
exit(1);
|
|
};
|
|
v->beamroot = NULL;
|
|
}
|
|
|
|
static void closegracebeam(struct voice* v)
|
|
/* called after a run of grace notes to be beamed together */
|
|
{
|
|
struct note* n;
|
|
struct feature* ft;
|
|
|
|
ft = v->gracebeamend;
|
|
if (ft == NULL) {
|
|
event_error("Missing grace notes");
|
|
} else {
|
|
n = ft->item.voidptr;
|
|
if (v->gracebeamroot == v->gracebeamend) {
|
|
n->beaming = single;
|
|
} else {
|
|
n->beaming = endbeam;
|
|
};
|
|
};
|
|
}
|
|
|
|
static void insertnote(struct feature* chordplace, struct feature* newfeature)
|
|
/* place NOTE in decreasing pitch order within chord */
|
|
{
|
|
struct note* n;
|
|
struct note* newnote;
|
|
struct feature* f;
|
|
struct feature* previous;
|
|
int foundplace;
|
|
|
|
newnote = newfeature->item.voidptr;
|
|
previous = chordplace;
|
|
f = chordplace->next;
|
|
foundplace = 0;
|
|
n = NULL;
|
|
while ((f != NULL)&&(f->type==NOTE)&&(foundplace == 0)) {
|
|
n = f->item.voidptr;
|
|
if (newnote->y > n->y) {
|
|
foundplace = 1;
|
|
} else {
|
|
previous = f;
|
|
f = f->next;
|
|
};
|
|
};
|
|
/* printvoiceline in drawtune.c expects the gchord or
|
|
* instructions to be associated with the first note in
|
|
* chord. If the notes are reordered then we need
|
|
* to move these fields.
|
|
* if previous == chordplace then move n->gchords and
|
|
* n->instructions to newnote->gchords and newnote->instructions
|
|
*/
|
|
if (previous == chordplace && n != NULL) {
|
|
newnote->gchords = n->gchords;
|
|
newnote->instructions = n->instructions;
|
|
n->gchords = NULL;
|
|
n->instructions = NULL;
|
|
}
|
|
newfeature->next = previous->next;
|
|
previous->next = newfeature;
|
|
if (newfeature->next == NULL) {
|
|
cv->last = newfeature;
|
|
};
|
|
}
|
|
|
|
static void beamitem(featuretype mytype, void* newitem, struct feature* x)
|
|
/* This routine is responsible for working out which notes are to be */
|
|
/* beamed together and recording this in the note structure record */
|
|
{
|
|
struct note* n;
|
|
|
|
/* deal with beaming here */
|
|
if (cv->ingrace) {
|
|
if ((mytype == NOTE) && (newitem != NULL)) {
|
|
n = newitem;
|
|
n->stemup = 1;
|
|
if (cv->gracebeamroot == NULL) {
|
|
cv->gracebeamroot = x;
|
|
cv->gracebeamend = x;
|
|
n->beaming = startbeam;
|
|
} else {
|
|
cv->gracebeamend = x;
|
|
n->beaming = midbeam;
|
|
};
|
|
};
|
|
} else {
|
|
if (cv->beamroot != NULL) {
|
|
switch (mytype) {
|
|
case NOTE:
|
|
n = newitem;
|
|
if (n->base_exp >= -2) {
|
|
n->beaming = single;
|
|
closebeam(cv);
|
|
} else {
|
|
/* extend beam */
|
|
if (n->y > cv->beammax) {
|
|
cv->beammax = n->y;
|
|
};
|
|
if (n->y < cv->beammin) {
|
|
cv->beammin = n->y;
|
|
};
|
|
cv->beamend = x;
|
|
n->beaming = midbeam;
|
|
};
|
|
break;
|
|
case KEY:
|
|
case REST:
|
|
case NOBEAM:
|
|
case SINGLE_BAR:
|
|
case DOUBLE_BAR:
|
|
case BAR_REP:
|
|
case REP_BAR:
|
|
case BAR1:
|
|
case REP_BAR2:
|
|
case DOUBLE_REP:
|
|
case THICK_THIN:
|
|
case THIN_THICK:
|
|
case TUPLE:
|
|
case MUSICSTOP:
|
|
/* closebeam */
|
|
closebeam(cv);
|
|
break;
|
|
default:
|
|
break;
|
|
};
|
|
} else {
|
|
if (mytype == NOTE) {
|
|
n = newitem;
|
|
if (n->base_exp >= -2) {
|
|
n->beaming = single;
|
|
} else {
|
|
n->beaming = startbeam;
|
|
cv->beammax = n->y;
|
|
cv->beammin = n->y;
|
|
cv->beamroot = x;
|
|
cv->beamend = x;
|
|
};
|
|
};
|
|
};
|
|
};
|
|
}
|
|
|
|
/* initialize a feature struct with empty values */
|
|
static void init_feature (struct feature *x, featuretype mytype)
|
|
{
|
|
x->next = NULL;
|
|
x->type = mytype;
|
|
x->item.voidptr = NULL;
|
|
x->xleft = 0;
|
|
x->xright = 0;
|
|
x->yup = 0;
|
|
x->ydown = 0;
|
|
}
|
|
|
|
static struct feature *addnumberfeature (featuretype mytype, int number)
|
|
/* handles numeric feature types
|
|
* may mark the end of a beamed section, so still need to call
|
|
* beamitem() on these
|
|
*/
|
|
{
|
|
struct feature *x;
|
|
|
|
if (cv == NULL) {
|
|
printf ("ERROR: no current voice in addfeature(status,type=%d\n", mytype);
|
|
//printf("status->inhead = %d status->inbody = %d\n", status->inhead, status->inbody);
|
|
exit (0);
|
|
}
|
|
x = (struct feature *)checkmalloc (sizeof (struct feature));
|
|
init_feature (x, mytype);
|
|
x->item.number = number;
|
|
if (cv->first == NULL) {
|
|
cv->first = x;
|
|
cv->last = x;
|
|
beamitem (mytype, NULL, x);
|
|
} else {
|
|
if ((cv->last == NULL) || (cv->last->next != NULL)) {
|
|
printf ("expecting NULL at list end!\n");
|
|
exit (0);
|
|
}
|
|
cv->last->next = x;
|
|
cv->last = x;
|
|
beamitem (mytype, NULL, x);
|
|
}
|
|
return (x);
|
|
}
|
|
|
|
static struct feature* addfeature(featuretype mytype, void* newitem)
|
|
/* append a new data element to the linked list for the current voice */
|
|
/* The element can be a note or lots of other things */
|
|
{
|
|
struct voice* p;
|
|
struct feature* x;
|
|
|
|
p = cv;
|
|
/* printf("in addfeature type=%d\n", mytype); */
|
|
if (cv == NULL) {
|
|
printf("ERROR: no current voice in addfeature type=%d\n", mytype);
|
|
printf("xinhead = %d xinbody = %d\n", xinhead, xinbody);
|
|
exit(0);
|
|
};
|
|
x = (struct feature*)checkmalloc(sizeof(struct feature));
|
|
init_feature (x, mytype);
|
|
x->item.voidptr = newitem;
|
|
if (cv->first == NULL) {
|
|
cv->first = x;
|
|
cv->last = x;
|
|
beamitem(mytype, newitem, x);
|
|
} else {
|
|
if ((cv->last == NULL)||(cv->last->next != NULL)) {
|
|
printf("expecting NULL at list end!\n");
|
|
exit(0);
|
|
};
|
|
if ((cv->inchord)&&(mytype==NOTE)) {
|
|
insertnote(cv->chordplace, x);
|
|
} else {
|
|
cv->last->next = x;
|
|
cv->last = x;
|
|
beamitem(mytype, newitem, x);
|
|
};
|
|
};
|
|
return(x);
|
|
}
|
|
|
|
struct llist* newlist()
|
|
/* create and initialize a new linked list */
|
|
{
|
|
struct llist* l;
|
|
|
|
l = (struct llist*)checkmalloc(sizeof(struct llist));
|
|
init_llist(l);
|
|
return(l);
|
|
}
|
|
|
|
static int notenum(int octave, char ch, cleftype_t * clef)
|
|
/* converts note to number for stave position */
|
|
/* note E is zero (bottom line of stave) */
|
|
{
|
|
int n;
|
|
|
|
n = 1 + (7 + ch - 'c') % 7 + 7 * (octave - 1);
|
|
/* apply shift associated with type of clef */
|
|
switch (clef->basic_clef) {
|
|
default:
|
|
case basic_clef_treble:
|
|
case basic_clef_auto:
|
|
case basic_clef_perc:
|
|
case basic_clef_none:
|
|
case basic_clef_undefined:
|
|
/* no shift */
|
|
break;
|
|
case basic_clef_bass:
|
|
n = n + 8;
|
|
break;
|
|
case basic_clef_alto:
|
|
n = n + 4;
|
|
break;
|
|
}
|
|
/* apply any shift from the placing of the clef */
|
|
n = n + (2 * clef->staveline);
|
|
/* We ignore octave shift from the clef. This is understood as an
|
|
* instruction to the player to play in a different octave, but
|
|
* the written notes do not change position.
|
|
*/
|
|
return (n);
|
|
}
|
|
|
|
int count_dots(int *base, int *base_exp, int n, int m)
|
|
/* convert fraction to 2^base_exp followed by dots */
|
|
/* previously used 1/base instead of 2^base_exp */
|
|
/* -1 indicates a note value which is impossible to represent */
|
|
{
|
|
int dots;
|
|
int start;
|
|
int a, b;
|
|
|
|
*base = 1; /* set default value if we fail */
|
|
*base_exp = 0;
|
|
if ((n<1)||(m<1)) {
|
|
return(-1);
|
|
};
|
|
/* check denominator is power of 2 */
|
|
a = m;
|
|
while (a>1) {
|
|
if (a%2 == 1) {
|
|
return(-1);
|
|
};
|
|
a = a/2;
|
|
};
|
|
dots = 0;
|
|
start = m;
|
|
while (start < n) {
|
|
start = start*2;
|
|
*base_exp = *base_exp + 1;
|
|
};
|
|
while (start > n) {
|
|
start = start/2;
|
|
*base_exp = *base_exp - 1;
|
|
};
|
|
if (start == 0) {
|
|
printf("Problem with %d / %d\n", n, m);
|
|
exit(0);
|
|
};
|
|
*base = m/start;
|
|
a = n - start;
|
|
b = start;
|
|
while (a>0) {
|
|
dots = dots + 1;
|
|
b = b/2;
|
|
a = a - b;
|
|
if (a< 0) {
|
|
return(-1);
|
|
};
|
|
};
|
|
return(dots);
|
|
}
|
|
|
|
static char* decstring(int decorators[])
|
|
/* creates a string of decorators (ornament, staccato, roll, up-bow etc.) */
|
|
/* from a boolean array */
|
|
{
|
|
int i, j;
|
|
char decs[DECSIZE+1];
|
|
|
|
j = 0;
|
|
for (i=0; i<DECSIZE; i++) {
|
|
if (decorators[i]) {
|
|
decs[j] = decorations[i];
|
|
j = j + 1;
|
|
};
|
|
};
|
|
decs[j] = '\0';
|
|
if (j==0) {
|
|
return(NULL);
|
|
} else {
|
|
return(addstring(decs));
|
|
};
|
|
}
|
|
|
|
static struct note* newnote(decorators, clef, xaccidental, xmult, xnote, xoctave,
|
|
a, b)
|
|
int decorators[DECSIZE];
|
|
cleftype_t *clef; /* [JA] 2020-10-19 */
|
|
int xmult;
|
|
char xaccidental, xnote;
|
|
int xoctave;
|
|
int a, b;
|
|
/* create and set up the fields for a note structure */
|
|
{
|
|
struct note* n;
|
|
|
|
n = (struct note*)checkmalloc(sizeof(struct note));
|
|
setfract(&n->len, a, b);
|
|
reducef(&n->len);
|
|
n->dots = count_dots(&n->base, &n->base_exp, n->len.num, n->len.denom);
|
|
/*
|
|
if (n->dots == -1) {
|
|
event_error("Illegal note length");
|
|
};
|
|
*/
|
|
n->accents = decstring(decorators);
|
|
n->accidental = xaccidental;
|
|
n->acc_offset = 0;
|
|
n->fliphead = 0;
|
|
n->mult = xmult;
|
|
n->octave = xoctave;
|
|
n->pitch = xnote;
|
|
n->y = notenum(xoctave, xnote, clef);
|
|
if (n->y < 4) {
|
|
n->stemup = 1;
|
|
} else {
|
|
n->stemup = 0;
|
|
};
|
|
n->beaming = single; /* initial default value */
|
|
n->syllables = NULL;
|
|
if (cv->ingrace) {
|
|
n->gchords = NULL;
|
|
n->instructions = NULL;
|
|
} else {
|
|
n->gchords = cv->gchords_pending;
|
|
cv->gchords_pending = NULL;
|
|
n->instructions = cv->instructions_pending;
|
|
cv->instructions_pending = NULL;
|
|
};
|
|
n->stemlength = 0.0;
|
|
return(n);
|
|
}
|
|
|
|
static struct rest* newrest(int a, int b, int multi)
|
|
/* create and set up a new rest structure */
|
|
{
|
|
struct rest* n;
|
|
|
|
n = (struct rest*)checkmalloc(sizeof(struct rest));
|
|
setfract(&n->len, a, b);
|
|
n->dots = count_dots(&n->base, &n->base_exp, a, b);
|
|
if (n->dots == -1) {
|
|
event_error("Illegal rest length");
|
|
};
|
|
n->multibar = multi;
|
|
if (cv->ingrace) {
|
|
n->gchords = NULL;
|
|
n->instructions = NULL;
|
|
} else {
|
|
n->gchords = cv->gchords_pending;
|
|
cv->gchords_pending = NULL;
|
|
n->instructions = cv->instructions_pending;
|
|
cv->instructions_pending = NULL;
|
|
};
|
|
return(n);
|
|
}
|
|
|
|
static void addunits(f, n, m)
|
|
struct fract* f;
|
|
int n, m;
|
|
/* add n/m to fraction pointed to by f */
|
|
{
|
|
f->num = n*f->denom*(cv->unitlen.num) + f->num*(m*cv->unitlen.denom);
|
|
f->denom = (m*cv->unitlen.denom)*f->denom;
|
|
reducef(f);
|
|
}
|
|
|
|
static void addfractions(f,n,m)
|
|
struct fract* f;
|
|
int n,m;
|
|
{
|
|
f->num = n*f->denom + f->num*m;
|
|
f->denom = m*f->denom;
|
|
reducef(f);
|
|
}
|
|
|
|
|
|
|
|
static struct voice* newvoice(int n)
|
|
/* create and set up a new voice data structure */
|
|
{
|
|
struct voice* v;
|
|
|
|
v = (struct voice*)checkmalloc(sizeof(struct voice));
|
|
v->first = NULL;
|
|
v->last = NULL;
|
|
v->voiceno = n;
|
|
v->octaveshift = thetune.octaveshift;
|
|
setfract(&v->unitlen, thetune.unitlen.num, thetune.unitlen.denom);
|
|
v->changetime = 0;
|
|
v->inslur = 0;
|
|
v->ingrace = 0;
|
|
v->inchord = 0;
|
|
v->tuplenotes = 0;
|
|
v->thistuple = NULL;
|
|
v->tuple_count = 0;
|
|
v->brokenpending = -1;
|
|
v->tiespending = 0;
|
|
v->slurpending = 0;
|
|
v->slurcount = 0;
|
|
v->barno = 0;
|
|
v->barchecking = thetune.barchecking;
|
|
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");
|
|
exit(0);
|
|
} else {
|
|
v->keysig = newkey(thetune.keysig->name, thetune.keysig->sharps,
|
|
thetune.keysig->map, thetune.keysig->mult);
|
|
};
|
|
v->tempo = NULL;
|
|
setfract(&v->barcount, 0, 1);
|
|
copy_timesig(&v->timesig, &thetune.timesig);
|
|
v->lastnote = NULL;
|
|
v->laststart = NULL;
|
|
v->lastend = NULL;
|
|
v->line = header;
|
|
v->thisstart = NULL;
|
|
v->thisend = NULL;
|
|
v->gchords_pending = NULL;
|
|
v->instructions_pending = NULL;
|
|
v->beamed_tuple_pending = 0;
|
|
v->linestart = NULL;
|
|
v->lineend = NULL;
|
|
v->more_lyrics = 0;
|
|
v->lyric_errors = 0;
|
|
v->thischord = NULL;
|
|
v->chordplace = NULL;
|
|
v->beamroot = NULL;
|
|
v->beamend = NULL;
|
|
v->gracebeamroot = NULL;
|
|
v->gracebeamend = NULL;
|
|
return(v);
|
|
}
|
|
|
|
static void setvoice(int n)
|
|
/* set current voice to voice n. If voice n does not exist, create it */
|
|
{
|
|
struct voice* v;
|
|
struct el* l;
|
|
int done;
|
|
|
|
if (thetune.voices.first == NULL) {
|
|
cv = newvoice(n);
|
|
v = cv;
|
|
addtolist(&thetune.voices, (void*)v);
|
|
} else {
|
|
l = thetune.voices.first;
|
|
done = 0;
|
|
while ((done == 0) && (l != NULL)) {
|
|
if (((struct voice*)l->datum)->voiceno == n) {
|
|
done = 1;
|
|
cv = (struct voice*)l->datum;
|
|
} else {
|
|
l = l->next;
|
|
};
|
|
};
|
|
if (done == 0) {
|
|
cv = newvoice(n);
|
|
v = cv;
|
|
addtolist(&thetune.voices, (void*)v);
|
|
};
|
|
};
|
|
}
|
|
|
|
static void init_tune(struct tune* t, int x)
|
|
/* initialize tune structure */
|
|
{
|
|
t->no = x;
|
|
t->octaveshift = 0;
|
|
init_llist(&t->title);
|
|
t->composer = NULL;
|
|
t->origin = NULL;
|
|
t->parts = NULL;
|
|
init_llist(&t->notes);
|
|
init_llist(&t->voices);
|
|
init_timesig(&t->timesig);
|
|
setfract(&t->unitlen, 0, 1);
|
|
t->cv = NULL;
|
|
t->keysig = NULL;
|
|
init_new_clef (&t->clef);
|
|
get_standard_clef ("treble", &t->clef); /* default to treble clef */
|
|
t->tempo = NULL;
|
|
init_llist(&t->words);
|
|
};
|
|
|
|
static void freekey(struct key* k)
|
|
/* free up memory allocated for key data structure */
|
|
{
|
|
if (k != NULL) {
|
|
if (k->name != NULL) {
|
|
free(k->name);
|
|
};
|
|
free(k);
|
|
};
|
|
}
|
|
|
|
static void freetempo(struct atempo *t)
|
|
/* free up memory allocated for temp data structure */
|
|
{
|
|
if (t->pre != NULL) {
|
|
free(t->pre);
|
|
};
|
|
if (t->post != NULL) {
|
|
free(t->post);
|
|
};
|
|
}
|
|
|
|
static void freefeature(void* item, featuretype type)
|
|
/* free memory allocated for feature in voice */
|
|
{
|
|
struct note *n;
|
|
struct rest *r;
|
|
|
|
switch(type) {
|
|
case NOTE:
|
|
case CHORDNOTE:
|
|
n = item;
|
|
if (n->accents != NULL) {
|
|
free(n->accents);
|
|
};
|
|
if (n->syllables != NULL) {
|
|
freellist(n->syllables);
|
|
free(n->syllables);
|
|
};
|
|
if (n->gchords != NULL) {
|
|
freellist(n->gchords);
|
|
free(n->gchords);
|
|
};
|
|
if (n->instructions != NULL) {
|
|
freellist(n->instructions);
|
|
free(n->instructions);
|
|
};
|
|
free(n);
|
|
break;
|
|
case REST:
|
|
r = item;
|
|
if (r->gchords != NULL) {
|
|
freellist(r->gchords);
|
|
free(r->gchords);
|
|
};
|
|
if (r->instructions != NULL) {
|
|
freellist(r->instructions);
|
|
free(r->instructions);
|
|
};
|
|
free(r);
|
|
break;
|
|
case KEY:
|
|
freekey(item);
|
|
break;
|
|
case TEMPO:
|
|
freetempo(item);
|
|
break;
|
|
case CLEF:
|
|
case TIME:
|
|
case PART:
|
|
case CHORDON:
|
|
case TUPLE:
|
|
case SLUR_ON:
|
|
case TIE:
|
|
case PLAY_ON_REP:
|
|
case LEFT_TEXT:
|
|
case PRINTLINE:
|
|
if (item != NULL) {
|
|
free(item);
|
|
};
|
|
break;
|
|
default:
|
|
break;
|
|
};
|
|
}
|
|
|
|
static void freevoice(struct voice* v)
|
|
/* free up memory allocated for voice data structure and voice data */
|
|
{
|
|
struct feature* ft;
|
|
struct feature* oldft;
|
|
|
|
ft = v->first;
|
|
while (ft != NULL) {
|
|
freefeature(ft->item.voidptr, ft->type);
|
|
oldft = ft;
|
|
ft = ft->next;
|
|
free(oldft);
|
|
};
|
|
if (v->keysig != NULL) {
|
|
freekey(v->keysig);
|
|
v->keysig = NULL;
|
|
};
|
|
if (v->tempo != NULL) {
|
|
freetempo(v->tempo);
|
|
free (v->tempo);
|
|
v->tempo = NULL;
|
|
};
|
|
if (v->clef != NULL) {
|
|
free(v->clef);
|
|
};
|
|
v->clef = NULL;
|
|
}
|
|
|
|
static void freetune(struct tune* t)
|
|
/* free up all dynamically allocated memory associated with tune */
|
|
{
|
|
struct voice* v;
|
|
|
|
if (t->composer != NULL) {
|
|
free(t->composer);
|
|
t->composer = NULL;
|
|
};
|
|
if (t->origin != NULL) {
|
|
free(t->origin);
|
|
t->origin = NULL;
|
|
};
|
|
if (t->parts != NULL) {
|
|
free(t->parts);
|
|
t->parts = NULL;
|
|
};
|
|
freellist(&t->title);
|
|
freellist(&t->notes);
|
|
if (t->keysig != NULL) {
|
|
freekey(t->keysig);
|
|
t->keysig = NULL;
|
|
};
|
|
if (t->tempo != NULL) {
|
|
freetempo(t->tempo);
|
|
free(t->tempo);
|
|
t->tempo = NULL;
|
|
};
|
|
v = firstitem(&t->voices);
|
|
while (v != NULL) {
|
|
freevoice(v);
|
|
v = nextitem(&t->voices);
|
|
};
|
|
freellist(&t->voices);
|
|
freellist(&t->words);
|
|
}
|
|
|
|
static int checkmatch(int refno)
|
|
/* compares current reference number against list of numbers */
|
|
/* following -e argument */
|
|
/* returns 1 if tune has been selected and 0 otherwise */
|
|
{
|
|
int select;
|
|
int n1, n2;
|
|
char* place;
|
|
|
|
place = matchstring;
|
|
if (strlen(matchstring)==0) {
|
|
select = 1;
|
|
} else {
|
|
select = 0;
|
|
while ((select==0)&&(*place >= '0')&&(*place <='9')) {
|
|
n1 = readnump(&place);
|
|
if (n1 == refno) {
|
|
select = 1;
|
|
} else {
|
|
if (*place == ',') {
|
|
place = place+1;
|
|
} else {
|
|
if (*place == '-') {
|
|
place = place+1;
|
|
n2 = readnump(&place);
|
|
if ((refno >= n1)&&(refno <= n2)) {
|
|
select = 1;
|
|
} else {
|
|
if (*place == ',') {
|
|
place = place+1;
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
if ((select==0)&&(*place != '\0')) {
|
|
event_warning("Number list after -e not fully parsed");
|
|
};
|
|
};
|
|
return(select);
|
|
}
|
|
|
|
void event_init(argc, argv, filename)
|
|
int argc;
|
|
char* argv[];
|
|
char** filename;
|
|
/* initialization routine - called once at the start of the program */
|
|
/* interprets the parameters in argv */
|
|
{
|
|
char* place;
|
|
int filearg;
|
|
int refmatch;
|
|
int papsize, margins, newscale;
|
|
int ier;
|
|
int j;
|
|
|
|
if (getarg("-ver",argc, argv) != -1) {
|
|
printf("%s\n",VERSION);
|
|
exit(0);
|
|
}
|
|
if (getarg("-d", argc, argv) != -1) {
|
|
debugging = 1;
|
|
} else {
|
|
debugging = 0;
|
|
};
|
|
if (getarg("-E", argc, argv) != -1) {
|
|
eps_out = 1;
|
|
} else {
|
|
eps_out = 0;
|
|
};
|
|
if (getarg("-OCC",argc,argv) != -1) oldchordconvention=1;
|
|
if (getarg("-V", argc, argv) != -1) {
|
|
separate_voices = 1;
|
|
} else {
|
|
separate_voices = 0;
|
|
};
|
|
if (getarg("-x", argc, argv) != -1) {
|
|
print_xref = 1;
|
|
} else {
|
|
print_xref = 0;
|
|
};
|
|
if (getarg("-N", argc, argv) != -1) {
|
|
pagenumbering = 1;
|
|
} else {
|
|
pagenumbering = 0;
|
|
};
|
|
if (getarg("-l", argc, argv) != -1) {
|
|
landscape = 1;
|
|
} else {
|
|
landscape = 0;
|
|
};
|
|
newscale = getarg("-s", argc, argv);
|
|
if ((newscale != -1) && (argc >= newscale+1)) { /* [SS] 2015-02-22 */
|
|
setscaling(argv[newscale]);
|
|
} else {
|
|
setscaling("");
|
|
};
|
|
margins = getarg("-M", argc, argv);
|
|
if ((margins != -1) && (argc >= margins)) {
|
|
setmargins(argv[margins]);
|
|
} else {
|
|
setmargins("");
|
|
};
|
|
papsize = getarg("-P", argc, argv);
|
|
if ((papsize != -1) && (argc >= papsize)) {
|
|
setpagesize(argv[papsize]);
|
|
} else {
|
|
setpagesize("");
|
|
};
|
|
barnums = getarg("-k",argc,argv);
|
|
ier = 0;
|
|
if ((barnums != -1) && (argc > barnums)) ier = sscanf(argv[barnums],"%d",&nnbars);
|
|
if ((barnums != -1) && (ier <1)) nnbars = 1;
|
|
|
|
refmatch = getarg("-e", argc, argv);
|
|
if (refmatch == -1) {
|
|
*matchstring = '\0';
|
|
} else {
|
|
if (strlen(argv[refmatch]) < 255) {
|
|
strcpy(matchstring, argv[refmatch]);
|
|
} else {
|
|
event_error("Exceeded character limit for -e string");
|
|
exit(1);
|
|
};
|
|
};
|
|
if ((getarg("-h", argc, argv) != -1) || (argc < 2)) {
|
|
printf("yaps version %s\n",VERSION);
|
|
printf("Usage: yaps <abc file> [<options>]\n");
|
|
printf(" possible options are -\n");
|
|
printf(" -ver :prints version number and exits\n");
|
|
printf(" -d : debug - display data structure\n");
|
|
printf(" -e <list> : draw tunes with reference numbers in list\n");
|
|
printf(" list is comma-separated and may contain ranges\n");
|
|
printf(" but no spaces e.g. 1,3,7-20\n");
|
|
printf(" -E : generate Encapsulated PostScript\n");
|
|
printf(" -l : landscape mode\n");
|
|
printf(" -M XXXxYYY : set margin sizes in points\n");
|
|
printf(" 28.3 points = 1cm, 72 points = 1 inch\n");
|
|
printf(" -N : add page numbering\n");
|
|
printf(" -k [nn] : number every nn bars\n");
|
|
printf(" -o <filename> : specify output file\n");
|
|
printf(" -P ss : paper size; 0 is A4, 1 is US Letter\n");
|
|
printf(" or XXXxYYY to set size in points\n");
|
|
printf(" -s XX : scaling factor (default is 0.7)\n");
|
|
printf(" -V : separate voices in multi-voice tune\n");
|
|
printf(" -x : print tune number in X: field\n");
|
|
printf(" -OCC : old chord convention (eg. +CE+)\n");
|
|
printf("Takes an abc music file and converts it to PostScript.\n");
|
|
printf("If no output filename is given, then by default it is\n");
|
|
printf("the input filename but with extension .ps .\n");
|
|
exit(0);
|
|
} else {
|
|
*filename = argv[1];
|
|
};
|
|
fileopen = 0;
|
|
filearg = getarg("-o", argc, argv);
|
|
/* beware of security risk from buffer overflows here [JA] 2020-11-01*/
|
|
if (filearg != -1) {
|
|
if (strlen(argv[filearg]) > MAX_OUTPUTROOT) /* [JA] 2020-11-01 */
|
|
{
|
|
printf("Specified output filename exceeds limit.\n");
|
|
exit(1);
|
|
}
|
|
#ifdef NO_SNPRINTF
|
|
sprintf(outputname, "%s",argv[filearg]); /* [SS] 2020-11-01 */
|
|
#else
|
|
snprintf(outputname, MAX_OUTPUTROOT,"%s",argv[filearg]);
|
|
#endif
|
|
} else {
|
|
if (strlen(argv[1]) > MAX_OUTPUTROOT)
|
|
{
|
|
printf("Implied output filename exceeds limit.\n");
|
|
exit(1);
|
|
}
|
|
#ifdef NO_SNPRINTF
|
|
sprintf(outputname,"%s", argv[1]); /* [SS] 2020-11-01 */
|
|
#else
|
|
snprintf(outputname,MAX_OUTPUTROOT,"%s", argv[1]);
|
|
#endif
|
|
place = strchr(outputname, '.');
|
|
if (place == NULL) {
|
|
strcat(outputname, ".ps");
|
|
} else {
|
|
strcpy(place, ".ps");
|
|
};
|
|
if (strcmp(argv[1], outputname)==0) {
|
|
printf("argument must be abc file, not PostScript file\n");
|
|
exit(1);
|
|
};
|
|
};
|
|
/* create filename root for EPS output */
|
|
strcpy(outputroot, outputname);
|
|
place = strchr(outputroot, '.');
|
|
if (place != NULL) {
|
|
*place = '\0';
|
|
};
|
|
xinbody =0;
|
|
xinhead = 0;
|
|
suppress = 0;
|
|
init_tune(&thetune, -1);
|
|
setup_fonts();
|
|
/* open_output_file(outputname); */
|
|
for (j=0;j<DECSIZE;j++) dummydecorator[j] = 0;
|
|
}
|
|
|
|
static void check_voice_end(struct voice* v)
|
|
/* make sure there are no unfinished structures */
|
|
{
|
|
if (v->inchord) {
|
|
event_error("incomplete chord at end of voice");
|
|
v->inchord = 0;
|
|
};
|
|
if (v->brokenpending != -1) {
|
|
event_error("incomplete broken rhythm at end of voice");
|
|
v->brokenpending = -1;
|
|
};
|
|
if ((v->slurcount > 0)||(v->slurpending)) {
|
|
event_error("incomplete slur at end of voice");
|
|
v->slurcount = 0;
|
|
v->slurpending = 0;
|
|
};
|
|
if (v->tiespending) {
|
|
event_error("incomplete ties at end of voice");
|
|
v->tiespending = 0;
|
|
};
|
|
if (v->gchords_pending != NULL) {
|
|
freellist(v->gchords_pending);
|
|
free(v->gchords_pending);
|
|
v->gchords_pending = NULL;
|
|
};
|
|
if (v->instructions_pending != NULL) {
|
|
freellist(v->instructions_pending);
|
|
free(v->instructions_pending);
|
|
v->instructions_pending = NULL;
|
|
};
|
|
if (v->tuplenotes > 0) {
|
|
event_error("incomplete tuple at end of voice");
|
|
v->tuplenotes = 0;
|
|
};
|
|
}
|
|
|
|
static void check_tune_end(struct tune* t)
|
|
/* check that all voices have been completed properly */
|
|
{
|
|
struct voice* v;
|
|
|
|
v = firstitem(&t->voices);
|
|
while (v != NULL) {
|
|
check_voice_end(v);
|
|
v = nextitem(&t->voices);
|
|
};
|
|
}
|
|
|
|
void event_eof()
|
|
/* end of input file has been encountered */
|
|
{
|
|
if (xinbody) {
|
|
check_tune_end(&thetune);
|
|
printtune(&thetune);
|
|
};
|
|
freetune(&thetune);
|
|
close_output_file();
|
|
}
|
|
|
|
void event_blankline()
|
|
/* A blank line has been encountered */
|
|
{
|
|
if (xinbody) {
|
|
check_tune_end(&thetune);
|
|
printtune(&thetune);
|
|
};
|
|
freetune(&thetune);
|
|
xinbody = 0;
|
|
xinhead = 0;
|
|
suppress = 0;
|
|
parseroff();
|
|
}
|
|
|
|
void event_text(p)
|
|
/* Text outside an abc tune has been encountered */
|
|
char *p;
|
|
{
|
|
}
|
|
|
|
void event_x_reserved(p)
|
|
char p;
|
|
{
|
|
}
|
|
|
|
void event_abbreviation(symbol, string, container)
|
|
/* abbreviation declaratiion - handled by parser. Ignore it here */
|
|
char symbol;
|
|
char *string;
|
|
char container;
|
|
{
|
|
}
|
|
|
|
void event_acciaccatura()
|
|
{
|
|
/* does nothing but outputs a / in toabc.c */
|
|
return;
|
|
}
|
|
|
|
/* [SS] 2015-03-23 */
|
|
void event_start_extended_overlay()
|
|
{
|
|
event_error("extended overlay not implemented in yaps");
|
|
}
|
|
|
|
void event_stop_extended_overlay()
|
|
{
|
|
event_error("extended overlay not implemented in yaps");
|
|
}
|
|
|
|
|
|
void event_split_voice()
|
|
{
|
|
addnumberfeature(SPLITVOICE, lineno);
|
|
event_error("voice split not implemented in yaps");
|
|
}
|
|
|
|
void event_tex(s)
|
|
char *s;
|
|
/* A TeX command has been found in the abc */
|
|
{
|
|
}
|
|
|
|
void event_linebreak()
|
|
/* A linebreak has been encountered */
|
|
{
|
|
/* [SS] 2015-11-15 * changed (void*) to (int *) */
|
|
if (xinbody) {
|
|
addnumberfeature(LINENUM, lineno);
|
|
};
|
|
}
|
|
|
|
static void tidy_ties()
|
|
/* create CLOSE_TIE features for ties straddling two lines of music */
|
|
/* CLOSE_TIE appears in the new music line */
|
|
{
|
|
struct feature* ft;
|
|
struct slurtie* s;
|
|
int i;
|
|
|
|
for (i=0; i<cv->tiespending; i++) {
|
|
ft = cv->tie_place[i]; /* pointer to TIE feature */
|
|
s = ft->item.voidptr;
|
|
addfeature(CLOSE_TIE, s);
|
|
};
|
|
}
|
|
|
|
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");
|
|
cv->more_lyrics = 0;
|
|
};
|
|
if ((cv->line == header) || (cv->line == newline)) {
|
|
addfeature(CLEF, newclef(cv->clef));
|
|
addfeature(KEY, newkey(cv->keysig->name, cv->keysig->sharps,
|
|
cv->keysig->map, cv->keysig->mult));
|
|
if ((cv->line == header)||(cv->changetime)) {
|
|
timesig_details_t *timesig;
|
|
|
|
timesig = newtimesig();
|
|
copy_timesig(timesig, &parser_voice->timesig);
|
|
addfeature (TIME, timesig);
|
|
cv->changetime = 0;
|
|
};
|
|
cv->line = midline;
|
|
tidy_ties();
|
|
};
|
|
cv->lineend = NULL;
|
|
}
|
|
|
|
static void divide_ties()
|
|
/* mark unresolved ties and slurs as straddling two lines of music */
|
|
{
|
|
struct feature* ft;
|
|
struct slurtie* s;
|
|
int i;
|
|
|
|
for (i=0; i<cv->tiespending; i++) {
|
|
ft = cv->tie_place[i]; /* pointer to TIE feature */
|
|
s = ft->item.voidptr;
|
|
s->crossline = 1;
|
|
};
|
|
for (i=0; i<cv->slurcount; i++) {
|
|
s = cv->slur_place[i];
|
|
s->crossline = 1;
|
|
};
|
|
}
|
|
|
|
/* A score_linebreak has been encountered [JA] 2020-10-07*/
|
|
void event_score_linebreak (char ch)
|
|
{
|
|
|
|
if (xinbody) {
|
|
/* end current score line - code from endmusicline */
|
|
cv->lineend = addnumberfeature (MUSICSTOP, 0);
|
|
addfeature (PRINTLINE, newvertspacing ());
|
|
cv->line = newline;
|
|
divide_ties ();
|
|
/* start new score line - startmusicline */
|
|
event_startmusicline ();
|
|
}
|
|
}
|
|
|
|
void event_endmusicline(endchar)
|
|
char endchar;
|
|
/* We are at the end of a line of abc notes */
|
|
{
|
|
cv->lineend = addnumberfeature(MUSICSTOP, 0);
|
|
if ((endchar == ' ') || (endchar == '!')) {
|
|
addfeature(PRINTLINE, newvertspacing());
|
|
cv->line = newline;
|
|
divide_ties();
|
|
};
|
|
}
|
|
|
|
void event_error(s)
|
|
char *s;
|
|
/* report any error message */
|
|
{
|
|
printf("Error in line %d : %s\n", lineno, s);
|
|
}
|
|
|
|
void event_warning(s)
|
|
char *s;
|
|
/* report any warning message */
|
|
{
|
|
printf("Warning in line %d : %s\n", lineno, s);
|
|
}
|
|
|
|
void event_comment(s)
|
|
char *s;
|
|
/* A comment has been encountered in the input */
|
|
{
|
|
}
|
|
|
|
int make_open()
|
|
/* called as fileopen = make_open() */
|
|
/* if file is not already open, open it and set fileopen */
|
|
{
|
|
if (fileopen == 0) {
|
|
open_output_file(outputname, (void*)NULL);
|
|
fileopen = 1;
|
|
};
|
|
return(1);
|
|
}
|
|
|
|
void event_specific(p, str)
|
|
char *p; /* first word after %% */
|
|
char *str; /* string following first word */
|
|
/* The special comment %% has been found */
|
|
/* abc2midi uses it for the %%MIDI commands */
|
|
/* yaps implements some of the abc2ps commands */
|
|
{
|
|
char* s;
|
|
double vspace;
|
|
char units[80];
|
|
int count;
|
|
|
|
/* ensure file has been opened since most commands require this */
|
|
if (!eps_out) {
|
|
fileopen = make_open();
|
|
};
|
|
s = str;
|
|
skipspace(&s);
|
|
if (strcmp(p, "newpage") == 0) {
|
|
if (xinbody == 0) {
|
|
if (fileopen) {
|
|
newpage();
|
|
};
|
|
} else {
|
|
addfeature(NEWPAGE, (void*)NULL);
|
|
};
|
|
};
|
|
if (strcmp(p, "text") == 0) {
|
|
if (xinbody == 0) {
|
|
if (fileopen) {
|
|
lefttext(s);
|
|
};
|
|
} else {
|
|
addfeature(LEFT_TEXT, addstring(s));
|
|
};
|
|
};
|
|
if ((strcmp(p, "centre") == 0) || (strcmp(p, "center") == 0)) {
|
|
if (xinbody == 0) {
|
|
if (fileopen) {
|
|
centretext(s);
|
|
};
|
|
} else {
|
|
addfeature(CENTRE_TEXT, addstring(s));
|
|
};
|
|
};
|
|
if (strcmp(p, "vskip") == 0) {
|
|
count = sscanf(s, "%lf%s", &vspace, units);
|
|
if (count > 0) {
|
|
if ((count >= 2) && (strncmp(units, "cm", 2) == 0)) {
|
|
vspace = vspace*28.3;
|
|
};
|
|
if ((count >= 2) && (strncmp(units, "in", 2) == 0)) {
|
|
vspace = vspace*72.0 ;
|
|
};
|
|
if (xinbody == 0) {
|
|
if (fileopen) {
|
|
vskip(vspace);
|
|
};
|
|
} else {
|
|
addnumberfeature(VSKIP, (int)vspace);
|
|
};
|
|
};
|
|
};
|
|
if (strcmp(p, "chordsabove") == 0) {
|
|
gchords_above = 1;
|
|
};
|
|
if (strcmp(p, "chordsbelow") == 0) {
|
|
gchords_above = 0;
|
|
};
|
|
/* look for font-related commands */
|
|
font_command(p, s);
|
|
}
|
|
|
|
void event_field(k, f)
|
|
char k;
|
|
char *f;
|
|
/* A field line has been encountered in the input abc */
|
|
{
|
|
switch (k) {
|
|
case 'T':
|
|
if (debugging) {
|
|
printf("T:%s\n", f);
|
|
};
|
|
addtolist(&thetune.title, addstring(f));
|
|
break;
|
|
case 'C':
|
|
if (thetune.composer != NULL) {
|
|
event_error("More than one C: field in tune");
|
|
} else {
|
|
thetune.composer = addstring(f);
|
|
};
|
|
break;
|
|
case 'O':
|
|
if (thetune.origin != NULL) {
|
|
event_error("More than one O: field in tune");
|
|
} else {
|
|
thetune.origin = addstring(f);
|
|
};
|
|
break;
|
|
case 'W':
|
|
if (debugging) {
|
|
printf("W:%s\n", f);
|
|
};
|
|
addtolist(&thetune.words, addstring(f));
|
|
break;
|
|
case 'N':
|
|
addtolist(&thetune.notes, addstring(f));
|
|
break;
|
|
default:
|
|
break;
|
|
};
|
|
}
|
|
|
|
struct feature* findbar(struct feature* wordplace, int* errors)
|
|
/* hunts through voice to find next bar data structure */
|
|
{
|
|
struct feature* ft;
|
|
|
|
ft = wordplace;
|
|
while ((ft != NULL) && (ft != cv->lineend) && (ft->type != MUSICSTOP) &&
|
|
(ft->type != SINGLE_BAR) && (ft->type != DOUBLE_BAR) &&
|
|
(ft->type != BAR_REP) && (ft->type != REP_BAR) &&
|
|
(ft->type != DOUBLE_REP) && (ft->type != THICK_THIN) &&
|
|
(ft->type != THIN_THICK) && (ft->type != BAR1) &&
|
|
(ft->type != REP_BAR2)) {
|
|
ft = ft->next;
|
|
};
|
|
if ((ft == NULL) || (ft == cv->lineend) || (ft->type == MUSICSTOP)) {
|
|
*errors = *errors + 1;
|
|
} else {
|
|
ft = ft->next;
|
|
};
|
|
return(ft);
|
|
}
|
|
|
|
struct feature* apply_syll(char* s, struct feature* wordplace, int* errors)
|
|
/* attaches a syllable from the lyrics to the appropriate note */
|
|
{
|
|
struct note* n;
|
|
struct feature* ft;
|
|
int inchord;
|
|
|
|
ft = wordplace;
|
|
inchord = 0;
|
|
while ((ft != NULL) && (ft != cv->lineend) &&
|
|
(ft->type != NOTE)) {
|
|
if (ft->type == CHORDON) {
|
|
inchord = 1;
|
|
};
|
|
ft = ft->next;
|
|
/* skip over any grace notes */
|
|
if ((ft != NULL) && (ft->type == GRACEON)) {
|
|
while ((ft != NULL) && (ft != cv->lineend) &&
|
|
(ft->type != GRACEOFF)) {
|
|
ft = ft->next;
|
|
};
|
|
};
|
|
};
|
|
if ((ft == NULL) || (ft == cv->lineend)) {
|
|
*errors = *errors + 1;
|
|
return(ft);
|
|
};
|
|
if (ft->type == NOTE) {
|
|
n = ft->item.voidptr;
|
|
if (n->syllables == NULL) {
|
|
n->syllables = newlist();
|
|
};
|
|
/*
|
|
if (strlen(s) < 80) {
|
|
ISOdecode(s, isocode);
|
|
addtolist(n->syllables, addstring(isocode));
|
|
} else {
|
|
*/
|
|
addtolist(n->syllables, addstring(s));
|
|
/*
|
|
};
|
|
*/
|
|
};
|
|
ft = ft->next;
|
|
/* skip over any chord */
|
|
if (inchord) {
|
|
while ((ft != NULL) && (ft->type != CHORDOFF) && (ft != cv->lineend)) {
|
|
ft = ft->next;
|
|
};
|
|
};
|
|
return(ft);
|
|
}
|
|
|
|
void event_words(p, continuation)
|
|
char* p;
|
|
int continuation;
|
|
/* A line of lyrics (w: ) has been encountered in the abc */
|
|
{
|
|
struct vstring syll;
|
|
char* q;
|
|
struct feature* wordplace;
|
|
unsigned char ch;
|
|
int errors;
|
|
|
|
if (!xinbody) {
|
|
if (!suppress) {
|
|
event_error("w: field outside tune body");
|
|
};
|
|
return;
|
|
};
|
|
wordplace = cv->linestart;
|
|
if (cv->more_lyrics) {
|
|
errors = cv->lyric_errors;
|
|
} else {
|
|
errors = 0;
|
|
};
|
|
if (wordplace == NULL) {
|
|
event_error("No notes to match words");
|
|
return;
|
|
};
|
|
initvstring(&syll);
|
|
q = p;
|
|
skipspace(&q);
|
|
while (*q != '\0') {
|
|
clearvstring(&syll);
|
|
ch = *q;
|
|
while(ch=='|') {
|
|
wordplace = findbar(wordplace, &errors);
|
|
q++;
|
|
ch = *q;
|
|
};
|
|
while (((ch>127)||isalnum(ch)||ispunct(ch))&&
|
|
(ch != '_')&&(ch != '-')&&(ch != '*')&& (ch != '|')) {
|
|
if (ch == '~') {
|
|
ch = ' ';
|
|
};
|
|
if ((ch == '\\') && (*(q+1)=='-')) {
|
|
ch = '-';
|
|
q++;
|
|
};
|
|
/* syllable[i] = ch; */
|
|
addch(ch, &syll);
|
|
q++;
|
|
ch = *q;
|
|
};
|
|
skipspace(&q);
|
|
if (ch == '-') {
|
|
addch(ch, &syll);
|
|
while (isspace(ch)||(ch=='-')) {
|
|
q++;
|
|
ch = *q;
|
|
};
|
|
};
|
|
if (syll.len > 0) {
|
|
wordplace = apply_syll(syll.st, wordplace, &errors);
|
|
} else {
|
|
if (ch=='_') {
|
|
clearvstring(&syll);
|
|
addch('_', &syll);
|
|
wordplace = apply_syll(syll.st, wordplace, &errors);
|
|
q++;
|
|
ch = *q;
|
|
};
|
|
if (ch=='*') {
|
|
clearvstring(&syll);
|
|
addch(' ', &syll);
|
|
wordplace = apply_syll(syll.st, wordplace, &errors);
|
|
q++;
|
|
ch = *q;
|
|
};
|
|
};
|
|
};
|
|
if (continuation) {
|
|
cv->more_lyrics = 1;
|
|
cv->lyric_errors = errors;
|
|
cv->linestart = wordplace;
|
|
} else {
|
|
cv->more_lyrics = 0;
|
|
if (errors > 0) {
|
|
event_error("Lyric line too long for music");
|
|
} else {
|
|
clearvstring(&syll);
|
|
wordplace = apply_syll(syll.st, wordplace, &errors);
|
|
if (errors == 0) {
|
|
event_error("Lyric line too short for music");
|
|
};
|
|
};
|
|
};
|
|
freevstring(&syll);
|
|
}
|
|
|
|
/* [SS] 2014-08-16 */
|
|
void appendfield (morewords)
|
|
char *morewords;
|
|
{
|
|
printf("appendfield not implemented here\n");
|
|
}
|
|
|
|
void event_part(s)
|
|
char* s;
|
|
/* A part field (P: ) has been encountered in the abc */
|
|
{
|
|
char label[200]; /* [SS] 2010-12-12 */
|
|
|
|
if (xinhead) {
|
|
if (thetune.parts != NULL) {
|
|
event_error("Multiple P: fields in header");
|
|
} else {
|
|
thetune.parts = addstring(s);
|
|
};
|
|
};
|
|
if (xinbody) {
|
|
/* addfeature(PART, addstring(s)); */
|
|
/* PART is handled like an instruction */
|
|
if (cv->instructions_pending == NULL) {
|
|
cv->instructions_pending = newlist();
|
|
};
|
|
sprintf(label, ":p%s", s);
|
|
addtolist(cv->instructions_pending, addstring(label));
|
|
};
|
|
}
|
|
|
|
void event_voice(n, s, vp)
|
|
int n;
|
|
char *s;
|
|
struct voice_params *vp;
|
|
/* A voice field (V: ) has been encountered */
|
|
{
|
|
if (xinbody) {
|
|
setvoice(n);
|
|
if (vp->gotclef) {
|
|
event_clef (vp->clefname, &vp->new_clef);
|
|
}
|
|
if (vp->gotoctave) {
|
|
event_octave (vp->octave, 0);
|
|
}
|
|
} else {
|
|
if (!suppress) {
|
|
event_error("V: field outside tune body");
|
|
};
|
|
};
|
|
}
|
|
|
|
void event_length(n)
|
|
int n;
|
|
/* A length field (L: ) has been encountered */
|
|
{
|
|
if (xinhead) {
|
|
setfract(&thetune.unitlen, 1, n);
|
|
} else {
|
|
if (xinbody) {
|
|
setfract(&cv->unitlen, 1, n);
|
|
} else {
|
|
if (!suppress) {
|
|
event_warning("L: field outside tune ignored");
|
|
};
|
|
};
|
|
};
|
|
}
|
|
|
|
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 */
|
|
/* of a new tune */
|
|
{
|
|
if (xinhead) {
|
|
event_error("incomplete tune");
|
|
};
|
|
if (xinbody) {
|
|
check_tune_end(&thetune);
|
|
printtune(&thetune);
|
|
};
|
|
freetune(&thetune);
|
|
xinbody = 0;
|
|
xinhead = 0;
|
|
suppress = 0;
|
|
parseroff();
|
|
if (debugging) {
|
|
printf("X:%d\n", n);
|
|
};
|
|
if (checkmatch(n)) {
|
|
parseron();
|
|
/* fileopen = make_open(); */
|
|
xinhead = 1;
|
|
xinbody = 0;
|
|
init_tune(&thetune, n);
|
|
} else {
|
|
suppress = 1;
|
|
};
|
|
}
|
|
|
|
void event_tempo(n, a, b, relative, pre, post)
|
|
int n, a, b;
|
|
int relative;
|
|
char *pre; /* text before tempo */
|
|
char *post; /* text after tempo */
|
|
/* A tempo field Q: has been encountered in the abc */
|
|
/* Q:a/b=N will have relative = 0 */
|
|
/* Q:N will have a=0 and b=0 */
|
|
/* Q:Ca/b = N will have relative = 1 */
|
|
{
|
|
if (xinhead) {
|
|
thetune.tempo = newtempo(n, a, b, relative, pre, post);
|
|
} else {
|
|
if (xinbody) {
|
|
if ((a == 0) && (b == 0)) {
|
|
addfeature(TEMPO, newtempo(n, cv->unitlen.num, cv->unitlen.denom, 0,
|
|
pre, post));
|
|
} else {
|
|
addfeature(TEMPO, newtempo(n, a, b, relative, pre, post));
|
|
};
|
|
} else {
|
|
if (!suppress) {
|
|
event_warning("Q: field outside tune ignored");
|
|
};
|
|
};
|
|
};
|
|
}
|
|
|
|
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) {
|
|
copy_timesig(&thetune.timesig, timesig);
|
|
thetune.barchecking = checkbars;
|
|
} else {
|
|
if (xinbody) {
|
|
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");
|
|
};
|
|
};
|
|
};
|
|
}
|
|
|
|
void event_clef(char* clefstr, cleftype_t * new_clef)
|
|
/* a clef has been encountered in the abc */
|
|
{
|
|
if (new_clef->basic_clef == basic_clef_undefined) {
|
|
return;
|
|
}
|
|
if (xinbody) {
|
|
copy_clef (cv->clef, new_clef);
|
|
addfeature (CLEF, newclef (new_clef));
|
|
};
|
|
if ((xinhead) && (!xinbody)) {
|
|
copy_clef (&thetune.clef, new_clef);
|
|
};
|
|
}
|
|
|
|
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'] = '_';
|
|
}
|
|
|
|
void altermap(basemap, basemul, modmap, modmul)
|
|
/* apply modifiers to a set of accidentals */
|
|
char basemap[7], modmap[7];
|
|
int basemul[7], modmul[7];
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i<7; i++) {
|
|
if (modmap[i] != ' ') {
|
|
basemap[i] = modmap[i];
|
|
basemul[i] = modmul[i];
|
|
};
|
|
};
|
|
}
|
|
|
|
|
|
void resolve_tempo(struct atempo* t, struct fract* unitlen)
|
|
/* Tempo may be given relative to the unit note length. At the start of */
|
|
/* the tune, we will know what this is and can deduce the tempo value. */
|
|
/* Can also deduce tempo if it has been given as a number only. */
|
|
{
|
|
if (t->relative==1) {
|
|
setfract(&t->basenote, t->basenote.num*unitlen->num,
|
|
t->basenote.denom*unitlen->denom);
|
|
reducef(&t->basenote);
|
|
t->relative = 0;
|
|
};
|
|
if ((t->basenote.num == 0) && (t->basenote.denom == 0)) {
|
|
setfract(&t->basenote, unitlen->num, unitlen->denom);
|
|
reducef(&t->basenote);
|
|
};
|
|
}
|
|
|
|
static void start_body()
|
|
/* We have reached the end of the header section and need to set */
|
|
/* default values for anything not explicitly declared */
|
|
{
|
|
parseron();
|
|
/* 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);
|
|
};
|
|
}
|
|
|
|
void event_true_key(sharps, s, modeindex, modmap, modmul)
|
|
int sharps;
|
|
char *s;
|
|
int modeindex; /* 0 major, 1,2,3 minor, 4 locrian, etc. */
|
|
char modmap[7];
|
|
int modmul[7];
|
|
/* key detected in K: field */
|
|
{
|
|
char basemap[7];
|
|
int basemul[7];
|
|
struct key* akey;
|
|
int minor;
|
|
minor =0;
|
|
if (modeindex >0 && modeindex <4) minor = 1;
|
|
|
|
setmap(sharps, basemap, basemul);
|
|
altermap(basemap, basemul, modmap, modmul);
|
|
if (xinbody) {
|
|
akey = newkey(s, sharps, basemap, basemul);
|
|
addfeature(KEY, akey);
|
|
set_keysig(cv->keysig, akey);
|
|
};
|
|
if (xinhead) {
|
|
if (thetune.keysig == NULL) {
|
|
thetune.keysig = newkey(s, sharps, basemap, basemul);
|
|
} else {
|
|
event_warning("Key specified twice");
|
|
};
|
|
xinbody = 1;
|
|
xinhead = 0;
|
|
setvoice(1);
|
|
start_body();
|
|
};
|
|
}
|
|
|
|
void event_octave(int num, int local)
|
|
/* deals with the special command I:octave=N */
|
|
{
|
|
if (xinhead) {
|
|
thetune.octaveshift = num;
|
|
};
|
|
if (xinbody) {
|
|
cv->octaveshift = num;
|
|
};
|
|
}
|
|
|
|
void event_key(sharps, s, minor, modmap, modmul, modmicrotone, gotkey,
|
|
gotclef, clefstr, new_clef,
|
|
octave, transpose, gotoctave, gottranspose, explict)
|
|
int sharps;
|
|
char *s;
|
|
int minor;
|
|
char modmap[7];
|
|
int modmul[7];
|
|
struct fraction modmicrotone[7]; /* [SS] 2014-01-06 */
|
|
int gotkey, gotclef;
|
|
char* clefstr; /* [JA] 2020-10-19 */
|
|
cleftype_t * new_clef;
|
|
int octave, transpose, gotoctave, gottranspose;
|
|
int explict;
|
|
/* A key field (K: ) has been encountered */
|
|
{
|
|
if (xinhead || xinbody) {
|
|
if (gotclef==1) {
|
|
event_clef(clefstr, new_clef);
|
|
};
|
|
if (gotkey==1) {
|
|
event_true_key(sharps, s, minor, modmap, modmul);
|
|
};
|
|
};
|
|
if (gotclef)
|
|
{
|
|
event_octave(new_clef->octave_offset, 0);
|
|
}
|
|
if (gotoctave) {
|
|
event_octave(octave, 0);
|
|
};
|
|
}
|
|
|
|
static void checkbar(int type)
|
|
/* Make sure that bar lasts for the correct musical time */
|
|
{
|
|
int valid;
|
|
char msg[80];
|
|
int a1, a2;
|
|
|
|
valid = 0;
|
|
a1 = cv->barlen.num*cv->barcount.denom;
|
|
a2 = cv->barcount.num*cv->barlen.denom;
|
|
if (a1 == a2) {
|
|
cv->barcount.num = 0;
|
|
cv->barcount.denom = 1;
|
|
cv->barno = cv->barno + 1;
|
|
} else {
|
|
if (((type==BAR_REP)||(type==REP_BAR)||(type==DOUBLE_REP)) &&
|
|
(a1 > a2)) {
|
|
/* do nothing */
|
|
} else {
|
|
if ((cv->barno > 0) && (cv->barcount.num != 0) &&
|
|
(cv->barchecking != 0)) {
|
|
sprintf(msg, "Bar %d is %d/%d not %d/%d", cv->barno,
|
|
cv->barcount.num, cv->barcount.denom,
|
|
cv->barlen.num, cv->barlen.denom);
|
|
event_warning(msg);
|
|
cv->barcount.num = 0;
|
|
cv->barcount.denom = 1;
|
|
cv->barno = cv->barno + 1;
|
|
} else {
|
|
cv->barcount.num = 0;
|
|
cv->barcount.denom = 1;
|
|
cv->barno = cv->barno + 1;
|
|
};
|
|
};
|
|
};
|
|
}
|
|
|
|
void event_bar(type, playonrep_list)
|
|
int type;
|
|
char* playonrep_list;
|
|
/* A bar has been encountered in the abc */
|
|
{
|
|
if (cv->inchord) {
|
|
event_warning("Bar line not permitted within chord");
|
|
return;
|
|
};
|
|
checkbar(type); /* increment bar number if bar complete */
|
|
/* [SS] 2015-11-15 * changed (void*) to (int *) */
|
|
addnumberfeature(type, cv->barno); /* save bar number */
|
|
if ((playonrep_list != NULL) && (strlen(playonrep_list) > 0)) {
|
|
event_playonrep(playonrep_list);
|
|
};
|
|
}
|
|
|
|
void event_space()
|
|
/* A region of whitespace has been encountered */
|
|
{
|
|
addfeature(NOBEAM, NULL);
|
|
}
|
|
|
|
void event_graceon()
|
|
/* start of grace note(s) */
|
|
{
|
|
if (cv->inchord) {
|
|
event_error("grace notes not allowed within chord");
|
|
return;
|
|
};
|
|
if (cv->ingrace) {
|
|
event_error("nested grace notes not allowed");
|
|
return;
|
|
};
|
|
cv->gracebeamroot = NULL;
|
|
cv->gracebeamend = NULL;
|
|
cv->ingrace = 1;
|
|
addfeature(GRACEON, NULL);
|
|
}
|
|
|
|
void event_graceoff()
|
|
/* end of grace note(s) */
|
|
{
|
|
if (!cv->ingrace) {
|
|
event_error("No grace notes to close");
|
|
return;
|
|
};
|
|
if (cv->inchord) {
|
|
event_error("cannot close grace notes within chord");
|
|
return;
|
|
};
|
|
addfeature(GRACEOFF, NULL);
|
|
closegracebeam(cv);
|
|
cv->ingrace = 0;
|
|
cv->gracebeamroot = NULL;
|
|
cv->gracebeamend = NULL;
|
|
}
|
|
|
|
void event_rep1()
|
|
/* start of first repeat */
|
|
{
|
|
addfeature(REP1, NULL);
|
|
}
|
|
|
|
void event_rep2()
|
|
/* start of second repeat */
|
|
{
|
|
addfeature(REP2, NULL);
|
|
}
|
|
|
|
void event_playonrep(s)
|
|
char* s;
|
|
/* play on repeat(s) X - where X can be a list */
|
|
{
|
|
addfeature(PLAY_ON_REP, addstring(s));
|
|
}
|
|
|
|
void event_broken(type, mult)
|
|
/* handles > >> >>> < << <<< in the abc */
|
|
int type, mult;
|
|
{
|
|
if (cv->inchord) {
|
|
event_error("Broken rhythm not allowed in chord");
|
|
} else {
|
|
if (cv->ingrace) {
|
|
event_error("Broken rhythm not allowed in grace notes");
|
|
} else {
|
|
cv->brokentype = type;
|
|
cv->brokenmult = mult;
|
|
cv->brokenpending = 0;
|
|
};
|
|
};
|
|
}
|
|
|
|
void event_tuple(n, q, r)
|
|
int n, q, r;
|
|
/* Start of a tuple has been encountered (e.g. triplet) */
|
|
/* Meaning is "play next r notes at q/n of notated value" */
|
|
/* where all 3 exist, otherwise r defaults to n and ratio */
|
|
/* is deduced from standard rules if q is missing */
|
|
{
|
|
if (cv->tuplenotes != 0) {
|
|
event_error("tuple within tuple not allowed");
|
|
return;
|
|
};
|
|
if (r != 0) {
|
|
cv->tuplenotes = r;
|
|
} else {
|
|
cv->tuplenotes = n;
|
|
};
|
|
if (q != 0) {
|
|
cv->tuplefactor.num = q;
|
|
cv->tuplefactor.denom = n;
|
|
} else {
|
|
cv->tuplefactor.denom = n;
|
|
if ((n == 2) || (n == 4) || (n == 8)) cv->tuplefactor.num = 3;
|
|
if ((n == 3) || (n == 6)) cv->tuplefactor.num = 2;
|
|
if ((n == 5) || (n == 7) || (n == 9)) {
|
|
if ((cv->barlen.num % 3) == 0) {
|
|
cv->tuplefactor.num = 3;
|
|
} else {
|
|
cv->tuplefactor.num = 2;
|
|
};
|
|
};
|
|
};
|
|
cv->thistuple = newtuple(cv->tuplefactor.denom, cv->tuplefactor.num,
|
|
cv->tuplenotes, n);
|
|
addfeature(TUPLE, cv->thistuple);
|
|
}
|
|
|
|
void event_startinline()
|
|
{
|
|
}
|
|
|
|
void event_closeinline()
|
|
{
|
|
}
|
|
|
|
void event_handle_gchord(s)
|
|
char* s;
|
|
/* Guitar/Accompaniment chord placed in linked list for association */
|
|
/* with next suitable note */
|
|
{
|
|
if (cv->gchords_pending == NULL) {
|
|
cv->gchords_pending = newlist();
|
|
};
|
|
if (*s == '_') {
|
|
if (cv->instructions_pending == NULL) {
|
|
cv->instructions_pending = newlist();
|
|
};
|
|
addtolist(cv->instructions_pending, addstring(s+1));
|
|
} else {
|
|
addtolist(cv->gchords_pending, addstring(s));
|
|
};
|
|
}
|
|
|
|
void event_handle_instruction(s)
|
|
char* s;
|
|
/* An instruction (! !) has been encountered */
|
|
{
|
|
char* inst;
|
|
static char segno[3] = ":s";
|
|
static char coda[3] = ":c";
|
|
struct dynamic *psaction;
|
|
int done;
|
|
|
|
done = 0;
|
|
inst = s;
|
|
if (strcmp(s, "fermata") == 0)
|
|
{
|
|
decorators_passback[4] =1;
|
|
/* don't show !fermata!. Treat it like H in music line */
|
|
return;
|
|
}
|
|
|
|
if (strcmp(s, "trill") == 0)
|
|
{
|
|
decorators_passback[6] =1;
|
|
/* don't show !trill!. Treat it like T in music line */
|
|
return;
|
|
}
|
|
|
|
if (strcmp(s, "segno") == 0) {
|
|
inst = segno;
|
|
done = 1;
|
|
};
|
|
if (strcmp(s, "coda") == 0) {
|
|
inst = coda;
|
|
done = 1;
|
|
};
|
|
|
|
if (done == 1) {
|
|
if (cv->instructions_pending == NULL) {
|
|
cv->instructions_pending = newlist();
|
|
};
|
|
addtolist(cv->instructions_pending, addstring(inst));
|
|
return;
|
|
}
|
|
if (strcmp(s,"red") == 0)
|
|
{
|
|
psaction = (struct dynamic*) checkmalloc(sizeof(struct note));
|
|
psaction->color = 'r';
|
|
addfeature(DYNAMIC,psaction);
|
|
}
|
|
if (strcmp(s,"black") == 0)
|
|
{
|
|
psaction = (struct dynamic*) checkmalloc(sizeof(struct note));
|
|
psaction->color = 'b';
|
|
addfeature(DYNAMIC,psaction);
|
|
}
|
|
}
|
|
|
|
struct slurtie* resolve_slur(struct feature* lastnote)
|
|
/* when an end-of-slur marker ')' is found, this routine works out */
|
|
/* where the other end of the slur was */
|
|
{
|
|
int i;
|
|
int resolved;
|
|
struct slurtie* s;
|
|
|
|
resolved = 0;
|
|
if (lastnote != NULL) {
|
|
for (i=0; i<cv->slurcount; i++) {
|
|
if ((resolved == 0) && (cv->slur_place[i]->begin != NULL) &&
|
|
(cv->slur_place[i]->begin != lastnote)) {
|
|
resolved = 1;
|
|
cv->slur_place[i]->end = lastnote;
|
|
s = cv->slur_place[i];
|
|
cv->slur_place[i] = cv->slur_place[cv->slurcount-1];
|
|
cv->slurcount = cv->slurcount - 1;
|
|
};
|
|
};
|
|
};
|
|
if (resolved == 0) {
|
|
event_error("Could not find start of slur to match close slur");
|
|
s = NULL;
|
|
};
|
|
return(s);
|
|
}
|
|
|
|
static void startslurs(struct feature* firstnote)
|
|
/* When a note is found after a (, set note to be start of slur */
|
|
/* Note may be the start of more than one slur */
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i<cv->slurcount; i++) {
|
|
if (cv->slur_place[i]->begin == NULL) {
|
|
cv->slur_place[i]->begin = firstnote;
|
|
};
|
|
};
|
|
}
|
|
|
|
void event_sluron(t)
|
|
int t;
|
|
/* start of slur */
|
|
{
|
|
struct slurtie* s;
|
|
|
|
s = newslurtie();
|
|
addfeature(SLUR_ON, s);
|
|
cv->slurpending = 1;
|
|
if (cv->slurcount < MAX_SLURS) {
|
|
cv->slur_place[cv->slurcount] = s;
|
|
cv->slurcount = cv->slurcount+1;
|
|
} else {
|
|
event_error("Static limit on number of slurs exceeded");
|
|
};
|
|
}
|
|
|
|
void event_sluroff(t)
|
|
int t;
|
|
/* end of slur */
|
|
{
|
|
struct slurtie* s;
|
|
|
|
s = resolve_slur(cv->lastnote);
|
|
addfeature(SLUR_OFF, s);
|
|
}
|
|
|
|
static void resolve_ties(struct feature* f)
|
|
/* try to match up note to previous tied note */
|
|
{
|
|
struct feature* ft;
|
|
struct slurtie* s;
|
|
struct note* m;
|
|
struct note* n;
|
|
int i, j;
|
|
|
|
n = f->item.voidptr;
|
|
for (i=0; i<cv->tiespending; i++) {
|
|
ft = cv->tie_place[i]; /* pointer to TIE feature */
|
|
s = ft->item.voidptr;
|
|
ft = s->begin; /* pointer to NOTE feature */
|
|
m = ft->item.voidptr;
|
|
if (m->y == n->y) { /* pitch match found */
|
|
s->end = f;
|
|
j = cv->tiespending;
|
|
cv->tie_place[i] = cv->tie_place[j-1];
|
|
cv->tie_status[i] = cv->tie_status[j-1];
|
|
cv->tiespending = j-1;
|
|
};
|
|
};
|
|
}
|
|
|
|
static void advance_ties()
|
|
/* deal with unresolved tied notes as musical time advances */
|
|
{
|
|
int i, j;
|
|
|
|
for (i=0; i<cv->tiespending; i++) {
|
|
cv->tie_status[i]++;
|
|
};
|
|
i = 0;
|
|
j = cv->tiespending;
|
|
while (i < j) {
|
|
if (cv->tie_status[i] >= 2) {
|
|
event_error("No note to tie to");
|
|
cv->tie_place[i] = cv->tie_place[j-1];
|
|
cv->tie_status[i] = cv->tie_status[j-1];
|
|
j = j - 1;
|
|
} else {
|
|
i = i + 1;
|
|
};
|
|
};
|
|
cv->tiespending = j;
|
|
}
|
|
|
|
void event_tie()
|
|
/* tie encountered in the abc */
|
|
{
|
|
struct slurtie* s;
|
|
struct feature* place;
|
|
int i;
|
|
|
|
if ((cv->lastnote == NULL)||(cv->lastnote->type != NOTE)) {
|
|
event_error("No note to tie");
|
|
} else {
|
|
s = newslurtie();
|
|
place = addfeature(TIE, s);
|
|
s->begin = cv->lastnote;
|
|
cv->lastnote = NULL;
|
|
i = cv->tiespending;
|
|
if (i < MAX_TIES) {
|
|
cv->tie_place[i] = place;
|
|
cv->tie_status[i] = 0;
|
|
cv->tiespending = cv->tiespending + 1;
|
|
} else {
|
|
event_error("Internal limit on ties exceeded");
|
|
};
|
|
};
|
|
}
|
|
|
|
void event_lineend(ch, n)
|
|
char ch;
|
|
int n;
|
|
/* Line ending with n copies of special character ch */
|
|
{
|
|
}
|
|
|
|
static void lenmul(n, a, b)
|
|
/* multiply note length by a/b */
|
|
struct feature* n;
|
|
int a, b;
|
|
{
|
|
struct note *anote;
|
|
struct rest* arest;
|
|
struct fract* afract;
|
|
|
|
if (n->type == NOTE) {
|
|
anote = n->item.voidptr;
|
|
afract = &anote->len;
|
|
afract->num = afract->num * a;
|
|
afract->denom = afract->denom * b;
|
|
reducef(afract);
|
|
/* re-calculate base, base_exp and dots */
|
|
anote->dots = count_dots(&anote->base, &anote->base_exp,
|
|
anote->len.num, anote->len.denom);
|
|
};
|
|
if (n->type == REST) {
|
|
arest = n->item.voidptr;
|
|
afract = &arest->len;
|
|
afract->num = afract->num * a;
|
|
afract->denom = afract->denom * b;
|
|
reducef(afract);
|
|
/* re-calculate base, base_exp and dots */
|
|
arest->dots = count_dots(&arest->base, &arest->base_exp,
|
|
arest->len.num, arest->len.denom);
|
|
};
|
|
}
|
|
|
|
struct fract* getlenfract(struct feature *f)
|
|
/* find fractional length of NOTE or REST */
|
|
{
|
|
struct fract *len;
|
|
struct note *anote;
|
|
struct rest *arest;
|
|
|
|
len = NULL;
|
|
if (f->type == NOTE) {
|
|
anote = f->item.voidptr;
|
|
len = &(anote->len);
|
|
};
|
|
if (f->type == REST) {
|
|
arest = f->item.voidptr;
|
|
len = &(arest->len);
|
|
};
|
|
return(len);
|
|
}
|
|
|
|
static void brokenadjust()
|
|
/* adjust lengths of broken notes */
|
|
{
|
|
int num1, num2, denom12;
|
|
struct feature* j;
|
|
struct fract* fr1;
|
|
struct fract* fr2;
|
|
int temp;
|
|
int failed;
|
|
int done;
|
|
|
|
switch(cv->brokenmult) {
|
|
case 1:
|
|
num1 = 3;
|
|
num2 = 1;
|
|
break;
|
|
case 2:
|
|
num1 = 7;
|
|
num2 = 1;
|
|
break;
|
|
case 3:
|
|
num1 = 15;
|
|
num2 = 1;
|
|
break;
|
|
default:
|
|
num1 = num2 = 1; /* [SDG] 2020-06-03 */
|
|
};
|
|
denom12 = (num1 + num2)/2;
|
|
if (cv->brokentype == LT) {
|
|
temp = num1;
|
|
num1 = num2;
|
|
num2 = temp;
|
|
};
|
|
failed = 0;
|
|
if ((cv->laststart == NULL) || (cv->lastend == NULL) ||
|
|
(cv->thisstart == NULL) || (cv->thisend == NULL)) {
|
|
failed = 1;
|
|
} else {
|
|
/* check for same length notes */
|
|
fr1 = getlenfract(cv->laststart);
|
|
fr2 = getlenfract(cv->thisstart);
|
|
/*
|
|
fr1 = cv->laststart->item.voidptr;
|
|
fr2 = cv->thisstart->item.voidptr;
|
|
*/
|
|
if ((fr1->num * fr2->denom) != (fr2->num * fr1->denom)) {
|
|
failed = 1;
|
|
};
|
|
};
|
|
if (failed) {
|
|
event_error("Cannot apply broken rhythm");
|
|
} else {
|
|
/*
|
|
printf("Adjusting %d to %d and %d to %d\n",
|
|
cv->laststart, cv->lastend, cv->thisstart, cv->thisend);
|
|
*/
|
|
j = cv->laststart;
|
|
done = 0;
|
|
while (done == 0) {
|
|
lenmul(j, num1, denom12);
|
|
done = (j == cv->lastend);
|
|
j = j->next;
|
|
};
|
|
j = cv->thisstart;
|
|
done = 0;
|
|
while (done == 0) {
|
|
lenmul(j, num2, denom12);
|
|
done = (j == cv->thisend);
|
|
j = j->next;
|
|
};
|
|
};
|
|
}
|
|
|
|
static void marknotestart(struct feature* place)
|
|
/* 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 */
|
|
{
|
|
cv->laststart = cv->thisstart;
|
|
cv->lastend = cv->thisend;
|
|
cv->thisstart = place;
|
|
}
|
|
|
|
static void marknoteend(struct feature* place)
|
|
/* 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 */
|
|
{
|
|
cv->thisend = place;
|
|
if (cv->brokenpending != -1) {
|
|
cv->brokenpending = cv->brokenpending + 1;
|
|
if (cv->brokenpending == 1) {
|
|
brokenadjust();
|
|
cv->brokenpending = -1;
|
|
};
|
|
};
|
|
}
|
|
|
|
static void marknote(struct feature* place)
|
|
/* when handling a single note, not a chord, marknotestart() and */
|
|
/* marknoteend() can be called together */
|
|
{
|
|
marknotestart(place);
|
|
marknoteend(place);
|
|
}
|
|
|
|
static void markchord(struct feature* chordplace)
|
|
/* find start and end of chord for applying broken rhythm */
|
|
{
|
|
struct feature* first;
|
|
struct feature* last;
|
|
|
|
first = chordplace->next;
|
|
last = first;
|
|
while ((last->next != NULL)&&(last->next->type==NOTE)) {
|
|
last = last->next;
|
|
};
|
|
marknotestart(first);
|
|
marknoteend(last);
|
|
}
|
|
|
|
void event_chord()
|
|
/* handles old '+' notation which marks the start and end of each chord */
|
|
{
|
|
if (cv->inchord) {
|
|
event_chordoff(1,1);
|
|
} else {
|
|
event_chordon(dummydecorator);
|
|
};
|
|
}
|
|
|
|
void event_chordon(int chorddecorators[])
|
|
/* start of a chord */
|
|
/* the array chorddecorators is not used yet. */
|
|
{
|
|
if (cv->inchord) {
|
|
event_error("nested chords found");
|
|
return;
|
|
};
|
|
cv->inchord = 1;
|
|
cv->chordcount = 0;
|
|
cv->thischord = newchord();
|
|
cv->chordplace = addfeature(CHORDON, cv->thischord);
|
|
}
|
|
|
|
void event_ignore () { }; /* [SS] 2018-12-21 */
|
|
|
|
|
|
|
|
void fix_enclosed_note_lengths(struct feature* chordplace,
|
|
int chord_n, int chord_m, int * base, int * base_exp)
|
|
{
|
|
struct feature* f;
|
|
struct note* anote;
|
|
if (chord_n ==1 && chord_m ==1) return;
|
|
f = chordplace->next;
|
|
anote = f->item.voidptr;
|
|
/* remove old note length from barcount */
|
|
addfractions(&cv->barcount, -anote->len.num, anote->len.denom);
|
|
while ((f != NULL)&&((f->type==NOTE)||(f->type=CHORDNOTE))) {
|
|
anote = f->item.voidptr;
|
|
/* remove old note length from barcount */
|
|
setfract(&anote->len, chord_n*cv->unitlen.num, chord_m*cv->unitlen.denom);
|
|
reducef(&anote->len);
|
|
/*printf("NOTE %c%c %d / %d\n", anote->accidental, anote->pitch,
|
|
anote->len.num, anote->len.denom);
|
|
*/
|
|
anote->dots = count_dots(&anote->base, &anote->base_exp,
|
|
anote->len.num, anote->len.denom);
|
|
f = f->next;
|
|
*base = anote->base;
|
|
*base_exp = anote->base_exp;
|
|
}
|
|
/* and new note length to barcount */
|
|
addfractions(&cv->barcount, anote->len.num, anote->len.denom);
|
|
}
|
|
|
|
|
|
void event_chordoff(int chord_n, int chord_m)
|
|
/* end of a chord */
|
|
{
|
|
struct feature* ft;
|
|
struct chord* thechord;
|
|
struct note* firstnote;
|
|
int base,base_exp;
|
|
|
|
if (!cv->inchord) {
|
|
event_error("no chord to close");
|
|
return;
|
|
};
|
|
ft = cv->chordplace;
|
|
if ((ft != NULL) && (ft->next != NULL) && (ft->next->type == NOTE)) {
|
|
thechord = ft->item.voidptr;
|
|
firstnote = ft->next->item.voidptr;
|
|
/* beaming for 1st note in chord */
|
|
beamitem(NOTE, firstnote, ft->next);
|
|
markchord(ft);
|
|
thechord->base = firstnote->base;
|
|
thechord->base_exp = firstnote->base_exp;
|
|
} else {
|
|
thechord = NULL; /* [SDG] 2020-06-04 */
|
|
event_error("mis-formed chord");
|
|
};
|
|
|
|
if (cv->chordplace) {
|
|
fix_enclosed_note_lengths(cv->chordplace,chord_n,chord_m,&base,&base_exp);
|
|
if (chord_n != 1 || chord_m != 1) {
|
|
thechord->base = base;
|
|
thechord->base_exp = base_exp;
|
|
}
|
|
}
|
|
cv->inchord = 0;
|
|
cv->thischord = NULL;
|
|
cv->chordplace = NULL;
|
|
advance_ties();
|
|
addfeature(CHORDOFF, NULL);
|
|
}
|
|
|
|
/* just a stub to ignore 'y' */
|
|
void event_spacing(n, m)
|
|
int n,m;
|
|
{
|
|
}
|
|
|
|
void xevent_rest(n, m, multi)
|
|
int n, m, multi;
|
|
/* A rest has been encountered in the abc */
|
|
/* multi is 0 for ordinary rests or count for multibar rests */
|
|
{
|
|
struct feature* restplace;
|
|
|
|
if (cv->ingrace) {
|
|
event_warning("rest in grace notes ignored");
|
|
return;
|
|
};
|
|
if (cv->inchord) {
|
|
event_warning("rest in chord ignored");
|
|
return;
|
|
};
|
|
if ((multi > 0) && (cv->tuplenotes > 0)) {
|
|
event_error("Multiple bar rest not allowed in tuple");
|
|
};
|
|
advance_ties();
|
|
restplace = addfeature(REST, newrest(n*cv->unitlen.num,
|
|
m*cv->unitlen.denom, multi));
|
|
if (cv->slurpending) {
|
|
startslurs(restplace);
|
|
cv->slurpending = 0;
|
|
};
|
|
cv->lastnote = restplace;
|
|
if (cv->inchord) {
|
|
cv->chordcount = cv->chordcount + 1;
|
|
} else {
|
|
marknote(restplace);
|
|
};
|
|
if ((!cv->ingrace) && (!cv->inchord || (cv->chordcount == 1))) {
|
|
if (cv->tuplenotes == 0) {
|
|
if (multi == 0) {
|
|
addunits(&cv->barcount, n, m);
|
|
};
|
|
} else {
|
|
addunits(&cv->barcount, n*cv->tuplefactor.num, m*cv->tuplefactor.denom);
|
|
cv->thistuple->beamed = 0;
|
|
cv->tuplenotes = cv->tuplenotes - 1;
|
|
if (cv->tuplenotes == 0) {
|
|
cv->thistuple = NULL;
|
|
};
|
|
};
|
|
};
|
|
}
|
|
|
|
void event_rest(decorators,n,m,type)
|
|
int n, m,type;
|
|
int decorators[DECSIZE];
|
|
/* A rest has been encountered in the abc */
|
|
{
|
|
xevent_rest(n, m, 0);
|
|
}
|
|
|
|
void event_mrest(n,m,c)
|
|
int n, m;
|
|
char c; /* [SS] 2017-04-19 to distinguish X from Z in abc2abc */
|
|
/* A multiple bar rest has been encountered in the abc */
|
|
{
|
|
xevent_rest(1, 1, n);
|
|
}
|
|
|
|
void event_note(decorators, clef, xaccidental, xmult, xnote, xoctave, n, m)
|
|
int decorators[DECSIZE];
|
|
cleftype_t *clef; /* [JA] 2020-10-19 */
|
|
int xmult;
|
|
char xaccidental, xnote;
|
|
int xoctave, n, m;
|
|
/* note found in abc */
|
|
{
|
|
struct note* nt;
|
|
struct feature* noteplace;
|
|
struct chord* thechord;
|
|
int pitchval;
|
|
|
|
nt = newnote(decorators, clef, xaccidental, xmult, xnote, xoctave,
|
|
n * cv->unitlen.num, m * cv->unitlen.denom);
|
|
nt->tuplenotes = cv->tuplenotes;
|
|
noteplace = addfeature(NOTE, nt);
|
|
cv->lastnote = noteplace;
|
|
resolve_ties(noteplace);
|
|
if (cv->slurpending) {
|
|
startslurs(noteplace);
|
|
cv->slurpending = 0;
|
|
};
|
|
if (!cv->inchord) {
|
|
marknote(noteplace);
|
|
advance_ties();
|
|
} else {
|
|
thechord = cv->thischord;
|
|
pitchval = notenum(xoctave, xnote, clef);
|
|
if (cv->chordcount == 0) {
|
|
thechord->ytop = pitchval;
|
|
thechord->ybot = pitchval;
|
|
} else {
|
|
if (pitchval > thechord->ytop) {
|
|
thechord->ytop = pitchval;
|
|
};
|
|
if (pitchval < thechord->ybot) {
|
|
thechord->ybot = pitchval;
|
|
};
|
|
};
|
|
cv->chordcount = cv->chordcount + 1;
|
|
};
|
|
if ((!cv->ingrace) &&
|
|
(!cv->inchord || (cv->chordcount == 1))) {
|
|
if (cv->tuplenotes == 0) {
|
|
addunits(&cv->barcount, n, m);
|
|
} else {
|
|
addunits(&cv->barcount, n*cv->tuplefactor.num, m*cv->tuplefactor.denom);
|
|
if (nt->base_exp > -3) {
|
|
cv->thistuple->beamed = 0;
|
|
};
|
|
cv->tuplenotes = cv->tuplenotes - 1;
|
|
if (cv->tuplenotes == 0) {
|
|
cv->thistuple = NULL;
|
|
/* prevent beaming from tuple to non-tuple notes */
|
|
addfeature(NOBEAM, NULL);
|
|
};
|
|
};
|
|
};
|
|
}
|
|
|
|
/* these functions are here to satisfy the linker */
|
|
void event_microtone(int dir, int a, int b)
|
|
{
|
|
}
|
|
|
|
void event_temperament(char *line) {
|
|
}
|
|
|
|
|
|
void event_normal_tone()
|
|
{
|
|
}
|
|
|
|
|
|
|
|
void event_info_key(key, value)
|
|
char* key;
|
|
char* value;
|
|
/* handles a (key,value) pair found in an I: field */
|
|
{
|
|
int num;
|
|
|
|
if (strcmp(key, "clef")==0) {
|
|
cleftype_t new_clef; /* [JA] 2020-10-19 */
|
|
int valid_clef;
|
|
|
|
init_new_clef (&new_clef);
|
|
valid_clef = get_standard_clef (value, &new_clef);
|
|
if (!valid_clef) {
|
|
valid_clef = get_extended_clef_details (value, &new_clef);
|
|
}
|
|
if (valid_clef) {
|
|
event_clef (value, &new_clef);
|
|
}
|
|
};
|
|
if (strcmp(key, "octave")==0) {
|
|
num = readsnumf(value);
|
|
event_octave(num,0);
|
|
};
|
|
}
|
|
|
|
|
|
int main(argc,argv)
|
|
int argc;
|
|
char *argv[];
|
|
{
|
|
char *filename;
|
|
int i;
|
|
|
|
oldchordconvention = 0;
|
|
for (i=0;i<DECSIZE;i++) decorators_passback[i]=0;
|
|
|
|
event_init(argc, argv, &filename);
|
|
if (argc < 2) {
|
|
/* printf("argc = %d\n", argc); */
|
|
} else {
|
|
init_abbreviations();
|
|
parsefile(filename);
|
|
free_abbreviations();
|
|
};
|
|
return(0);
|
|
}
|
|
|