diff -Nru abcmidi-20200706/abc.h abcmidi-20200728/abc.h --- abcmidi-20200706/abc.h 2020-05-01 23:30:50.000000000 +0000 +++ abcmidi-20200728/abc.h 2020-07-27 15:52:44.000000000 +0000 @@ -1,146 +1,146 @@ -/* abc.h - header file shared by abc2midi, abc2abc and yaps */ -/* Copyright James Allwright 2000 */ -/* may be copied under the terms of the GNU public license */ - -typedef enum { -ABC2MIDI, -ABC2ABC, -YAPS, -ABCMATCH} programname; - -/* define types of abc object */ -typedef enum { -/* types of bar sign */ -SINGLE_BAR, -DOUBLE_BAR, -DOTTED_BAR, -BAR_REP, -REP_BAR, -PLAY_ON_REP, -REP1, -REP2, -/* BAR1 = SINGLE_BAR + REP1 */ -/* REP_BAR2 = REP_BAR + REP2 */ -BAR1, -REP_BAR2, -DOUBLE_REP, -THICK_THIN, -THIN_THICK, -/* other things */ -PART, -TEMPO, -TIME, -KEY, -REST, -TUPLE, -/* CHORD replaced by CHORDON and CHORDOFF */ -NOTE, -NONOTE, -OLDTIE, -TEXT, -SLUR_ON, -SLUR_OFF, -TIE, -CLOSE_TIE, -TITLE, -CHANNEL, -TRANSPOSE, -RTRANSPOSE, -GTRANSPOSE, -GRACEON, -GRACEOFF, -SETGRACE, -SETC, -SETTRIM, -EXPAND, -GCHORD, -GCHORDON, -GCHORDOFF, -VOICE, -CHORDON, -CHORDOFF, -CHORDOFFEX, -DRUMON, -DRUMOFF, -DRONEON, -DRONEOFF, -SLUR_TIE, -TNOTE, -/* broken rhythm */ -LT, -GT, -DYNAMIC, -LINENUM, -MUSICLINE, -MUSICSTOP, -WORDLINE, -WORDSTOP, -INSTRUCTION, -NOBEAM, -CHORDNOTE, -CLEF, -PRINTLINE, -NEWPAGE, -LEFT_TEXT, -CENTRE_TEXT, -VSKIP, -COPYRIGHT, -COMPOSER, -ARPEGGIO, -SPLITVOICE, -META, -PEDAL_ON, -PEDAL_OFF, -EFFECT -} featuretype; - -/* note decorations */ -#define DECSIZE 13 -extern char decorations[]; -#define STACCATO 0 -#define TENUTO 1 -#define LOUD 2 -#define ROLL 3 -#define FERMATA 4 -#define ORNAMENT 5 -#define TRILL 6 -#define BOWUP 7 -#define BOWDOWN 8 -#define BREATH 9 -#define CODA 10 -#define UPPERMORDENT 11 -#define SEGNO 12 - -/* The vstring routines provide a simple way to handle */ -/* arbitrary length strings */ -struct vstring { -int len; -int limit; -char* st; -}; -#ifndef KANDR -/* vstring routines */ -extern void initvstring(struct vstring* s); -extern void extendvstring(struct vstring* s); -extern void addch(char ch, struct vstring* s); -extern void addtext(char* text, struct vstring* s); -extern void clearvstring(struct vstring* s); -extern void freevstring(struct vstring* s); -/* error-handling routines */ -extern void event_error(char *s); -extern void event_fatal_error(char *s); -extern void event_warning(char *s); -#else -/* vstring routines */ -extern void initvstring(); -extern void extendvstring(); -extern void addch(); -extern void addtext(); -extern void clearvstring(); -extern void freevstring(); -/* error-handling routines */ -extern void event_error(); -extern void event_fatal_error(); -extern void event_warning(); -#endif - +/* abc.h - header file shared by abc2midi, abc2abc and yaps */ +/* Copyright James Allwright 2000 */ +/* may be copied under the terms of the GNU public license */ + +typedef enum { +ABC2MIDI, +ABC2ABC, +YAPS, +ABCMATCH} programname; + +/* define types of abc object */ +typedef enum { +/* types of bar sign */ +SINGLE_BAR, +DOUBLE_BAR, +DOTTED_BAR, +BAR_REP, +REP_BAR, +PLAY_ON_REP, +REP1, +REP2, +/* BAR1 = SINGLE_BAR + REP1 */ +/* REP_BAR2 = REP_BAR + REP2 */ +BAR1, +REP_BAR2, +DOUBLE_REP, +THICK_THIN, +THIN_THICK, +/* other things */ +PART, +TEMPO, +TIME, +KEY, +REST, +TUPLE, +/* CHORD replaced by CHORDON and CHORDOFF */ +NOTE, +NONOTE, +OLDTIE, +TEXT, +SLUR_ON, +SLUR_OFF, +TIE, +CLOSE_TIE, +TITLE, +CHANNEL, +TRANSPOSE, +RTRANSPOSE, +GTRANSPOSE, +GRACEON, +GRACEOFF, +SETGRACE, +SETC, +SETTRIM, +EXPAND, +GCHORD, +GCHORDON, +GCHORDOFF, +VOICE, +CHORDON, +CHORDOFF, +CHORDOFFEX, +DRUMON, +DRUMOFF, +DRONEON, +DRONEOFF, +SLUR_TIE, +TNOTE, +/* broken rhythm */ +LT, +GT, +DYNAMIC, +LINENUM, +MUSICLINE, +MUSICSTOP, +WORDLINE, +WORDSTOP, +INSTRUCTION, +NOBEAM, +CHORDNOTE, +CLEF, +PRINTLINE, +NEWPAGE, +LEFT_TEXT, +CENTRE_TEXT, +VSKIP, +COPYRIGHT, +COMPOSER, +ARPEGGIO, +SPLITVOICE, +META, +PEDAL_ON, +PEDAL_OFF, +EFFECT +} featuretype; + +/* note decorations */ +#define DECSIZE 13 +extern char decorations[]; +#define STACCATO 0 +#define TENUTO 1 +#define LOUD 2 +#define ROLL 3 +#define FERMATA 4 +#define ORNAMENT 5 +#define TRILL 6 +#define BOWUP 7 +#define BOWDOWN 8 +#define BREATH 9 +#define CODA 10 +#define UPPERMORDENT 11 +#define SEGNO 12 + +/* The vstring routines provide a simple way to handle */ +/* arbitrary length strings */ +struct vstring { +int len; +int limit; +char* st; +}; +#ifndef KANDR +/* vstring routines */ +extern void initvstring(struct vstring* s); +extern void extendvstring(struct vstring* s); +extern void addch(char ch, struct vstring* s); +extern void addtext(char* text, struct vstring* s); +extern void clearvstring(struct vstring* s); +extern void freevstring(struct vstring* s); +/* error-handling routines */ +extern void event_error(char *s); +extern void event_fatal_error(char *s); +extern void event_warning(char *s); +#else +/* vstring routines */ +extern void initvstring(); +extern void extendvstring(); +extern void addch(); +extern void addtext(); +extern void clearvstring(); +extern void freevstring(); +/* error-handling routines */ +extern void event_error(); +extern void event_fatal_error(); +extern void event_warning(); +#endif + diff -Nru abcmidi-20200706/abcmatch.c abcmidi-20200728/abcmatch.c --- abcmidi-20200706/abcmatch.c 2020-06-06 23:19:50.000000000 +0000 +++ abcmidi-20200728/abcmatch.c 2020-07-27 15:52:43.000000000 +0000 @@ -1,1561 +1,1561 @@ -/* abcmatch.c -This contains the main program and core functions of the program to -compare abc files. - -The abc file may either be a single tune or a compilation -of tunes. The program searches for specific bars in the -file and returns its positions in the file. There are -various matching criteria ranging from exact to approximate -match. The program assumes there is a file called match.abc -containing a description of the bars to be matched. Though -you can run abcmatch as a stand alone program, it was -really designed to run with a graphical user interface such -as runabc.tcl (version 1.59 or higher). - - -Limitations: -tied notes longer than 8 quarter notes are ignored. - -Road map to the code: - -Matching: - The templates derived from match.abc are stored in the - global arrays tpmidipitch, tpnotelength, tpnotes, tpbars, and - tpbarlineptr. - - If temporal quantization is selected, (i.e. qnt >0) then - the template is represented by mpitch_samples[] and msamples[]. - The temporal quantization representation is created by the - function make_bar_image (). All the bars in the template - file match.abc are converted and stored in mpitch_samples - and msamples[] when match.abc is processed. However, the bars - in the tunes compared are converted only as needed. - - match_notes() is called if there is no temporal quantization - (exact matching). - match_samples() is called if there is temporal quantization. - - difference_midipitch() is called if we are doing contour - matching on the temporal quantized images. For exact matching - (i.e. qnt == 0), when contouring is specified (con ==1) the - differencing (and possibly quantization of the contour) is - performed in the function match_notes. - - count_matched_tune_bars () is called if only want an overview - (brief ==1). - find_and_report_matching_bars() is called if we want the details - (brief !=1); -*/ - - - -#define VERSION "1.74 June 04 2020 abcmatch" -#include -#include -#include -#include "abc.h" -#include "parseabc.h" - -#define MAX(A, B) ((A) > (B) ? (A) : (B)) -#define MIN(A, B) ((A) < (B) ? (A) : (B)) - -#define BAR -1000 -#define RESTNOTE -2000 -#define ANY -3000 - -enum ACTION -{ - none, - cpitch_histogram, - cwpitch_histogram, - clength_histogram, - cpitch_histogram_table, - interval_pdf, - interval_pdf_table -} action; - - -int *checkmalloc (int); -extern int getarg (char *, int, char **); -void free_feature_representation (); - -/* variables shared with abcparse.c and abcstore.c */ -/* many of these variables are used in event_int which has - been moved to this file. -*/ -extern int *pitch, *num, *denom, *pitchline; -extern char **atext, **words; -extern featuretype *feature; -extern int notes; -extern int note_unit_length; -extern int time_num, time_denom; -extern int sf, mi; -extern FILE *fp; -extern int xrefno; -extern int check, nowarn, noerror, verbose, maxnotes, xmatch, dotune; -extern int maxtexts, maxwords; -extern int voicesused; - -/* midipitch: pitch in midi units of each note in the abc file. A pitch - of zero is reserved for rest notes. Bar lines are signaled with BAR. - If contour matching is used, then all the pitch differences are offsetted - by 256 to avoid the likelihood of a zero difference mistaken for a - rest. - notelength: represents the length of a note. A quarter note is 24 units - (assuming L:1/4 in abc file) and all other note sizes are proportional. - Thus a half note is 48 units, a sixteenth note is 6 units. - nnotes: the number of notes in the midipitch array. - nbars: the number of bar lines in the midipitch array. - barlineptr: indicates the index in midipitch, notelength of the start - of bar lines. -*/ - -/* data structure for input to matcher. */ -int imidipitch[2000]; /* pitch-barline midi note representation of input tune */ -int inotelength[2000]; /* notelength representation of input tune */ -int innotes; /*number of notes in imidipitch,inotelength representation */ -int inbars; /*number of bars in input tune */ -int ibarlineptr[500]; /*pointers to bar lines in imidipitch */ -int itimesig_num, itimesig_denom; -int imaxnotes = 2000; /* maximum limits of this program */ -int imaxbars = 200; -int resolution = 12; /* default to 1/8 note resolution */ -int anymode = 0; /* default to matching all bars */ -int ignore_simple = 0; /* ignore simple bars */ -int con = 0; /* default for no contour matching */ -int qnt = 0; /* pitch difference quantization flag */ -int brief = 0; /* set brief to 1 if brief mode */ -int cthresh = 3; /* minimum number of common bars to report */ -int fixednumberofnotes=0; /* for -fixed parameter */ -int fileindex = -1; -int wphist, phist, lhist; /* flags for computing pitch or length histogram */ -int pdftab; /* flag for computing pitch pdf for each tune */ -int qntflag = 0; /* flag for turning on contour quantization */ -int wpintv = 0; /* flag for computing interval pdf */ -int wpipdf = 0; /* flag for computing interval pdf for each tune*/ -int norhythm = 0; /* ignore note lengths */ -int levdist = 0; /* levenshtein distance */ - -char *templatefile; - -/* data structure for matcher (template) (usually match.abc)*/ -int tpmidipitch[1000]; /*pitch-barline representation for template */ -int tpnotelength[1000]; /*note lengths for template */ -int tpnotes; /* number of notes in template */ -int tpbars; /* number of bar lines in template */ -int tpbarlineptr[300]; /* I don't expect 300 bar lines, but lets play safe */ -int tptimesig_num, tptimesig_denom; -int tpmaxnotes = 1000; /* maximum limits of this program */ -int tpmaxbars = 300; -unsigned char tpbarstatus[300]; - -int pitch_histogram[128]; -int weighted_pitch_histogram[128]; -int length_histogram[144]; -int interval_histogram[128]; -int lastpitch; - -/* compute the midi offset for the key signature. Since - we do not have negative indices in arrays, sf2midishift[7] - corresponds to no flats/no sharps, ie. C major scale. - If sf is the number of sharps (when positive) and the number of - flats when negative, then sf2midishift[7+i] is the offset in - semitones from C. Thus G is 7, D is 2, Bb is 10 etc. -*/ -int sf2midishift[] = { 11, 6, 1, 8, 3, 10, 5, 0, 7, 2, 9, 4, 11, 6, 1, }; - - -/* when using a resolution greater than 0, time resolution - * is reduced by a certain factor. The resolution is reduced - * in the arrays ipitch_samples and mpitch_samples. We need - * more room in mpitch_samples for the template, since we - * use this template over and over. Therefore we compute it - * and store it for the entire template. On the other hand, the - * input tune is always changing, so we only store one bar at - * a time for the input tune. - */ - -int ipitch_samples[400], isamples; -int mpitch_samples[4000], msamples[160]; /* maximum number of bars 160 */ - -char titlename[48]; -char keysignature[16]; - -int tpxref = 0; /* template reference number */ -int tp_fileindex = 0; /* file sequence number for tpxref */ - - -void -make_note_representation (int *nnotes, int *nbars, int maxnotes, int maxbars, - int *timesig_num, int *timesig_denom, - int *barlineptr, int *notelength, int *midipitch) -/* converts between the feature,pitch,num,denom representation to the - midipitch,notelength,... representation. This simplification does - not preserve chords, decorations, grace notes etc. -*/ -{ - float fract; - int i; - int skip_rests, multiplier, inchord, ingrace; - int maxpitch=0; /* [SDG] 2020-06-03 */ - *nnotes = 0; - *nbars = 0; - inchord = 0; - ingrace = 0; - skip_rests = 0; - *timesig_num = time_num; - *timesig_denom = time_denom; - multiplier = (int) 24.0; - barlineptr[*nbars] = 0; - for (i = 0; i < notes; i++) - { - switch (feature[i]) - { - case NOTE: - if (inchord) - maxpitch = MAX (maxpitch, pitch[i]); - else if (ingrace) - break; - else - { - midipitch[*nnotes] = pitch[i]; - fract = (float) num[i] / (float) denom[i]; - notelength[*nnotes] = (int) (fract * multiplier + 0.01); - (*nnotes)++; - } - break; - case TNOTE: - midipitch[*nnotes] = pitch[i]; - fract = (float) num[i] / (float) denom[i]; - notelength[*nnotes] = (int) (fract * multiplier + 0.01); - (*nnotes)++; - skip_rests = 2; - break; - case REST: - if (skip_rests > 0) - { - skip_rests--; - break; - } - else /* for handling tied notes */ - { - midipitch[*nnotes] = RESTNOTE; - fract = (float) num[i] / (float) denom[i]; - notelength[*nnotes] = (int) (fract * multiplier + 0.01); - (*nnotes)++; - break; - } - case CHORDON: - inchord = 1; - maxpitch = 0; - break; - case CHORDOFF: - inchord = 0; - midipitch[*nnotes] = maxpitch; - fract = (float) num[i] / (float) denom[i]; - notelength[*nnotes] = (int) (fract * multiplier + 0.01); - (*nnotes)++; - break; - case GRACEON: - ingrace = 1; - break; - case GRACEOFF: - ingrace = 0; - break; - case DOUBLE_BAR: - case SINGLE_BAR: - case REP_BAR: - case REP_BAR2: - case BAR_REP: -/* bar lines at the beginning of the music can cause the - bar numbering to be incorrect and loss of synchrony with - runabc. If no notes were encountered and nbars = 0, - then we are at the beginning and we wish to bypass - these bar line indications. Note bar numbering starts - from 0. [SS] 2013-11-17 -*/ - if (nbars >0 && *nnotes > 0) { - midipitch[*nnotes] = BAR; - notelength[*nnotes] = BAR; - (*nnotes)++; - if (*nbars < maxbars) (*nbars)++; - else printf("abcmatch too many bars\n"); - } - barlineptr[*nbars] = *nnotes; - break; - case TIME: - *timesig_num = num[i]; - *timesig_denom = denom[i]; - if (*timesig_num == 2 && *timesig_denom ==2) { - *timesig_num = 4; - *timesig_denom = 4; - /* [SS] 2013-11-12 */ - } - break; - default: - break; - } - if (*nnotes > 2000) - { - printf ("ran out of space for midipitch for xref %d\n",xrefno); - exit (0); - } - if (*nbars > 599) - { - printf ("ran out of space for barlineptr for xref %d\n",xrefno); - exit (0); - } - } - midipitch[*nnotes + 1] = BAR; /* in case a final bar line is missing */ -/*printf("refno =%d %d notes %d bar lines %d/%d time-signature %d sharps\n" -,xrefno,(*nnotes),(*nbars),(*timesig_num),(*timesig_denom),sf);*/ -#ifdef DEBUG -printf("nbars = %d\n",*nbars); -for (i=0;i<*nbars;i++) printf("barlineptr[%d] = %d\n",i,barlineptr[i]); -i = 0; -while (i < 50) { - for (j=i;j 4) - return 2; - if (pitch > 1) - return 1; - return 0; -} - -int quantize7(int pitch) -{ -if (pitch < -4) - return -3; -if (pitch < -2) - return -2; -if (pitch < 0) - return -1; -if (pitch > 4) - return 3; -if (pitch >2) - return 2; -if (pitch >0) - return 1; -return 0; -} - - - -void -init_histograms () -{ - int i; - for (i = 0; i < 128; i++) - pitch_histogram[i] = 0; - for (i = 0; i < 128; i++) - weighted_pitch_histogram[i] = 0; - for (i = 0; i < 144; i++) - length_histogram[i] = 0; - for (i = 0; i < 128; i++) - interval_histogram[i] = 0; - lastpitch = 0; -} - - -void -compute_note_histograms () -{ - int i, index, delta; - for (i = 0; i < innotes; i++) - { - index = imidipitch[i]; - if (index < 0) - continue; /* [SS] 2013-02-26 */ - pitch_histogram[index]++; - weighted_pitch_histogram[index] += inotelength[i]; - index = inotelength[i]; - if (index > 143) - index = 143; - length_histogram[index]++; - if (lastpitch != 0) {delta = imidipitch[i] - lastpitch; - delta += 60; - if (delta > -1 && delta < 128) interval_histogram[delta]++; - } - lastpitch = imidipitch[i]; - } -} - - -void -print_length_histogram () -{ - int i; - printf ("\n\nlength histogram\n"); - for (i = 0; i < 144; i++) - if (length_histogram[i] > 0) - printf ("%d %d\n", i, length_histogram[i]); -} - - -void -print_pitch_histogram () -{ - int i; - printf ("pitch_histogram\n"); - for (i = 0; i < 128; i++) - if (pitch_histogram[i] > 0) - printf ("%d %d\n", i, pitch_histogram[i]); -} - -void -print_weighted_pitch_histogram () -{ - int i; - printf ("weighted_pitch_histogram\n"); - for (i = 0; i < 128; i++) - if (weighted_pitch_histogram[i] > 0) - printf ("%d %d\n", i, weighted_pitch_histogram[i]); -} - -void -print_interval_pdf() -{ - int i; - printf ("interval_histogram\n"); - for (i = 0; i < 128; i++) - if (interval_histogram[i] > 0) - printf ("%d %d\n", i-60, interval_histogram[i]); -} - - -void -make_and_print_pitch_pdf () -{ - float pdf[12], sum; - int i, j; - for (i = 0; i < 12; i++) - pdf[i] = 0.0; - sum = 0.0; - for (i = 1; i < 128; i++) - { - j = i % 12; - pdf[j] += (float) weighted_pitch_histogram[i]; - sum += (float) weighted_pitch_histogram[i]; - } - if (sum <= 0.000001) - sum = 1.0; - for (i = 0; i < 12; i++) - { - pdf[i] = pdf[i] / sum; - printf ("%d %5.3f\n", i, pdf[i]); - } -} - -void -make_and_print_interval_pdf () -{ - float pdf[24], sum; - int i, j; - for (i = 0; i < 24; i++) - pdf[i] = 0.0; - sum = 0.0; - for (i = 48; i < 72; i++) - { - j = i-48; - pdf[j] = (float) interval_histogram[i]; - sum += (float) interval_histogram[i]; - } - if (sum <= 0.000001) - sum = 1.0; - for (i = 0; i < 24; i++) - { - pdf[i] = pdf[i] / sum; - printf ("%d %5.3f\n", i-12, pdf[i]); - } -} - -void -difference_midipitch (int *pitch_samples, int nsamples) -{ - int i; - int lastsample, newsample; - lastsample = -1; - for (i = 0; i < nsamples; i++) - { - newsample = pitch_samples[i]; - if (newsample == RESTNOTE) - { - pitch_samples[i] = RESTNOTE; - continue; - } - - else if (lastsample == -1) - { - pitch_samples[i] = ANY; - } - else - { - pitch_samples[i] = newsample - lastsample; - if (qntflag > 0) - pitch_samples[i] = quantize7 (pitch_samples[i]); - } - lastsample = newsample; - } -} - - - -int -make_bar_image (int bar_number, int resolution, - int *barlineptr, int *notelength, int nnotes, int delta_pitch, - int *midipitch, int *pitch_samples) -{ -/* the function returns the midipitch at regular time interval - for bar %d xref %d\n,bar_number,xrefnos - (set by resolution) for a particular bar. Thus if you have - the notes CDEF in a bar (L=1/8), then integrated_length - will contain 12,24,36,48. If resolution is set to 6, then - pitch_samples will return the pitch at time units 0, 6, 12,... - mainly CCDDEEFF or 60,60,62,62,64,64,65,65 in midipitch. - The pitch is shifted by delta_pitch to account for a different - key signature in the matching template. - - input: midipitch[],notelength,resolution,delta_pitch,nnotes - bar_number - output: pitch_samples - - the function returns the number of pitch_samples i creates - -*/ - int integrated_length[50]; /* maximum of 50 notes in a bar */ -/* integrated_length is the number of time units in the bar after note i; -*/ - int offset, lastnote, lastpulse, lastsample; - int i, j, t; - offset = barlineptr[bar_number]; -/* double bar is always placed at the beginning of the tune */ - - i = 1; - integrated_length[0] = notelength[offset]; - lastnote = 0; - while (notelength[i + offset] != BAR) - { - if (notelength[i + offset] > 288) - return -1; /* don't try to handle notes longer than 2 whole */ - integrated_length[i] = - integrated_length[i - 1] + notelength[i + offset]; - lastnote = i; - i++; - if (i + offset > nnotes) - { - /* printf("make_bar_image -- running past last note for bar %d xref %d\n",bar_number,xrefno); */ - break; - } - if (i > 49) - { - printf ("make_bar_image -- bar %d has too many notes for xref %d\n", - bar_number, xrefno); - break; - } - } - lastpulse = integrated_length[lastnote]; - i = 0; - j = 0; - t = 0; - while (t < lastpulse) - { - while (t >= integrated_length[i]) - i++; - while (t < integrated_length[i]) - { - if (midipitch[i + offset] == RESTNOTE) - pitch_samples[j] = RESTNOTE; /* REST don't transpose */ - else - pitch_samples[j] = midipitch[i + offset] + delta_pitch; - j++; - t += resolution; - if (j >= 400) - { - printf - ("make_bar_image -- pitch_sample is out of space for bar %d xrefno = %d\n", - bar_number, xrefno); - break; - } - } - } - lastsample = j; - t = 0; - return lastsample; -} - -/* for debugging */ -void -print_bars (int mbar_number, int ibar_number) -{ - int i; - int ioffset, moffset; - ioffset = ibarlineptr[ibar_number]; - moffset = tpbarlineptr[mbar_number]; - i = 0; - while (tpmidipitch[i + moffset] != BAR) - { - printf ("%d %d\n", tpmidipitch[i + moffset], imidipitch[i + ioffset]); - i++; - } - printf ("\n"); -} - - - -#define MIN3(a, b, c) ((a) < (b) ? ((a) < (c) ? (a) : (c)) : ((b) < (c) ? (b) : (c))) - -int levenshtein(int *s1, int *s2, int s1len, int s2len) { - int x, y, lastdiag, olddiag; /* [SS] 2015-10-08 removed unsigned */ - int column[33]; - for (y = 1; y <= s1len; y++) - column[y] = y; - for (x = 1; x <= s2len; x++) { - column[0] = x; - for (y = 1, lastdiag = x-1; y <= s1len; y++) { - olddiag = column[y]; - column[y] = MIN3(column[y] + 1, column[y-1] + 1, lastdiag + (s1[y-1] == s2[x-1] ? 0 : 1)); - lastdiag = olddiag; - } - if (column[x] >= levdist) return levdist; - } - if (column[s1len] < levdist && s1len/(2*levdist)>= 1) return 0; - return(column[s1len]); -} - -int perfect_match (int *s1, int *s2, int s1len) { -int i; -for (i=0;i 0) { - deltapitch = quantize7 (deltapitch); - deltapitchtp = quantize7(deltapitchtp); - } - string1[notes] = 256*deltapitch + inotelength[i + ioffset]; - string2[notes] = 256*deltapitchtp + tpnotelength[i + moffset]; - if (notes < 32) notes++; - else printf("notes > 32\n"); - - /* printf("%d %d\n",deltapitch,deltapitchtp);*/ - } - else { /* first note in bar - no lastnote */ - tplastnote = tpmidipitch[i+moffset]; - lastnote = imidipitch[i+ioffset]; - } - } - else { -/* absolute matching (with transposition) */ -/*printf("%d %d\n",imidipitch[i+ioffset],tpmidipitch[i+moffset]-delta_pitch);*/ - string1[notes] = 256*imidipitch[i+ioffset] + inotelength[i + ioffset]; - string2[notes] = 256*(tpmidipitch[i+moffset] - delta_pitch) + tpnotelength[i + moffset]; - if (notes < 32) notes++; - else printf("notes > 32\n"); - } - i++; - } - if (imidipitch[i + ioffset] != BAR) - return -1; /* in case template has fewer notes */ - - if (notes < 2) - return -1; - if (levdist == 0) return perfect_match(string1,string2,notes); - else return levenshtein(string1,string2,notes,notes); -} - - - -/* absolute match - matches notes relative to key signature */ -/* this matching algorithm ignores bar lines and tries to match - a fixed number of notes set by fnotes. It is unnecessary for - the time signatures in the template and tune to match. */ - -int -fixed_match_notes (int fnotes, int mbar_number, int ibar_number, int delta_pitch) -{ - int i,j, notes; - int ioffset, moffset; - int tplastnote,lastnote; /* for contour matching */ - int deltapitch,deltapitchtp; - int string1[32],string2[32]; - ioffset = ibarlineptr[ibar_number]; - moffset = tpbarlineptr[mbar_number]; - /*printf("ioffset = %d moffset = %d\n",ioffset,moffset);*/ - i = j = 0; - notes = 0; - lastnote = 0; - tplastnote =0; - while (notes < fnotes) - { - /*printf("%d %d\n",imidipitch[j+ioffset],tpmidipitch[i+moffset]);*/ - if (imidipitch[j + ioffset] == RESTNOTE - || imidipitch[j + ioffset] == BAR) - { - j++; - continue; - } /* pass over RESTS or BARS */ - if (tpmidipitch[i + moffset] == RESTNOTE - || tpmidipitch[i + moffset] == BAR) - { - i++; - continue; - } /* pass over RESTS or BARS */ - - if (imidipitch[j + ioffset] == -1 || tpmidipitch[i + moffset] == -1) - { - printf("unexpected negative pitch at %d or %d for xref %d\n",i+ioffset,i+moffset,xrefno); - i++; - j++; - continue; - } /* unknown contour note */ - - if (norhythm == 1) { - inotelength[j + ioffset] = 0; - tpnotelength[i + moffset] = 0; - } - - if (con == 1) { -/* contour matching */ - if (tplastnote !=0) { - deltapitchtp = tpmidipitch[i+moffset] - tplastnote; - deltapitch = imidipitch[j+ioffset] - lastnote; - tplastnote = tpmidipitch[i+moffset]; - lastnote = imidipitch[j+ioffset]; - if (qntflag > 0) { - deltapitch = quantize7 (deltapitch); - deltapitchtp = quantize7(deltapitchtp); - } - string1[notes] = 256*deltapitch + inotelength[j + ioffset]; - string2[notes] = 256*deltapitchtp + tpnotelength[i + moffset]; - if (notes < 32) notes++; - else printf("notes > 32\n"); - - /* printf("deltapitch %d %d\n",deltapitch,deltapitchtp); - printf("length %d %d\n",inotelength[j + ioffset],tpnotelength[i+moffset]); - */ - if (deltapitch != deltapitchtp) - return -1; -/* match succeeded */ - /* printf("%d %d\n",deltapitch,deltapitchtp);*/ - } else { /* first note in bar - no lastnote */ - tplastnote = tpmidipitch[i+moffset]; - lastnote = imidipitch[j+ioffset]; - } - } - else { -/* absolute matching (with transposition) */ - /*printf("%d %d\n",imidipitch[j+ioffset],tpmidipitch[i+moffset]-delta_pitch); - printf("%d %d\n",inotelength[j+ioffset],tpnotelength[i+moffset]); - */ - - string1[notes] = 256*imidipitch[j+ioffset] + inotelength[j + ioffset]; - string2[notes] = 256*(tpmidipitch[i+moffset] - delta_pitch) + tpnotelength[i + moffset]; - if (notes < 32) notes++; - else printf("notes > 32\n"); - } - - i++; - j++; - } - if (notes < 2) - return -1; - /*printf("ioffset = %d moffset = %d\n",ioffset,moffset);*/ - if (levdist == 0) return perfect_match(string1,string2,notes); - else return levenshtein(string1,string2,notes,notes); -} - - - -/* for debugging */ -void -print_bar_samples (int mmsamples, int *mmpitch_samples) -{ - int i; - for (i = 0; i < mmsamples; i++) - printf ("%d %d\n", mmpitch_samples[i], ipitch_samples[i]); -} - - -int -match_samples (int mmsamples, int *mmpitch_samples) -{ - int i; - int changes; - int last_sample; - int string1[32],string2[32]; - int j; - if (mmsamples != isamples) - return -1; - changes = 0; - last_sample = ipitch_samples[0]; /* [SS] 2012-02-05 */ - j = 0; - for (i = 0; i < mmsamples; i++) - { - if (ipitch_samples[i] == -1 || mmpitch_samples[i] == -1) - continue; /* unknown contour note (ie. 1st note) */ - string1[j] = ipitch_samples[i]; - string2[j] = mmpitch_samples[i]; - j++; - if (last_sample != ipitch_samples[i]) - { - last_sample = ipitch_samples[i]; - changes++; - } - } - if (ignore_simple && changes < 3) - return -1; - if (levdist == 0) return perfect_match(string1,string2,j); - else return levenshtein(string1,string2,j,j); -} - - - -int -match_any_bars (int tpbars, int barnum, int delta_key, int nmatches) -{ -/* This function reports any bars in match template match a particular - * bar (barnum) in the tune. If a match is found then barnum is - * reported. It runs in one of two modes depending on the value - * of the variable called resolution. - - * The template is not passed as an argument but is accessed from - * the global arrays, tpmidipitch[1000] and tpnotelength[1000]. - */ - - int kmatches; - int moffset, j, dif; -/* for every bar in match sample */ - kmatches = nmatches; - moffset = 0; - if (resolution > 0) - { - isamples = make_bar_image (barnum, resolution, - ibarlineptr, inotelength, innotes, delta_key, - imidipitch,ipitch_samples); - if (isamples < 1) - return kmatches; - if (con == 1) - difference_midipitch (ipitch_samples, isamples); - for (j = 0; j < tpbars; j++) - { - - dif = match_samples (msamples[j], mpitch_samples + moffset); -#if 0 /* debugging */ - printf("bar %d\n",j); - print_bar_samples(msamples[j],mpitch_samples+moffset); - /*printf("dif = %d\n\n",dif);*/ -#endif - - - moffset += msamples[j]; - if (dif == 0) - { - if (tpxref > 0) tpbarstatus[j] = 1; - kmatches++; - if (kmatches == 1) - printf ("%d %d %d ", fileindex, xrefno, barnum); -/* subtract one from bar because first bar always seems to be 2 */ - else - printf (" %d ", barnum ); - break; - } - } - } - else /* exact match */ - { - for (j = 0; j < tpbars; j++) - { - if (fixednumberofnotes) - dif = fixed_match_notes (fixednumberofnotes,j, barnum, delta_key); - else - dif = match_notes (j, barnum, delta_key); - - - if (dif == 0) - { - if (tpxref > 0) tpbarstatus[j] = 1; - kmatches++; - if (kmatches == 1) - printf ("%d %d %d ", fileindex, xrefno, barnum); - else - printf (" %d ", barnum ); - break; - } /* dif condition */ - } /*for loop */ - } /* resolution condition */ - return kmatches; -} - - -int -match_all_bars (int tpbars, int barnum, int delta_key, int nmatches) -{ -/* This function tries to match all the bars in the match template - with the bars in the bars in the tune. All the template bars - must match in the same sequence in order to be reported. - It runs in one of two modes depending on the value of resolution. -*/ - int kmatches; - int moffset, j, dif; - int matched_bars[20]; -/* for every bar in match sample */ - kmatches = 0; - moffset = 0; - if (resolution > 0) - for (j = 0; j < tpbars; j++) - { - isamples = make_bar_image (barnum + j, resolution, - ibarlineptr, inotelength, innotes, - delta_key, imidipitch ,ipitch_samples); - if (con == 1) - difference_midipitch (ipitch_samples, isamples); - dif = match_samples (msamples[j], mpitch_samples + moffset); - moffset += msamples[j]; - if (dif != 0) - return nmatches; - matched_bars[kmatches] = barnum + j ; - kmatches++; - if (j > 15) - break; - } - else - for (j = 0; j < tpbars; j++) - { - if (fixednumberofnotes) - dif = fixed_match_notes (fixednumberofnotes,j, barnum + j, delta_key); - else - dif = match_notes (j, barnum + j, delta_key); - if (dif != 0) - return nmatches; - matched_bars[kmatches] = barnum + j ; - /*printf("matched %d\n",barnum+j-1);*/ - kmatches++; - if (j > 15) - break; - } - - if (nmatches == 0) - printf ("%d %d ", fileindex, xrefno); - for (j = 0; j < kmatches; j++) - printf ("%d ", matched_bars[j]); - return kmatches + nmatches; -} - - - -void find_and_report_matching_bars -/* top level matching function. Distinguishes between, - any, all and contour modes and calls the right functions -*/ - (int tpbars, int inbars, int transpose, int anymode, int con) -{ - int i, nmatches; - if (con == 1) - { - transpose = 0; /* not applicable here */ - } - nmatches = 0; - if (anymode == 1) /* match any bars in template */ - - for (i = 0; i < inbars; i++) - { - nmatches = match_any_bars (tpbars, i, transpose, nmatches); - } - - else - /* match all bars in template in sequence */ - for (i = 0; i < inbars - tpbars; i++) - { - nmatches = match_all_bars (tpbars, i, transpose, nmatches); - } - - if (nmatches > 0) - printf ("\n"); -} - - -int -find_first_matching_tune_bar (int mbarnumber, int inbars, int transpose) -/* given a bar number in the template, the function looks for - * the first bar in the tune which matches the template. - * Unfortunately since we are only looking for single bars, - * it may not be useful to play around with the resolution - * parameter. - */ -{ - int i, dif; - for (i = 1; i < inbars; i++) - { - dif = match_notes (mbarnumber, i, transpose); - if (dif == 0) - return i; - } - return -1; -} - - - - -int -find_first_matching_template_bar (int barnumber, int tpbars, int transpose) -/* given a bar number in the tune, the function looks for - * the first bar in the template which matches the tune bar. - */ -{ - int i, dif; - for (i = 1; i < tpbars; i++) - { - if (fixednumberofnotes) - dif = fixed_match_notes (fixednumberofnotes,i, barnumber, transpose); - else - dif = match_notes (i, barnumber, transpose); - if (dif == 0) - return i; - } - return -1; -} - -/* this function is not used */ -int -count_matched_template_bars (int tpbars, int inbars, int transpose) -{ - int i, count, bar; - count = 0; - for (i = 0; i < tpbars; i++) - { - bar = find_first_matching_tune_bar (i, inbars, transpose); - /*if (bar >= 0) printf ("bar %d matches %d\n",bar,i); */ - if (bar >= 0) - count++; - } - return count; -} - - -int -count_matched_tune_bars (int tpbars, int inbars, int transpose) -/* used only by brief mode */ -{ - int i, count, bar; - count = 0; - for (i=0;i<300;i++) tpbarstatus[i] = 0; - for (i = 0; i < inbars; i++) - { - bar = find_first_matching_template_bar (i, inbars, transpose); - /*if (bar >= 0) {printf ("bar %d matches %d\n",bar,i); - print_bars(bar,i); - } - */ - if (bar >= 0) { - count++; - tpbarstatus[bar] = 1; - } - } - return count; -} - -int count_matching_template_bars () -{ -int i; -int count; -count = 0; -for (i=0;i<300;i++) - if (tpbarstatus[i] > 0) count++; -return count; -} - -void startfile(); /* links with matchsup.c */ - -int -analyze_abc_file (char *filename) -{ - FILE *fp; - fp = fopen (filename, "rt"); - if (fp == NULL) - { - printf ("cannot open file %s\n", filename); - exit (0); - } - init_histograms (); - while (!feof (fp)) - { - fileindex++; - startfile (); - parsetune (fp); -/* printf("fileindex = %d xrefno =%d\n",fileindex,xrefno); */ -/* printf("%s\n",titlename); */ - if (notes < 10) - continue; - /*print_feature_list(); */ - make_note_representation (&innotes, &inbars, imaxnotes, imaxbars, - &itimesig_num, &itimesig_denom, ibarlineptr, - inotelength, imidipitch); - compute_note_histograms (); - if (action == cpitch_histogram_table) - { - printf ("%4d %s %s\n%s\n", xrefno, filename, keysignature, - titlename); - make_and_print_pitch_pdf (); - init_histograms (); - } - if (action == interval_pdf_table) - { - printf ("%4d %s %s\n%s\n", xrefno, filename, keysignature, - titlename); - make_and_print_interval_pdf (); - init_histograms (); - } - - } - fclose (fp); - switch (action) - { - case cpitch_histogram: - print_pitch_histogram (); - break; - case cwpitch_histogram: - print_weighted_pitch_histogram (); - break; - case clength_histogram: - print_length_histogram (); - break; - case interval_pdf: - print_interval_pdf (); - break; - default: - ; - } - return (0); -} - - -void -event_init (argc, argv, filename) -/* this routine is called first by abcparse.c */ - int argc; - char *argv[]; - char **filename; -{ - int i,j; - - xmatch = 0; - /* look for code checking option */ - if (getarg ("-c", argc, argv) != -1) - { - check = 1; - nowarn = 0; - noerror = 0; - } - else - { - check = 0; - }; - if (getarg ("-ver", argc, argv) != -1) - { - printf ("%s\n", VERSION); - exit (0); - } - /* look for verbose option */ - if (getarg ("-v", argc, argv) != -1) - { - verbose = 1; - } - else - { - verbose = 0; - }; - j = getarg ("-r", argc, argv); - if (j != -1 && argc >= j+1) /* [SS] 2015-02-22 */ - sscanf (argv[j], "%d", &resolution); - if (getarg ("-a", argc, argv) != -1) - anymode = 1; - if (getarg ("-ign", argc, argv) != -1) - ignore_simple = 1; - if (getarg ("-con", argc, argv) != -1) - con = 1; - if (getarg ("-qnt", argc, argv) != -1) - { - qntflag = 1; - con = 1; - } - if (getarg ("-norhythm",argc,argv) != -1) - {norhythm = 1; - resolution = 0; - } - - j = getarg("-lev",argc,argv); - if (j != -1) { - if (argv[j] == NULL) { - printf("'error: expecting a number specifying the maximum levenshtein distance allowed\n"); - exit(0); - } - levdist = readnumf(argv[j]); - } - - j = getarg("-tp",argc,argv); - if (j != -1) { - if (argv[j] == NULL) { - printf ("error: expecting file name and optional reference number\n"); - exit(0); - } - free(templatefile); - templatefile = addstring(argv[j]); - if (*templatefile == '-') { - printf ("error: -tp filename must not begin with a -\n"); - exit(0); - } - if (argv[j+1] != NULL && isdigit(*argv[j+1])) { - tpxref = readnumf(argv[j+1]); - } - anymode = 1; /* only mode which makes sense for a entire tune template*/ - for (i=0;i<300;i++) tpbarstatus[i] = 0; - } - - - j = getarg ("-br", argc, argv); - if (j != -1) - { - if (argv[j] == NULL) - { - printf ("error: expecting number after parameter -br\n"); - exit (0); - } - - sscanf (argv[j], "%d", &cthresh); - brief = 1; - } - - j = getarg ("-fixed", argc, argv); - if (j != -1) - { - if (argv[j] == NULL) - { - printf ("error: expecting number after parameter -fixed\n"); - exit (0); - } - - sscanf (argv[j], "%d", &fixednumberofnotes); - } - - wphist = getarg ("-wpitch_hist", argc, argv); - phist = getarg ("-pitch_hist", argc, argv); - lhist = getarg ("-length_hist", argc, argv); - pdftab = getarg ("-pitch_table", argc, argv); - wpintv = getarg ("-interval_hist",argc,argv); - wpipdf = getarg ("-interval_table",argc,argv); - - if (phist >= 0) - { - action = cpitch_histogram; - } - if (lhist >= 0) - { - action = clength_histogram; - } - if (wphist >= 0) - { - action = cwpitch_histogram; - } - if (pdftab >= 0) - { - action = cpitch_histogram_table; - } - if (wpintv >=0) - { - action = interval_pdf; - } - if (wpipdf >=0) - { - action = interval_pdf_table; - } - - if (brief == 1) - resolution = 0; /* do not compute msamples in main() */ - maxnotes = 3000; - /* allocate space for notes */ - if (fixednumberofnotes > 0) resolution = 0; - pitch = checkmalloc (maxnotes * sizeof (int)); - num = checkmalloc (maxnotes * sizeof (int)); - denom = checkmalloc (maxnotes * sizeof (int)); - pitchline = 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 ("abcmatch version %s\n", VERSION); - printf ("Usage : abcmatch [-options] \n"); - printf (" [reference number] selects a tune\n"); - printf (" -c returns error and warning messages\n"); - printf (" -v selects verbose option\n"); - printf (" -r resolution for matching\n"); - printf (" -fixed fixed number of notes\n"); - printf (" -con pitch contour match\n"); - printf (" -qnt contour quantization\n"); - printf (" -lev use levenshtein distance\n"); - printf (" -ign ignore simple bars\n"); - printf (" -a report any matching bars (default all bars)\n"); - printf (" -br %%d only report number of matched bars when\n\ - above given threshold\n"); - printf (" -tp [reference number]\n"); - printf (" -ver returns version number\n"); - printf (" -pitch_hist pitch histogram\n"); - printf (" -wpitch_hist interval weighted pitch histogram\n"); - printf (" -length_hist note duration histogram\n"); - printf (" -interval_hist pitch interval histogram\n"); - printf (" -pitch_table separate pitch pdfs for each tune\n"); - printf (" -interval_table separate interval pdfs for each tune\n"); - exit (0); - } - else - { - *filename = argv[1]; - }; - /* look for user-supplied output filename */ - dotune = 0; - parseroff (); -} - - - -int -main (argc, argv) - int argc; - char *argv[]; -{ - char *filename; - int i; - int ikey, mkey; - int moffset; - int transpose; - int mseqno; - /* sequence number of template (match.abc) - * mseqno can differ from xrefnum when running count_matched_tune_bars - * because there is no guarantee the xref numbers are in - * sequence in the input file. Hopefully fileindex matches sequence - * number in script calling this executable. - */ - - int kfile, count; - int kount; - -/* initialization */ - action = none; - templatefile = addstring("match.abc"); - event_init (argc, argv, &filename); - init_abbreviations (); - - -/* get the search template from the file match.abc written - in abc format. This file is automatically generated by - runabc.tcl when you are using this search function. -*/ - - if (action != none) - analyze_abc_file (filename); - - else - { /* if not computing histograms */ - if (tpxref >0 ) xmatch = tpxref;/* get only tune with ref number xmatch*/ - parsefile (templatefile); - if (tpxref != 0 && tpxref != xrefno) { - printf("could not find X:%d in file %s\n",tpxref,filename); - exit(0); - } - mkey = sf2midishift[sf + 7]; - mseqno = xrefno; /* if -br mode, X:refno is file sequence number */ - /* xrefno was set by runabc.tcl to be file sequence number of tune */ - /*print_feature_list(); */ - make_note_representation (&tpnotes, &tpbars, tpmaxnotes, tpmaxbars, - &tptimesig_num, &tptimesig_denom, tpbarlineptr, - tpnotelength, tpmidipitch); - - /* trim off any initial bar lines - for (i = 0; i < tpbars; i++) - { - j = i; - if (tpmidipitch[tpbarlineptr[i]] != BAR) - break; - } - for (i = j; i < tpbars; i++) - tpbarlineptr[i - j] = tpbarlineptr[i]; - tpbars -= j; - */ - - moffset = 0; -/* if not exact match, i.e. resolution > 0 compute to an - sample representation of the template. -*/ - if (resolution > 0) - for (i = 0; i < tpbars; i++) - { - msamples[i] = - make_bar_image (i, resolution, - tpbarlineptr, tpnotelength, tpnotes, 0, - tpmidipitch, mpitch_samples + moffset); - if (con == 1) - difference_midipitch (mpitch_samples + moffset, msamples[i]); - moffset += msamples[i]; - if (moffset > 3900) - printf ("abcmatch: out of room in mpitch_samples\n"); - } - -/* now process the input file */ - - - xmatch = 0; /* we do not want to filter any reference numbers here */ - fp = fopen (filename, "rt"); - if (fp == NULL) - { - printf ("cannot open file %s\n", filename); - exit (0); - } - - kfile = 0; - while (!feof (fp)) - { - fileindex++; - startfile (); - parsetune (fp); - /*printf("fileindex = %d xrefno =%d\n",fileindex,xrefno); - printf("%s\n",titlename); */ - if (tpxref == xrefno) { - tp_fileindex = fileindex; - continue; - } - if (notes < 10) - continue; - ikey = sf2midishift[sf + 7]; - /*print_feature_list(); */ - if (voicesused) {/*printf("xref %d has voices\n",xrefno);*/ - continue; - } - make_note_representation (&innotes, &inbars, imaxnotes, imaxbars, - &itimesig_num, &itimesig_denom, - ibarlineptr, inotelength, imidipitch); - { - -/* ignore tunes which do not share the same time signature as the template */ - if ((itimesig_num != tptimesig_num - || itimesig_denom != tptimesig_denom) - && fixednumberofnotes == 0) - continue; - - - transpose = mkey - ikey; -/* we do not know whether to transpose up or down so we will - go in the direction with the smallest number of steps - [SS] 2013-11-12 -*/ - if (transpose > 6) transpose = transpose -12; - if (transpose < -6) transpose = transpose + 12; - - -/* brief mode is used by the grouper in runabc.tcl */ - if (brief) - { - if (mseqno == fileindex) - continue; /* don't check tune against itself */ - count = count_matched_tune_bars (tpbars, inbars, transpose); - kount = count_matching_template_bars(); - /*if (count >= cthresh) [SS] 2013-11-26 */ - if (kount >= cthresh) - { - if (kfile == 0) - printf ("%d\n", tpbars); - printf (" %d %d %d\n", fileindex, count,kount); - kfile++; - } - } - - else -/* top level matching function if not brief mode */ - find_and_report_matching_bars (tpbars, inbars, transpose, - anymode, con); - } - } - fclose (fp); - if (tpxref > 0) { - printf ("%d %d ", tp_fileindex, tpxref); - for (i = 0; i 0) then + the template is represented by mpitch_samples[] and msamples[]. + The temporal quantization representation is created by the + function make_bar_image (). All the bars in the template + file match.abc are converted and stored in mpitch_samples + and msamples[] when match.abc is processed. However, the bars + in the tunes compared are converted only as needed. + + match_notes() is called if there is no temporal quantization + (exact matching). + match_samples() is called if there is temporal quantization. + + difference_midipitch() is called if we are doing contour + matching on the temporal quantized images. For exact matching + (i.e. qnt == 0), when contouring is specified (con ==1) the + differencing (and possibly quantization of the contour) is + performed in the function match_notes. + + count_matched_tune_bars () is called if only want an overview + (brief ==1). + find_and_report_matching_bars() is called if we want the details + (brief !=1); +*/ + + + +#define VERSION "1.74 June 04 2020 abcmatch" +#include +#include +#include +#include "abc.h" +#include "parseabc.h" + +#define MAX(A, B) ((A) > (B) ? (A) : (B)) +#define MIN(A, B) ((A) < (B) ? (A) : (B)) + +#define BAR -1000 +#define RESTNOTE -2000 +#define ANY -3000 + +enum ACTION +{ + none, + cpitch_histogram, + cwpitch_histogram, + clength_histogram, + cpitch_histogram_table, + interval_pdf, + interval_pdf_table +} action; + + +int *checkmalloc (int); +extern int getarg (char *, int, char **); +void free_feature_representation (); + +/* variables shared with abcparse.c and abcstore.c */ +/* many of these variables are used in event_int which has + been moved to this file. +*/ +extern int *pitch, *num, *denom, *pitchline; +extern char **atext, **words; +extern featuretype *feature; +extern int notes; +extern int note_unit_length; +extern int time_num, time_denom; +extern int sf, mi; +extern FILE *fp; +extern int xrefno; +extern int check, nowarn, noerror, verbose, maxnotes, xmatch, dotune; +extern int maxtexts, maxwords; +extern int voicesused; + +/* midipitch: pitch in midi units of each note in the abc file. A pitch + of zero is reserved for rest notes. Bar lines are signaled with BAR. + If contour matching is used, then all the pitch differences are offsetted + by 256 to avoid the likelihood of a zero difference mistaken for a + rest. + notelength: represents the length of a note. A quarter note is 24 units + (assuming L:1/4 in abc file) and all other note sizes are proportional. + Thus a half note is 48 units, a sixteenth note is 6 units. + nnotes: the number of notes in the midipitch array. + nbars: the number of bar lines in the midipitch array. + barlineptr: indicates the index in midipitch, notelength of the start + of bar lines. +*/ + +/* data structure for input to matcher. */ +int imidipitch[2000]; /* pitch-barline midi note representation of input tune */ +int inotelength[2000]; /* notelength representation of input tune */ +int innotes; /*number of notes in imidipitch,inotelength representation */ +int inbars; /*number of bars in input tune */ +int ibarlineptr[500]; /*pointers to bar lines in imidipitch */ +int itimesig_num, itimesig_denom; +int imaxnotes = 2000; /* maximum limits of this program */ +int imaxbars = 200; +int resolution = 12; /* default to 1/8 note resolution */ +int anymode = 0; /* default to matching all bars */ +int ignore_simple = 0; /* ignore simple bars */ +int con = 0; /* default for no contour matching */ +int qnt = 0; /* pitch difference quantization flag */ +int brief = 0; /* set brief to 1 if brief mode */ +int cthresh = 3; /* minimum number of common bars to report */ +int fixednumberofnotes=0; /* for -fixed parameter */ +int fileindex = -1; +int wphist, phist, lhist; /* flags for computing pitch or length histogram */ +int pdftab; /* flag for computing pitch pdf for each tune */ +int qntflag = 0; /* flag for turning on contour quantization */ +int wpintv = 0; /* flag for computing interval pdf */ +int wpipdf = 0; /* flag for computing interval pdf for each tune*/ +int norhythm = 0; /* ignore note lengths */ +int levdist = 0; /* levenshtein distance */ + +char *templatefile; + +/* data structure for matcher (template) (usually match.abc)*/ +int tpmidipitch[1000]; /*pitch-barline representation for template */ +int tpnotelength[1000]; /*note lengths for template */ +int tpnotes; /* number of notes in template */ +int tpbars; /* number of bar lines in template */ +int tpbarlineptr[300]; /* I don't expect 300 bar lines, but lets play safe */ +int tptimesig_num, tptimesig_denom; +int tpmaxnotes = 1000; /* maximum limits of this program */ +int tpmaxbars = 300; +unsigned char tpbarstatus[300]; + +int pitch_histogram[128]; +int weighted_pitch_histogram[128]; +int length_histogram[144]; +int interval_histogram[128]; +int lastpitch; + +/* compute the midi offset for the key signature. Since + we do not have negative indices in arrays, sf2midishift[7] + corresponds to no flats/no sharps, ie. C major scale. + If sf is the number of sharps (when positive) and the number of + flats when negative, then sf2midishift[7+i] is the offset in + semitones from C. Thus G is 7, D is 2, Bb is 10 etc. +*/ +int sf2midishift[] = { 11, 6, 1, 8, 3, 10, 5, 0, 7, 2, 9, 4, 11, 6, 1, }; + + +/* when using a resolution greater than 0, time resolution + * is reduced by a certain factor. The resolution is reduced + * in the arrays ipitch_samples and mpitch_samples. We need + * more room in mpitch_samples for the template, since we + * use this template over and over. Therefore we compute it + * and store it for the entire template. On the other hand, the + * input tune is always changing, so we only store one bar at + * a time for the input tune. + */ + +int ipitch_samples[400], isamples; +int mpitch_samples[4000], msamples[160]; /* maximum number of bars 160 */ + +char titlename[48]; +char keysignature[16]; + +int tpxref = 0; /* template reference number */ +int tp_fileindex = 0; /* file sequence number for tpxref */ + + +void +make_note_representation (int *nnotes, int *nbars, int maxnotes, int maxbars, + int *timesig_num, int *timesig_denom, + int *barlineptr, int *notelength, int *midipitch) +/* converts between the feature,pitch,num,denom representation to the + midipitch,notelength,... representation. This simplification does + not preserve chords, decorations, grace notes etc. +*/ +{ + float fract; + int i; + int skip_rests, multiplier, inchord, ingrace; + int maxpitch=0; /* [SDG] 2020-06-03 */ + *nnotes = 0; + *nbars = 0; + inchord = 0; + ingrace = 0; + skip_rests = 0; + *timesig_num = time_num; + *timesig_denom = time_denom; + multiplier = (int) 24.0; + barlineptr[*nbars] = 0; + for (i = 0; i < notes; i++) + { + switch (feature[i]) + { + case NOTE: + if (inchord) + maxpitch = MAX (maxpitch, pitch[i]); + else if (ingrace) + break; + else + { + midipitch[*nnotes] = pitch[i]; + fract = (float) num[i] / (float) denom[i]; + notelength[*nnotes] = (int) (fract * multiplier + 0.01); + (*nnotes)++; + } + break; + case TNOTE: + midipitch[*nnotes] = pitch[i]; + fract = (float) num[i] / (float) denom[i]; + notelength[*nnotes] = (int) (fract * multiplier + 0.01); + (*nnotes)++; + skip_rests = 2; + break; + case REST: + if (skip_rests > 0) + { + skip_rests--; + break; + } + else /* for handling tied notes */ + { + midipitch[*nnotes] = RESTNOTE; + fract = (float) num[i] / (float) denom[i]; + notelength[*nnotes] = (int) (fract * multiplier + 0.01); + (*nnotes)++; + break; + } + case CHORDON: + inchord = 1; + maxpitch = 0; + break; + case CHORDOFF: + inchord = 0; + midipitch[*nnotes] = maxpitch; + fract = (float) num[i] / (float) denom[i]; + notelength[*nnotes] = (int) (fract * multiplier + 0.01); + (*nnotes)++; + break; + case GRACEON: + ingrace = 1; + break; + case GRACEOFF: + ingrace = 0; + break; + case DOUBLE_BAR: + case SINGLE_BAR: + case REP_BAR: + case REP_BAR2: + case BAR_REP: +/* bar lines at the beginning of the music can cause the + bar numbering to be incorrect and loss of synchrony with + runabc. If no notes were encountered and nbars = 0, + then we are at the beginning and we wish to bypass + these bar line indications. Note bar numbering starts + from 0. [SS] 2013-11-17 +*/ + if (nbars >0 && *nnotes > 0) { + midipitch[*nnotes] = BAR; + notelength[*nnotes] = BAR; + (*nnotes)++; + if (*nbars < maxbars) (*nbars)++; + else printf("abcmatch too many bars\n"); + } + barlineptr[*nbars] = *nnotes; + break; + case TIME: + *timesig_num = num[i]; + *timesig_denom = denom[i]; + if (*timesig_num == 2 && *timesig_denom ==2) { + *timesig_num = 4; + *timesig_denom = 4; + /* [SS] 2013-11-12 */ + } + break; + default: + break; + } + if (*nnotes > 2000) + { + printf ("ran out of space for midipitch for xref %d\n",xrefno); + exit (0); + } + if (*nbars > 599) + { + printf ("ran out of space for barlineptr for xref %d\n",xrefno); + exit (0); + } + } + midipitch[*nnotes + 1] = BAR; /* in case a final bar line is missing */ +/*printf("refno =%d %d notes %d bar lines %d/%d time-signature %d sharps\n" +,xrefno,(*nnotes),(*nbars),(*timesig_num),(*timesig_denom),sf);*/ +#ifdef DEBUG +printf("nbars = %d\n",*nbars); +for (i=0;i<*nbars;i++) printf("barlineptr[%d] = %d\n",i,barlineptr[i]); +i = 0; +while (i < 50) { + for (j=i;j 4) + return 2; + if (pitch > 1) + return 1; + return 0; +} + +int quantize7(int pitch) +{ +if (pitch < -4) + return -3; +if (pitch < -2) + return -2; +if (pitch < 0) + return -1; +if (pitch > 4) + return 3; +if (pitch >2) + return 2; +if (pitch >0) + return 1; +return 0; +} + + + +void +init_histograms () +{ + int i; + for (i = 0; i < 128; i++) + pitch_histogram[i] = 0; + for (i = 0; i < 128; i++) + weighted_pitch_histogram[i] = 0; + for (i = 0; i < 144; i++) + length_histogram[i] = 0; + for (i = 0; i < 128; i++) + interval_histogram[i] = 0; + lastpitch = 0; +} + + +void +compute_note_histograms () +{ + int i, index, delta; + for (i = 0; i < innotes; i++) + { + index = imidipitch[i]; + if (index < 0) + continue; /* [SS] 2013-02-26 */ + pitch_histogram[index]++; + weighted_pitch_histogram[index] += inotelength[i]; + index = inotelength[i]; + if (index > 143) + index = 143; + length_histogram[index]++; + if (lastpitch != 0) {delta = imidipitch[i] - lastpitch; + delta += 60; + if (delta > -1 && delta < 128) interval_histogram[delta]++; + } + lastpitch = imidipitch[i]; + } +} + + +void +print_length_histogram () +{ + int i; + printf ("\n\nlength histogram\n"); + for (i = 0; i < 144; i++) + if (length_histogram[i] > 0) + printf ("%d %d\n", i, length_histogram[i]); +} + + +void +print_pitch_histogram () +{ + int i; + printf ("pitch_histogram\n"); + for (i = 0; i < 128; i++) + if (pitch_histogram[i] > 0) + printf ("%d %d\n", i, pitch_histogram[i]); +} + +void +print_weighted_pitch_histogram () +{ + int i; + printf ("weighted_pitch_histogram\n"); + for (i = 0; i < 128; i++) + if (weighted_pitch_histogram[i] > 0) + printf ("%d %d\n", i, weighted_pitch_histogram[i]); +} + +void +print_interval_pdf() +{ + int i; + printf ("interval_histogram\n"); + for (i = 0; i < 128; i++) + if (interval_histogram[i] > 0) + printf ("%d %d\n", i-60, interval_histogram[i]); +} + + +void +make_and_print_pitch_pdf () +{ + float pdf[12], sum; + int i, j; + for (i = 0; i < 12; i++) + pdf[i] = 0.0; + sum = 0.0; + for (i = 1; i < 128; i++) + { + j = i % 12; + pdf[j] += (float) weighted_pitch_histogram[i]; + sum += (float) weighted_pitch_histogram[i]; + } + if (sum <= 0.000001) + sum = 1.0; + for (i = 0; i < 12; i++) + { + pdf[i] = pdf[i] / sum; + printf ("%d %5.3f\n", i, pdf[i]); + } +} + +void +make_and_print_interval_pdf () +{ + float pdf[24], sum; + int i, j; + for (i = 0; i < 24; i++) + pdf[i] = 0.0; + sum = 0.0; + for (i = 48; i < 72; i++) + { + j = i-48; + pdf[j] = (float) interval_histogram[i]; + sum += (float) interval_histogram[i]; + } + if (sum <= 0.000001) + sum = 1.0; + for (i = 0; i < 24; i++) + { + pdf[i] = pdf[i] / sum; + printf ("%d %5.3f\n", i-12, pdf[i]); + } +} + +void +difference_midipitch (int *pitch_samples, int nsamples) +{ + int i; + int lastsample, newsample; + lastsample = -1; + for (i = 0; i < nsamples; i++) + { + newsample = pitch_samples[i]; + if (newsample == RESTNOTE) + { + pitch_samples[i] = RESTNOTE; + continue; + } + + else if (lastsample == -1) + { + pitch_samples[i] = ANY; + } + else + { + pitch_samples[i] = newsample - lastsample; + if (qntflag > 0) + pitch_samples[i] = quantize7 (pitch_samples[i]); + } + lastsample = newsample; + } +} + + + +int +make_bar_image (int bar_number, int resolution, + int *barlineptr, int *notelength, int nnotes, int delta_pitch, + int *midipitch, int *pitch_samples) +{ +/* the function returns the midipitch at regular time interval + for bar %d xref %d\n,bar_number,xrefnos + (set by resolution) for a particular bar. Thus if you have + the notes CDEF in a bar (L=1/8), then integrated_length + will contain 12,24,36,48. If resolution is set to 6, then + pitch_samples will return the pitch at time units 0, 6, 12,... + mainly CCDDEEFF or 60,60,62,62,64,64,65,65 in midipitch. + The pitch is shifted by delta_pitch to account for a different + key signature in the matching template. + + input: midipitch[],notelength,resolution,delta_pitch,nnotes + bar_number + output: pitch_samples + + the function returns the number of pitch_samples i creates + +*/ + int integrated_length[50]; /* maximum of 50 notes in a bar */ +/* integrated_length is the number of time units in the bar after note i; +*/ + int offset, lastnote, lastpulse, lastsample; + int i, j, t; + offset = barlineptr[bar_number]; +/* double bar is always placed at the beginning of the tune */ + + i = 1; + integrated_length[0] = notelength[offset]; + lastnote = 0; + while (notelength[i + offset] != BAR) + { + if (notelength[i + offset] > 288) + return -1; /* don't try to handle notes longer than 2 whole */ + integrated_length[i] = + integrated_length[i - 1] + notelength[i + offset]; + lastnote = i; + i++; + if (i + offset > nnotes) + { + /* printf("make_bar_image -- running past last note for bar %d xref %d\n",bar_number,xrefno); */ + break; + } + if (i > 49) + { + printf ("make_bar_image -- bar %d has too many notes for xref %d\n", + bar_number, xrefno); + break; + } + } + lastpulse = integrated_length[lastnote]; + i = 0; + j = 0; + t = 0; + while (t < lastpulse) + { + while (t >= integrated_length[i]) + i++; + while (t < integrated_length[i]) + { + if (midipitch[i + offset] == RESTNOTE) + pitch_samples[j] = RESTNOTE; /* REST don't transpose */ + else + pitch_samples[j] = midipitch[i + offset] + delta_pitch; + j++; + t += resolution; + if (j >= 400) + { + printf + ("make_bar_image -- pitch_sample is out of space for bar %d xrefno = %d\n", + bar_number, xrefno); + break; + } + } + } + lastsample = j; + t = 0; + return lastsample; +} + +/* for debugging */ +void +print_bars (int mbar_number, int ibar_number) +{ + int i; + int ioffset, moffset; + ioffset = ibarlineptr[ibar_number]; + moffset = tpbarlineptr[mbar_number]; + i = 0; + while (tpmidipitch[i + moffset] != BAR) + { + printf ("%d %d\n", tpmidipitch[i + moffset], imidipitch[i + ioffset]); + i++; + } + printf ("\n"); +} + + + +#define MIN3(a, b, c) ((a) < (b) ? ((a) < (c) ? (a) : (c)) : ((b) < (c) ? (b) : (c))) + +int levenshtein(int *s1, int *s2, int s1len, int s2len) { + int x, y, lastdiag, olddiag; /* [SS] 2015-10-08 removed unsigned */ + int column[33]; + for (y = 1; y <= s1len; y++) + column[y] = y; + for (x = 1; x <= s2len; x++) { + column[0] = x; + for (y = 1, lastdiag = x-1; y <= s1len; y++) { + olddiag = column[y]; + column[y] = MIN3(column[y] + 1, column[y-1] + 1, lastdiag + (s1[y-1] == s2[x-1] ? 0 : 1)); + lastdiag = olddiag; + } + if (column[x] >= levdist) return levdist; + } + if (column[s1len] < levdist && s1len/(2*levdist)>= 1) return 0; + return(column[s1len]); +} + +int perfect_match (int *s1, int *s2, int s1len) { +int i; +for (i=0;i 0) { + deltapitch = quantize7 (deltapitch); + deltapitchtp = quantize7(deltapitchtp); + } + string1[notes] = 256*deltapitch + inotelength[i + ioffset]; + string2[notes] = 256*deltapitchtp + tpnotelength[i + moffset]; + if (notes < 32) notes++; + else printf("notes > 32\n"); + + /* printf("%d %d\n",deltapitch,deltapitchtp);*/ + } + else { /* first note in bar - no lastnote */ + tplastnote = tpmidipitch[i+moffset]; + lastnote = imidipitch[i+ioffset]; + } + } + else { +/* absolute matching (with transposition) */ +/*printf("%d %d\n",imidipitch[i+ioffset],tpmidipitch[i+moffset]-delta_pitch);*/ + string1[notes] = 256*imidipitch[i+ioffset] + inotelength[i + ioffset]; + string2[notes] = 256*(tpmidipitch[i+moffset] - delta_pitch) + tpnotelength[i + moffset]; + if (notes < 32) notes++; + else printf("notes > 32\n"); + } + i++; + } + if (imidipitch[i + ioffset] != BAR) + return -1; /* in case template has fewer notes */ + + if (notes < 2) + return -1; + if (levdist == 0) return perfect_match(string1,string2,notes); + else return levenshtein(string1,string2,notes,notes); +} + + + +/* absolute match - matches notes relative to key signature */ +/* this matching algorithm ignores bar lines and tries to match + a fixed number of notes set by fnotes. It is unnecessary for + the time signatures in the template and tune to match. */ + +int +fixed_match_notes (int fnotes, int mbar_number, int ibar_number, int delta_pitch) +{ + int i,j, notes; + int ioffset, moffset; + int tplastnote,lastnote; /* for contour matching */ + int deltapitch,deltapitchtp; + int string1[32],string2[32]; + ioffset = ibarlineptr[ibar_number]; + moffset = tpbarlineptr[mbar_number]; + /*printf("ioffset = %d moffset = %d\n",ioffset,moffset);*/ + i = j = 0; + notes = 0; + lastnote = 0; + tplastnote =0; + while (notes < fnotes) + { + /*printf("%d %d\n",imidipitch[j+ioffset],tpmidipitch[i+moffset]);*/ + if (imidipitch[j + ioffset] == RESTNOTE + || imidipitch[j + ioffset] == BAR) + { + j++; + continue; + } /* pass over RESTS or BARS */ + if (tpmidipitch[i + moffset] == RESTNOTE + || tpmidipitch[i + moffset] == BAR) + { + i++; + continue; + } /* pass over RESTS or BARS */ + + if (imidipitch[j + ioffset] == -1 || tpmidipitch[i + moffset] == -1) + { + printf("unexpected negative pitch at %d or %d for xref %d\n",i+ioffset,i+moffset,xrefno); + i++; + j++; + continue; + } /* unknown contour note */ + + if (norhythm == 1) { + inotelength[j + ioffset] = 0; + tpnotelength[i + moffset] = 0; + } + + if (con == 1) { +/* contour matching */ + if (tplastnote !=0) { + deltapitchtp = tpmidipitch[i+moffset] - tplastnote; + deltapitch = imidipitch[j+ioffset] - lastnote; + tplastnote = tpmidipitch[i+moffset]; + lastnote = imidipitch[j+ioffset]; + if (qntflag > 0) { + deltapitch = quantize7 (deltapitch); + deltapitchtp = quantize7(deltapitchtp); + } + string1[notes] = 256*deltapitch + inotelength[j + ioffset]; + string2[notes] = 256*deltapitchtp + tpnotelength[i + moffset]; + if (notes < 32) notes++; + else printf("notes > 32\n"); + + /* printf("deltapitch %d %d\n",deltapitch,deltapitchtp); + printf("length %d %d\n",inotelength[j + ioffset],tpnotelength[i+moffset]); + */ + if (deltapitch != deltapitchtp) + return -1; +/* match succeeded */ + /* printf("%d %d\n",deltapitch,deltapitchtp);*/ + } else { /* first note in bar - no lastnote */ + tplastnote = tpmidipitch[i+moffset]; + lastnote = imidipitch[j+ioffset]; + } + } + else { +/* absolute matching (with transposition) */ + /*printf("%d %d\n",imidipitch[j+ioffset],tpmidipitch[i+moffset]-delta_pitch); + printf("%d %d\n",inotelength[j+ioffset],tpnotelength[i+moffset]); + */ + + string1[notes] = 256*imidipitch[j+ioffset] + inotelength[j + ioffset]; + string2[notes] = 256*(tpmidipitch[i+moffset] - delta_pitch) + tpnotelength[i + moffset]; + if (notes < 32) notes++; + else printf("notes > 32\n"); + } + + i++; + j++; + } + if (notes < 2) + return -1; + /*printf("ioffset = %d moffset = %d\n",ioffset,moffset);*/ + if (levdist == 0) return perfect_match(string1,string2,notes); + else return levenshtein(string1,string2,notes,notes); +} + + + +/* for debugging */ +void +print_bar_samples (int mmsamples, int *mmpitch_samples) +{ + int i; + for (i = 0; i < mmsamples; i++) + printf ("%d %d\n", mmpitch_samples[i], ipitch_samples[i]); +} + + +int +match_samples (int mmsamples, int *mmpitch_samples) +{ + int i; + int changes; + int last_sample; + int string1[32],string2[32]; + int j; + if (mmsamples != isamples) + return -1; + changes = 0; + last_sample = ipitch_samples[0]; /* [SS] 2012-02-05 */ + j = 0; + for (i = 0; i < mmsamples; i++) + { + if (ipitch_samples[i] == -1 || mmpitch_samples[i] == -1) + continue; /* unknown contour note (ie. 1st note) */ + string1[j] = ipitch_samples[i]; + string2[j] = mmpitch_samples[i]; + j++; + if (last_sample != ipitch_samples[i]) + { + last_sample = ipitch_samples[i]; + changes++; + } + } + if (ignore_simple && changes < 3) + return -1; + if (levdist == 0) return perfect_match(string1,string2,j); + else return levenshtein(string1,string2,j,j); +} + + + +int +match_any_bars (int tpbars, int barnum, int delta_key, int nmatches) +{ +/* This function reports any bars in match template match a particular + * bar (barnum) in the tune. If a match is found then barnum is + * reported. It runs in one of two modes depending on the value + * of the variable called resolution. + + * The template is not passed as an argument but is accessed from + * the global arrays, tpmidipitch[1000] and tpnotelength[1000]. + */ + + int kmatches; + int moffset, j, dif; +/* for every bar in match sample */ + kmatches = nmatches; + moffset = 0; + if (resolution > 0) + { + isamples = make_bar_image (barnum, resolution, + ibarlineptr, inotelength, innotes, delta_key, + imidipitch,ipitch_samples); + if (isamples < 1) + return kmatches; + if (con == 1) + difference_midipitch (ipitch_samples, isamples); + for (j = 0; j < tpbars; j++) + { + + dif = match_samples (msamples[j], mpitch_samples + moffset); +#if 0 /* debugging */ + printf("bar %d\n",j); + print_bar_samples(msamples[j],mpitch_samples+moffset); + /*printf("dif = %d\n\n",dif);*/ +#endif + + + moffset += msamples[j]; + if (dif == 0) + { + if (tpxref > 0) tpbarstatus[j] = 1; + kmatches++; + if (kmatches == 1) + printf ("%d %d %d ", fileindex, xrefno, barnum); +/* subtract one from bar because first bar always seems to be 2 */ + else + printf (" %d ", barnum ); + break; + } + } + } + else /* exact match */ + { + for (j = 0; j < tpbars; j++) + { + if (fixednumberofnotes) + dif = fixed_match_notes (fixednumberofnotes,j, barnum, delta_key); + else + dif = match_notes (j, barnum, delta_key); + + + if (dif == 0) + { + if (tpxref > 0) tpbarstatus[j] = 1; + kmatches++; + if (kmatches == 1) + printf ("%d %d %d ", fileindex, xrefno, barnum); + else + printf (" %d ", barnum ); + break; + } /* dif condition */ + } /*for loop */ + } /* resolution condition */ + return kmatches; +} + + +int +match_all_bars (int tpbars, int barnum, int delta_key, int nmatches) +{ +/* This function tries to match all the bars in the match template + with the bars in the bars in the tune. All the template bars + must match in the same sequence in order to be reported. + It runs in one of two modes depending on the value of resolution. +*/ + int kmatches; + int moffset, j, dif; + int matched_bars[20]; +/* for every bar in match sample */ + kmatches = 0; + moffset = 0; + if (resolution > 0) + for (j = 0; j < tpbars; j++) + { + isamples = make_bar_image (barnum + j, resolution, + ibarlineptr, inotelength, innotes, + delta_key, imidipitch ,ipitch_samples); + if (con == 1) + difference_midipitch (ipitch_samples, isamples); + dif = match_samples (msamples[j], mpitch_samples + moffset); + moffset += msamples[j]; + if (dif != 0) + return nmatches; + matched_bars[kmatches] = barnum + j ; + kmatches++; + if (j > 15) + break; + } + else + for (j = 0; j < tpbars; j++) + { + if (fixednumberofnotes) + dif = fixed_match_notes (fixednumberofnotes,j, barnum + j, delta_key); + else + dif = match_notes (j, barnum + j, delta_key); + if (dif != 0) + return nmatches; + matched_bars[kmatches] = barnum + j ; + /*printf("matched %d\n",barnum+j-1);*/ + kmatches++; + if (j > 15) + break; + } + + if (nmatches == 0) + printf ("%d %d ", fileindex, xrefno); + for (j = 0; j < kmatches; j++) + printf ("%d ", matched_bars[j]); + return kmatches + nmatches; +} + + + +void find_and_report_matching_bars +/* top level matching function. Distinguishes between, + any, all and contour modes and calls the right functions +*/ + (int tpbars, int inbars, int transpose, int anymode, int con) +{ + int i, nmatches; + if (con == 1) + { + transpose = 0; /* not applicable here */ + } + nmatches = 0; + if (anymode == 1) /* match any bars in template */ + + for (i = 0; i < inbars; i++) + { + nmatches = match_any_bars (tpbars, i, transpose, nmatches); + } + + else + /* match all bars in template in sequence */ + for (i = 0; i < inbars - tpbars; i++) + { + nmatches = match_all_bars (tpbars, i, transpose, nmatches); + } + + if (nmatches > 0) + printf ("\n"); +} + + +int +find_first_matching_tune_bar (int mbarnumber, int inbars, int transpose) +/* given a bar number in the template, the function looks for + * the first bar in the tune which matches the template. + * Unfortunately since we are only looking for single bars, + * it may not be useful to play around with the resolution + * parameter. + */ +{ + int i, dif; + for (i = 1; i < inbars; i++) + { + dif = match_notes (mbarnumber, i, transpose); + if (dif == 0) + return i; + } + return -1; +} + + + + +int +find_first_matching_template_bar (int barnumber, int tpbars, int transpose) +/* given a bar number in the tune, the function looks for + * the first bar in the template which matches the tune bar. + */ +{ + int i, dif; + for (i = 1; i < tpbars; i++) + { + if (fixednumberofnotes) + dif = fixed_match_notes (fixednumberofnotes,i, barnumber, transpose); + else + dif = match_notes (i, barnumber, transpose); + if (dif == 0) + return i; + } + return -1; +} + +/* this function is not used */ +int +count_matched_template_bars (int tpbars, int inbars, int transpose) +{ + int i, count, bar; + count = 0; + for (i = 0; i < tpbars; i++) + { + bar = find_first_matching_tune_bar (i, inbars, transpose); + /*if (bar >= 0) printf ("bar %d matches %d\n",bar,i); */ + if (bar >= 0) + count++; + } + return count; +} + + +int +count_matched_tune_bars (int tpbars, int inbars, int transpose) +/* used only by brief mode */ +{ + int i, count, bar; + count = 0; + for (i=0;i<300;i++) tpbarstatus[i] = 0; + for (i = 0; i < inbars; i++) + { + bar = find_first_matching_template_bar (i, inbars, transpose); + /*if (bar >= 0) {printf ("bar %d matches %d\n",bar,i); + print_bars(bar,i); + } + */ + if (bar >= 0) { + count++; + tpbarstatus[bar] = 1; + } + } + return count; +} + +int count_matching_template_bars () +{ +int i; +int count; +count = 0; +for (i=0;i<300;i++) + if (tpbarstatus[i] > 0) count++; +return count; +} + +void startfile(); /* links with matchsup.c */ + +int +analyze_abc_file (char *filename) +{ + FILE *fp; + fp = fopen (filename, "rt"); + if (fp == NULL) + { + printf ("cannot open file %s\n", filename); + exit (0); + } + init_histograms (); + while (!feof (fp)) + { + fileindex++; + startfile (); + parsetune (fp); +/* printf("fileindex = %d xrefno =%d\n",fileindex,xrefno); */ +/* printf("%s\n",titlename); */ + if (notes < 10) + continue; + /*print_feature_list(); */ + make_note_representation (&innotes, &inbars, imaxnotes, imaxbars, + &itimesig_num, &itimesig_denom, ibarlineptr, + inotelength, imidipitch); + compute_note_histograms (); + if (action == cpitch_histogram_table) + { + printf ("%4d %s %s\n%s\n", xrefno, filename, keysignature, + titlename); + make_and_print_pitch_pdf (); + init_histograms (); + } + if (action == interval_pdf_table) + { + printf ("%4d %s %s\n%s\n", xrefno, filename, keysignature, + titlename); + make_and_print_interval_pdf (); + init_histograms (); + } + + } + fclose (fp); + switch (action) + { + case cpitch_histogram: + print_pitch_histogram (); + break; + case cwpitch_histogram: + print_weighted_pitch_histogram (); + break; + case clength_histogram: + print_length_histogram (); + break; + case interval_pdf: + print_interval_pdf (); + break; + default: + ; + } + return (0); +} + + +void +event_init (argc, argv, filename) +/* this routine is called first by abcparse.c */ + int argc; + char *argv[]; + char **filename; +{ + int i,j; + + xmatch = 0; + /* look for code checking option */ + if (getarg ("-c", argc, argv) != -1) + { + check = 1; + nowarn = 0; + noerror = 0; + } + else + { + check = 0; + }; + if (getarg ("-ver", argc, argv) != -1) + { + printf ("%s\n", VERSION); + exit (0); + } + /* look for verbose option */ + if (getarg ("-v", argc, argv) != -1) + { + verbose = 1; + } + else + { + verbose = 0; + }; + j = getarg ("-r", argc, argv); + if (j != -1 && argc >= j+1) /* [SS] 2015-02-22 */ + sscanf (argv[j], "%d", &resolution); + if (getarg ("-a", argc, argv) != -1) + anymode = 1; + if (getarg ("-ign", argc, argv) != -1) + ignore_simple = 1; + if (getarg ("-con", argc, argv) != -1) + con = 1; + if (getarg ("-qnt", argc, argv) != -1) + { + qntflag = 1; + con = 1; + } + if (getarg ("-norhythm",argc,argv) != -1) + {norhythm = 1; + resolution = 0; + } + + j = getarg("-lev",argc,argv); + if (j != -1) { + if (argv[j] == NULL) { + printf("'error: expecting a number specifying the maximum levenshtein distance allowed\n"); + exit(0); + } + levdist = readnumf(argv[j]); + } + + j = getarg("-tp",argc,argv); + if (j != -1) { + if (argv[j] == NULL) { + printf ("error: expecting file name and optional reference number\n"); + exit(0); + } + free(templatefile); + templatefile = addstring(argv[j]); + if (*templatefile == '-') { + printf ("error: -tp filename must not begin with a -\n"); + exit(0); + } + if (argv[j+1] != NULL && isdigit(*argv[j+1])) { + tpxref = readnumf(argv[j+1]); + } + anymode = 1; /* only mode which makes sense for a entire tune template*/ + for (i=0;i<300;i++) tpbarstatus[i] = 0; + } + + + j = getarg ("-br", argc, argv); + if (j != -1) + { + if (argv[j] == NULL) + { + printf ("error: expecting number after parameter -br\n"); + exit (0); + } + + sscanf (argv[j], "%d", &cthresh); + brief = 1; + } + + j = getarg ("-fixed", argc, argv); + if (j != -1) + { + if (argv[j] == NULL) + { + printf ("error: expecting number after parameter -fixed\n"); + exit (0); + } + + sscanf (argv[j], "%d", &fixednumberofnotes); + } + + wphist = getarg ("-wpitch_hist", argc, argv); + phist = getarg ("-pitch_hist", argc, argv); + lhist = getarg ("-length_hist", argc, argv); + pdftab = getarg ("-pitch_table", argc, argv); + wpintv = getarg ("-interval_hist",argc,argv); + wpipdf = getarg ("-interval_table",argc,argv); + + if (phist >= 0) + { + action = cpitch_histogram; + } + if (lhist >= 0) + { + action = clength_histogram; + } + if (wphist >= 0) + { + action = cwpitch_histogram; + } + if (pdftab >= 0) + { + action = cpitch_histogram_table; + } + if (wpintv >=0) + { + action = interval_pdf; + } + if (wpipdf >=0) + { + action = interval_pdf_table; + } + + if (brief == 1) + resolution = 0; /* do not compute msamples in main() */ + maxnotes = 3000; + /* allocate space for notes */ + if (fixednumberofnotes > 0) resolution = 0; + pitch = checkmalloc (maxnotes * sizeof (int)); + num = checkmalloc (maxnotes * sizeof (int)); + denom = checkmalloc (maxnotes * sizeof (int)); + pitchline = 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 ("abcmatch version %s\n", VERSION); + printf ("Usage : abcmatch [-options] \n"); + printf (" [reference number] selects a tune\n"); + printf (" -c returns error and warning messages\n"); + printf (" -v selects verbose option\n"); + printf (" -r resolution for matching\n"); + printf (" -fixed fixed number of notes\n"); + printf (" -con pitch contour match\n"); + printf (" -qnt contour quantization\n"); + printf (" -lev use levenshtein distance\n"); + printf (" -ign ignore simple bars\n"); + printf (" -a report any matching bars (default all bars)\n"); + printf (" -br %%d only report number of matched bars when\n\ + above given threshold\n"); + printf (" -tp [reference number]\n"); + printf (" -ver returns version number\n"); + printf (" -pitch_hist pitch histogram\n"); + printf (" -wpitch_hist interval weighted pitch histogram\n"); + printf (" -length_hist note duration histogram\n"); + printf (" -interval_hist pitch interval histogram\n"); + printf (" -pitch_table separate pitch pdfs for each tune\n"); + printf (" -interval_table separate interval pdfs for each tune\n"); + exit (0); + } + else + { + *filename = argv[1]; + }; + /* look for user-supplied output filename */ + dotune = 0; + parseroff (); +} + + + +int +main (argc, argv) + int argc; + char *argv[]; +{ + char *filename; + int i; + int ikey, mkey; + int moffset; + int transpose; + int mseqno; + /* sequence number of template (match.abc) + * mseqno can differ from xrefnum when running count_matched_tune_bars + * because there is no guarantee the xref numbers are in + * sequence in the input file. Hopefully fileindex matches sequence + * number in script calling this executable. + */ + + int kfile, count; + int kount; + +/* initialization */ + action = none; + templatefile = addstring("match.abc"); + event_init (argc, argv, &filename); + init_abbreviations (); + + +/* get the search template from the file match.abc written + in abc format. This file is automatically generated by + runabc.tcl when you are using this search function. +*/ + + if (action != none) + analyze_abc_file (filename); + + else + { /* if not computing histograms */ + if (tpxref >0 ) xmatch = tpxref;/* get only tune with ref number xmatch*/ + parsefile (templatefile); + if (tpxref != 0 && tpxref != xrefno) { + printf("could not find X:%d in file %s\n",tpxref,filename); + exit(0); + } + mkey = sf2midishift[sf + 7]; + mseqno = xrefno; /* if -br mode, X:refno is file sequence number */ + /* xrefno was set by runabc.tcl to be file sequence number of tune */ + /*print_feature_list(); */ + make_note_representation (&tpnotes, &tpbars, tpmaxnotes, tpmaxbars, + &tptimesig_num, &tptimesig_denom, tpbarlineptr, + tpnotelength, tpmidipitch); + + /* trim off any initial bar lines + for (i = 0; i < tpbars; i++) + { + j = i; + if (tpmidipitch[tpbarlineptr[i]] != BAR) + break; + } + for (i = j; i < tpbars; i++) + tpbarlineptr[i - j] = tpbarlineptr[i]; + tpbars -= j; + */ + + moffset = 0; +/* if not exact match, i.e. resolution > 0 compute to an + sample representation of the template. +*/ + if (resolution > 0) + for (i = 0; i < tpbars; i++) + { + msamples[i] = + make_bar_image (i, resolution, + tpbarlineptr, tpnotelength, tpnotes, 0, + tpmidipitch, mpitch_samples + moffset); + if (con == 1) + difference_midipitch (mpitch_samples + moffset, msamples[i]); + moffset += msamples[i]; + if (moffset > 3900) + printf ("abcmatch: out of room in mpitch_samples\n"); + } + +/* now process the input file */ + + + xmatch = 0; /* we do not want to filter any reference numbers here */ + fp = fopen (filename, "rt"); + if (fp == NULL) + { + printf ("cannot open file %s\n", filename); + exit (0); + } + + kfile = 0; + while (!feof (fp)) + { + fileindex++; + startfile (); + parsetune (fp); + /*printf("fileindex = %d xrefno =%d\n",fileindex,xrefno); + printf("%s\n",titlename); */ + if (tpxref == xrefno) { + tp_fileindex = fileindex; + continue; + } + if (notes < 10) + continue; + ikey = sf2midishift[sf + 7]; + /*print_feature_list(); */ + if (voicesused) {/*printf("xref %d has voices\n",xrefno);*/ + continue; + } + make_note_representation (&innotes, &inbars, imaxnotes, imaxbars, + &itimesig_num, &itimesig_denom, + ibarlineptr, inotelength, imidipitch); + { + +/* ignore tunes which do not share the same time signature as the template */ + if ((itimesig_num != tptimesig_num + || itimesig_denom != tptimesig_denom) + && fixednumberofnotes == 0) + continue; + + + transpose = mkey - ikey; +/* we do not know whether to transpose up or down so we will + go in the direction with the smallest number of steps + [SS] 2013-11-12 +*/ + if (transpose > 6) transpose = transpose -12; + if (transpose < -6) transpose = transpose + 12; + + +/* brief mode is used by the grouper in runabc.tcl */ + if (brief) + { + if (mseqno == fileindex) + continue; /* don't check tune against itself */ + count = count_matched_tune_bars (tpbars, inbars, transpose); + kount = count_matching_template_bars(); + /*if (count >= cthresh) [SS] 2013-11-26 */ + if (kount >= cthresh) + { + if (kfile == 0) + printf ("%d\n", tpbars); + printf (" %d %d %d\n", fileindex, count,kount); + kfile++; + } + } + + else +/* top level matching function if not brief mode */ + find_and_report_matching_bars (tpbars, inbars, transpose, + anymode, con); + } + } + fclose (fp); + if (tpxref > 0) { + printf ("%d %d ", tp_fileindex, tpxref); + for (i = 0; i -#include - -/* define USE_INDEX if your C libraries have index() instead of strchr() */ -#ifdef USE_INDEX -#define strchr index -#endif - -int arg_index = 0; -char *arg_option; -char *pvcon = NULL; - -char* crack(argc, argv, flags, ign) - int argc; char **argv; char *flags; int ign; -{ - char *pv, *flgp, *strchr(); - while ((arg_index) < argc) - { - if (pvcon != NULL) - pv = pvcon; - else - { - if (++arg_index >= argc) return(NULL); - pv = argv[arg_index]; - if (*pv != '-') - return(NULL); - } - pv++; /* skip '-' or prev. flag */ - if (pv != NULL) - { - if ((flgp=strchr(flags,*pv)) != NULL) - { - pvcon = pv; - if (*(flgp+1) == '|') { arg_option = pv+1; pvcon = NULL; } - return(pv); - } - else - if (!ign) - { - fprintf(stderr, "%s: no such flag: %s\n", argv[0], pv); - exit(0); - } - else - pvcon = NULL; - } - pvcon = NULL; - } - return(NULL); - } - +/* + * crack - put the command line to the vice. + * + * This routine takes four arguments, the variables argc and argv from + * the program's main procedure, a list of flags to recognize, and an + * indication whether to ignore unknown flags or not. + * + * The action of crack is to read a command line laid out in the format: + * + * % command [flags]... [files]... + * + * where the flags are of the form -foption, that is, a minus, a character + * and an optional argument, butted up to the character. No space may + * appear between any flag and its option. The first command line + * argument not preceded by '-' is taken as the end of the flags and the + * beginning of file names. + * + * The flags argument to crack looks like this: "a|b|cd" for flags a b c + * and d. In this example, a and b take (optional!) options, as specified + * by the trailing colon, c and d do not. When crack scans a flag, it + * returns the flag character after setting the external character pointer + * arg_option to the option part. It also sets arg_index to the index of + * the argv variable scanned. Crack returns NULL when it has scanned the + * last flag. The value of arg_index then points at the first + * argument after the last flag, which should be the first filename, if + * any. If there are no arguments, or no more arguments after reading + * the flags, arg_index == argc; + * + * Flags may be concatenated, for instance, using the flags argument + * given above: -cd will treat c and d as + * flags. -cdaoption also works. However, tacking what you think is + * a flag after another flag that takes an option will cause the flag to + * be lost. I.e., -ac will treat c as an option, not a flag. + * + * When the ignore flag is zero, flags scanned that are not in the flags + * variable generate an error message and crack returns EOF. If ignore is + * nonzero, unknown flags are suppressed, that is, no flag is returned. + * The purpose of ignoring flags is so that more than one part of a + * program can scan the command line without having to know about the + * flags of all the other parts. For instance, where a program calculates + * a sampling rate by one flag and a value in seconds in another, it must + * search for the sampling rate first, then the time value. Two calls to + * crack() would be required, one to scan just for the flag setting sampling + * rate, another to ignore the rate flag, but to set the time value based + * on the sampling rate. + * NOTE: WHEN MAKING MORE THAN ONE CALL TO crack() IN A PROGRAM, IT + * IS NECESSARY TO RESET arg_index TO 0 FIRST. + * + * When ignoring unknown flags, if an unknown flag has an option + * associated with it, the option is also ignored. Care should be excercised + * here because it may be possible that the associated "option" is really + * more concatenated flags. These, if any, are lost. The rule is that, + * when ignoring unknown flags, the first instance of an unknown flag + * causes that flag and the rest of that argument to be discarded. For + * instance, if flags is set to "a:b:cd", and a command line: + * "-zcdaoption" is supplied, c d and a would be ignored because they come + * after z in the same argument. The point is there is no way to disambiguate + * flags from unknown options when ignoring flags, so concatenating options, + * while nice, is problematical. + * + * FIX applied : index changed to index 6/9/96 + * FIX applied : crack returns char* instead of char 22/6/98 + */ + +#include +#include + +/* define USE_INDEX if your C libraries have index() instead of strchr() */ +#ifdef USE_INDEX +#define strchr index +#endif + +int arg_index = 0; +char *arg_option; +char *pvcon = NULL; + +char* crack(argc, argv, flags, ign) + int argc; char **argv; char *flags; int ign; +{ + char *pv, *flgp, *strchr(); + while ((arg_index) < argc) + { + if (pvcon != NULL) + pv = pvcon; + else + { + if (++arg_index >= argc) return(NULL); + pv = argv[arg_index]; + if (*pv != '-') + return(NULL); + } + pv++; /* skip '-' or prev. flag */ + if (pv != NULL) + { + if ((flgp=strchr(flags,*pv)) != NULL) + { + pvcon = pv; + if (*(flgp+1) == '|') { arg_option = pv+1; pvcon = NULL; } + return(pv); + } + else + if (!ign) + { + fprintf(stderr, "%s: no such flag: %s\n", argv[0], pv); + exit(0); + } + else + pvcon = NULL; + } + pvcon = NULL; + } + return(NULL); + } + diff -Nru abcmidi-20200706/debian/changelog abcmidi-20200728/debian/changelog --- abcmidi-20200706/debian/changelog 2020-07-06 17:22:38.000000000 +0000 +++ abcmidi-20200728/debian/changelog 2020-07-30 15:48:03.000000000 +0000 @@ -1,3 +1,10 @@ +abcmidi (20200728-1) unstable; urgency=medium + + * New upstream version 20200728 + * Do not clean before build for now + + -- Dennis Braun Thu, 30 Jul 2020 17:48:03 +0200 + abcmidi (20200706-1) unstable; urgency=medium * New upstream version 20200706 diff -Nru abcmidi-20200706/debian/patches/fix-clean.patch abcmidi-20200728/debian/patches/fix-clean.patch --- abcmidi-20200706/debian/patches/fix-clean.patch 2020-07-04 13:32:27.000000000 +0000 +++ abcmidi-20200728/debian/patches/fix-clean.patch 2020-07-30 15:40:56.000000000 +0000 @@ -1,18 +1,16 @@ -Description: Fix clean target - Add -f option to rm in Makefile to prevent FTBFS at the clean target. -Author: Ross Gammon +Description: Disable clean, because nothing to remove at start. +Author: Dennis Braun Forwarded: not-yet -Last-Update: 2018-03-03 +Last-Update: 2020-07-30 --- This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ --- abcmidi.orig/Makefile +++ abcmidi/Makefile -@@ -137,7 +137,7 @@ - matchsup.o : matchsup.c abc.h parseabc.h parser2.h - - clean : -- rm *.o ${binaries} -+ rm -f *.o ${binaries} - - install: abc2midi midi2abc abc2abc mftext midicopy yaps abcmatch - $(INSTALL) -d $(DESTDIR)$(bindir) +@@ -137,7 +137,6 @@ + matchsup.o : matchsup.c abc.h parseabc.h parser2.h + + clean : +- rm *.o ${binaries} + + install: abc2midi midi2abc abc2abc mftext midicopy yaps abcmatch + $(INSTALL) -d $(DESTDIR)$(bindir) diff -Nru abcmidi-20200706/debug.c abcmidi-20200728/debug.c --- abcmidi-20200706/debug.c 2020-06-06 23:05:34.000000000 +0000 +++ abcmidi-20200728/debug.c 2020-07-27 15:52:43.000000000 +0000 @@ -1,252 +1,252 @@ -/* debug.c */ -/* part of yaps - abc to PostScript converter */ -/* print tune data structure to screen for debugging purposes only */ - -#include -#include "abc.h" -#include "structs.h" - -void showfeature(struct feature *ft) -{ - char* astring; - struct fract* afract; - struct key* akey; - struct rest* arest; - struct tuple* atuple; - struct note* anote; - struct aclef* theclef; - switch (ft->type) { - case SINGLE_BAR: printf("SINGLE_BAR\n"); - break; - case DOUBLE_BAR: printf("DOUBLE_BAR\n"); - break; - case BAR_REP: printf("BAR_REP\n"); - break; - case REP_BAR: printf("REP_BAR\n"); - break; - case REP1: printf("REP1\n"); - break; - case REP2: printf("REP2\n"); - break; - case PLAY_ON_REP: - printf("PLAY_ON_REP %s\n", (char*)ft->item); - break; - case BAR1: printf("BAR1\n"); - break; - case REP_BAR2: printf("REP_BAR2\n"); - break; - case DOUBLE_REP: printf("DOUBLE_REP\n"); - break; - case THICK_THIN: printf("THICK_THIN\n"); - break; - case THIN_THICK: printf("THIN_THICK\n"); - break; - case PART: printf("PART\n"); - astring = ft->item; - break; - case TEMPO: printf("TEMPO\n"); - break; - case TIME: - afract = ft->item; - printf("TIME %d / %d\n", afract->num, afract->denom); - break; - case KEY: printf("KEY\n"); - akey = ft->item; - break; - case REST: printf("REST\n"); - arest = ft->item; - break; - case TUPLE: printf("TUPLE\n"); - atuple = ft->item; - break; - case NOTE: - anote = ft->item; - printf("NOTE %c%c %d / %d\n", anote->accidental, anote->pitch, - anote->len.num, anote->len.denom); - if (anote->gchords != NULL) { - astring = firstitem(anote->gchords); - while (astring != NULL) { - printf("gchord: %s\n", astring); - astring = nextitem(anote->gchords); - }; - }; - if (anote->syllables != NULL) { - astring = firstitem(anote->syllables); - while (astring != NULL) { - printf("syllable: %s\n", astring); - astring = nextitem(anote->syllables); - }; - }; - printf("stemup=%d beaming=%d base =%d base_exp=%d x=%.1f left=%.1f right=%.1f\n", - anote->stemup, anote->beaming, anote->base, anote->base_exp, - ft->x, ft->xleft, ft->xright); - break; - case CHORDNOTE: - anote = ft->item; - printf("CHORDNOTE %c%c %d / %d\n", anote->accidental, anote->pitch, - anote->len.num, anote->len.denom); - printf("stemup=%d beaming=%d base =%d base_exp=%d x=%.1f left=%.1f right=%.1f\n", - anote->stemup, anote->beaming, anote->base, anote->base_exp, - ft->x, ft->xleft, ft->xright); - printf("x=%.1f\n", ft->x); - break; - case NONOTE: printf("NONOTE\n"); - break; - case OLDTIE: printf("OLDTIE\n"); - break; - case TEXT: printf("TEXT\n"); - break; - case SLUR_ON: printf("SLUR_ON\n"); - break; - case SLUR_OFF: printf("SLUR_OFF\n"); - break; - case TIE: printf("TIE\n"); - break; - case CLOSE_TIE: printf("CLOSE_TIE\n"); - break; - case TITLE: printf("TITLE\n"); - break; - case CHANNEL: printf("CHANNEL\n"); - break; - case TRANSPOSE: printf("TRANSPOSE\n"); - break; - case RTRANSPOSE: printf("RTRANSPOSE\n"); - break; - case GRACEON: printf("GRACEON\n"); - break; - case GRACEOFF: printf("GRACEOFF\n"); - break; - case SETGRACE: printf("SETGRACE\n"); - break; - case SETC: printf("SETC\n"); - break; - case GCHORD: printf("GCHORD\n"); - break; - case GCHORDON: printf("GCHORDON\n"); - break; - case GCHORDOFF: printf("GCHORDOFF\n"); - break; - case VOICE: printf("VOICE\n"); - break; - case CHORDON: printf("CHORDON\n"); - break; - case CHORDOFF: printf("CHORDOFF\n"); - break; - case SLUR_TIE: printf("SLUR_TIE\n"); - break; - case TNOTE: printf("TNOTE\n"); - break; - case LT: printf("LT\n"); - break; - case GT: printf("GT\n"); - break; - case DYNAMIC: printf("DYNAMIC\n"); - break; - case LINENUM: - printf("LINENUM %p\n", (void *)(ft->item)); /* [SDG] 2020-06-03 */ - break; - case MUSICLINE: printf("MUSICLINE\n"); - break; - case PRINTLINE: printf("PRINTLINE\n"); - break; - case MUSICSTOP: printf("MUSICSTOP\n"); - break; - case WORDLINE: printf("WORDLINE\n"); - break; - case WORDSTOP: printf("WORDSTOP\n"); - break; - case INSTRUCTION: printf("INSTRUCTION\n"); - break; - case NOBEAM: printf("NOBEAM\n"); - break; - case CLEF: printf("CLEF\n"); - theclef = ft->item; - break; - case SPLITVOICE: printf("SPLITVOICE\n"); - default: - printf("unknown type: %d\n", (int)ft->type); - break; - }; -} - - -static int showline(v) -struct voice* v; -/* draws one line of music from specified voice */ -{ - struct feature* ft; -/* struct note* anote; - struct key* akey; - char* astring; - struct fract* afract; - struct rest* arest; - struct tuple* atuple; - struct aclef* theclef; -*/ - int sharps; - struct chord* thischord; - int chordcount; - int printedline; - - if (v->place == NULL) { - return(0); - }; - chordcount = 0; - v->beamed_tuple_pending = 0; - thischord = NULL; - if (v->keysig == NULL) { - event_error("Voice has no key signature"); - } else { - sharps = v->keysig->sharps; - }; - ft = v->place; - printedline = 0; - while ((ft != NULL) && (!printedline)) { - /* printf("type = %d\n", ft->type); */ - showfeature(ft); - ft = ft->next; - }; - v->place = ft; - return(1); -} - -void showtune(struct tune* t) -/* print abc structure to the screen */ -{ - char* atitle; - int notesdone; - struct voice* thisvoice; - - notesdone = 0; - atitle = firstitem(&t->title); - while (atitle != NULL) { - printf("TITLE: %s\n", atitle); - atitle = nextitem(&t->title); - }; - if (t->composer != NULL) { - printf("COMPOSER: %s\n", t->composer); /* [SDG] 2020-06-03 */ - }; - if (t->origin != NULL) { - printf("ORIGIN: %s\n", t->origin); - }; - if (t->parts != NULL) { - printf("PARTS: %s\n", t->parts); - }; - thisvoice = firstitem(&t->voices); - while (thisvoice != NULL) { - thisvoice->place = thisvoice->first; - thisvoice = nextitem(&t->voices); - }; -/* - doneline = 1; - while(doneline) { -*/ - thisvoice = firstitem(&t->voices); - while (thisvoice != NULL) { - printf("-----------(voice %d)-------\n", thisvoice->voiceno); - showline(thisvoice); - thisvoice = nextitem(&t->voices); - }; -/* }; */ -} - +/* debug.c */ +/* part of yaps - abc to PostScript converter */ +/* print tune data structure to screen for debugging purposes only */ + +#include +#include "abc.h" +#include "structs.h" + +void showfeature(struct feature *ft) +{ + char* astring; + struct fract* afract; + struct key* akey; + struct rest* arest; + struct tuple* atuple; + struct note* anote; + struct aclef* theclef; + switch (ft->type) { + case SINGLE_BAR: printf("SINGLE_BAR\n"); + break; + case DOUBLE_BAR: printf("DOUBLE_BAR\n"); + break; + case BAR_REP: printf("BAR_REP\n"); + break; + case REP_BAR: printf("REP_BAR\n"); + break; + case REP1: printf("REP1\n"); + break; + case REP2: printf("REP2\n"); + break; + case PLAY_ON_REP: + printf("PLAY_ON_REP %s\n", (char*)ft->item); + break; + case BAR1: printf("BAR1\n"); + break; + case REP_BAR2: printf("REP_BAR2\n"); + break; + case DOUBLE_REP: printf("DOUBLE_REP\n"); + break; + case THICK_THIN: printf("THICK_THIN\n"); + break; + case THIN_THICK: printf("THIN_THICK\n"); + break; + case PART: printf("PART\n"); + astring = ft->item; + break; + case TEMPO: printf("TEMPO\n"); + break; + case TIME: + afract = ft->item; + printf("TIME %d / %d\n", afract->num, afract->denom); + break; + case KEY: printf("KEY\n"); + akey = ft->item; + break; + case REST: printf("REST\n"); + arest = ft->item; + break; + case TUPLE: printf("TUPLE\n"); + atuple = ft->item; + break; + case NOTE: + anote = ft->item; + printf("NOTE %c%c %d / %d\n", anote->accidental, anote->pitch, + anote->len.num, anote->len.denom); + if (anote->gchords != NULL) { + astring = firstitem(anote->gchords); + while (astring != NULL) { + printf("gchord: %s\n", astring); + astring = nextitem(anote->gchords); + }; + }; + if (anote->syllables != NULL) { + astring = firstitem(anote->syllables); + while (astring != NULL) { + printf("syllable: %s\n", astring); + astring = nextitem(anote->syllables); + }; + }; + printf("stemup=%d beaming=%d base =%d base_exp=%d x=%.1f left=%.1f right=%.1f\n", + anote->stemup, anote->beaming, anote->base, anote->base_exp, + ft->x, ft->xleft, ft->xright); + break; + case CHORDNOTE: + anote = ft->item; + printf("CHORDNOTE %c%c %d / %d\n", anote->accidental, anote->pitch, + anote->len.num, anote->len.denom); + printf("stemup=%d beaming=%d base =%d base_exp=%d x=%.1f left=%.1f right=%.1f\n", + anote->stemup, anote->beaming, anote->base, anote->base_exp, + ft->x, ft->xleft, ft->xright); + printf("x=%.1f\n", ft->x); + break; + case NONOTE: printf("NONOTE\n"); + break; + case OLDTIE: printf("OLDTIE\n"); + break; + case TEXT: printf("TEXT\n"); + break; + case SLUR_ON: printf("SLUR_ON\n"); + break; + case SLUR_OFF: printf("SLUR_OFF\n"); + break; + case TIE: printf("TIE\n"); + break; + case CLOSE_TIE: printf("CLOSE_TIE\n"); + break; + case TITLE: printf("TITLE\n"); + break; + case CHANNEL: printf("CHANNEL\n"); + break; + case TRANSPOSE: printf("TRANSPOSE\n"); + break; + case RTRANSPOSE: printf("RTRANSPOSE\n"); + break; + case GRACEON: printf("GRACEON\n"); + break; + case GRACEOFF: printf("GRACEOFF\n"); + break; + case SETGRACE: printf("SETGRACE\n"); + break; + case SETC: printf("SETC\n"); + break; + case GCHORD: printf("GCHORD\n"); + break; + case GCHORDON: printf("GCHORDON\n"); + break; + case GCHORDOFF: printf("GCHORDOFF\n"); + break; + case VOICE: printf("VOICE\n"); + break; + case CHORDON: printf("CHORDON\n"); + break; + case CHORDOFF: printf("CHORDOFF\n"); + break; + case SLUR_TIE: printf("SLUR_TIE\n"); + break; + case TNOTE: printf("TNOTE\n"); + break; + case LT: printf("LT\n"); + break; + case GT: printf("GT\n"); + break; + case DYNAMIC: printf("DYNAMIC\n"); + break; + case LINENUM: + printf("LINENUM %p\n", (void *)(ft->item)); /* [SDG] 2020-06-03 */ + break; + case MUSICLINE: printf("MUSICLINE\n"); + break; + case PRINTLINE: printf("PRINTLINE\n"); + break; + case MUSICSTOP: printf("MUSICSTOP\n"); + break; + case WORDLINE: printf("WORDLINE\n"); + break; + case WORDSTOP: printf("WORDSTOP\n"); + break; + case INSTRUCTION: printf("INSTRUCTION\n"); + break; + case NOBEAM: printf("NOBEAM\n"); + break; + case CLEF: printf("CLEF\n"); + theclef = ft->item; + break; + case SPLITVOICE: printf("SPLITVOICE\n"); + default: + printf("unknown type: %d\n", (int)ft->type); + break; + }; +} + + +static int showline(v) +struct voice* v; +/* draws one line of music from specified voice */ +{ + struct feature* ft; +/* struct note* anote; + struct key* akey; + char* astring; + struct fract* afract; + struct rest* arest; + struct tuple* atuple; + struct aclef* theclef; +*/ + int sharps; + struct chord* thischord; + int chordcount; + int printedline; + + if (v->place == NULL) { + return(0); + }; + chordcount = 0; + v->beamed_tuple_pending = 0; + thischord = NULL; + if (v->keysig == NULL) { + event_error("Voice has no key signature"); + } else { + sharps = v->keysig->sharps; + }; + ft = v->place; + printedline = 0; + while ((ft != NULL) && (!printedline)) { + /* printf("type = %d\n", ft->type); */ + showfeature(ft); + ft = ft->next; + }; + v->place = ft; + return(1); +} + +void showtune(struct tune* t) +/* print abc structure to the screen */ +{ + char* atitle; + int notesdone; + struct voice* thisvoice; + + notesdone = 0; + atitle = firstitem(&t->title); + while (atitle != NULL) { + printf("TITLE: %s\n", atitle); + atitle = nextitem(&t->title); + }; + if (t->composer != NULL) { + printf("COMPOSER: %s\n", t->composer); /* [SDG] 2020-06-03 */ + }; + if (t->origin != NULL) { + printf("ORIGIN: %s\n", t->origin); + }; + if (t->parts != NULL) { + printf("PARTS: %s\n", t->parts); + }; + thisvoice = firstitem(&t->voices); + while (thisvoice != NULL) { + thisvoice->place = thisvoice->first; + thisvoice = nextitem(&t->voices); + }; +/* + doneline = 1; + while(doneline) { +*/ + thisvoice = firstitem(&t->voices); + while (thisvoice != NULL) { + printf("-----------(voice %d)-------\n", thisvoice->voiceno); + showline(thisvoice); + thisvoice = nextitem(&t->voices); + }; +/* }; */ +} + diff -Nru abcmidi-20200706/doc/abc2abc.1 abcmidi-20200728/doc/abc2abc.1 --- abcmidi-20200706/doc/abc2abc.1 2020-01-22 18:18:20.000000000 +0000 +++ abcmidi-20200728/doc/abc2abc.1 2020-07-27 15:52:43.000000000 +0000 @@ -1,123 +1,123 @@ -.TH ABC2ABC 1 "07 June 2011" -.SH NAME -abc2abc \- a simple abc checker/re-formatter/transposer -.SH SYNOPSIS -\fBabc2abc\fP \fIfile\fP [ \fB-s\fP ] [ \fB-n\fP ] [ \fB-b\fP ] -[ \fB-r\fP ] [ \fB-e\fP ] [ \fB-t \fP\fIsemitones\fP ] [ \fB-nda\fP ] -[ \fB-u\fP ] [ \fB-d\fP ] [ \fB-v\fP ] [ \fB-V\fP\fIvoice number\fP] -[\fB-P\fP\fivoice number\fp] [\fB-nokeys\fP] -[ \fB-nokeyf\fP] [ \fB-usekey\fP\fI(sharps/flats)\fP] -[\fB-useclef\fP\fI(treble/clef)\fP] [ \fB-OCC\fP ] -.SH "DESCRIPTION" -.PP -.B abc2abc -is a simple abc checker/re-formatter/transposer. -It will check whether the \fIfile\fP given on the command line -obeys basic abc syntax. -.PP -If you want to check an abc tune, -it is recommended that you use -.B abc2midi -with the \fB-c\fP option. -This performs extra checks that -.B abc2abc -does not do. -.PP -The output of -.B abc2abc -goes to standard output. -Use redirection to save it to a file. -.SH OPTIONS -.TP -.B \-s -Rework spacing in the file (which affects how notes are beamed together -when the music is printed out). This option does not appear to be working -correctly. -.TP -.BI \-n " X" -Reformats the abc file with line breaks every \fIX\fP bars. -.TP -.B \-b -Don't do bar checking. -.TP -.B \-r -Don't do repeat checking. -.TP -.B \-e -Don't report errors. -.TP -.BI \-t " n" -Transpose tune by \fIn\fP semitones. This function will also -work with K: none or one of \-nokeys or \-nokeyf. -If a voice is assigned to channel 10 (drum channel) using a -%%MIDI channel 10 -command, then this voice is never transposed. - -.TP -.B \-nda -Convert double accidentals in guitar chord to another chord though -strictly not correct. -.TP -.B \-u -Update notation; the older notation \fB+ +\fP for chords is replaced by -\fB[]\fP and \fBs s\fP for slurs is replaced by \fB()\fP. -.TP -.B \-OCC -Accept the old notation for chord. Normally this is turned off, -since it conflicts with abc draft standard version 2.0 for -decorations (eg. +crescendo(+). -.TP -.B \-d -Re-notate the tune with all note lengths doubled. The unit length specified by the L: field -command is halved (e.g. L:1/8 to L:1/16). -.TP -.B \-v -Re-notate the tune with all note lengths halved. The unit length specified by the L: field -command is doubled (e.g. L:1/8 to L:1/4). -.TP -.B \-ver -Prints version number and exits. -.TP -.BI \-V " X[,Y...]" -For multivoiced abc files (i.e. contains V: field commands), only voices \fIX[,Y,...]\fP are copied. -.TP -.BI \-P " X,[,Y...]" -For multivoiced abc files (i.e. contains V: field commands), all voices except \fIX[,Y...]\fP remain the same. Voices X,Y... are modified according the other runtime parameters. -.TP -.BI \-X " n" -For a file containing many tunes, the X: reference numbers are renumbered sequentially -starting from number \fIn\fP. -.TP -.BI \-xref " n" -Only the tune with X: n is processed. -.TP -.B \-nokeys -No key signature will be assumed. Instead, sharps and naturals will -be placed wherever they are needed. -.TP -.B \-nokeyf -No key signature will be assumed. Instead, flats and naturals will -be placed wherever they are needed. -.TP -.B \-usekey " sf -This will force abc2abc to output the notes in the key signature -keys[sf] where sf specifies the number of flats (\-negative) or -sharps (+positive) in the key signature. It is a number between -\-5 and +5 inclusive. -.TP -.B \-useclef -This works with only the -t (transpose) and provided the abc -file does not already have a clef command in the K: field. It -does not support voices. -.PP -* Normally abc2abc will convert the deprecated notation for -decorations (eg. !ppp!) to the abc version 2.0 draft standard (eg. +ppp+). -If you do not wish to change to this standard include the \-OCC flag. - -.SH "SEE ALSO" -.IR abcmtex "(1), " abc2midi "(1), " midi2abc "(1), " mftext "(1)" -.SH AUTHOR -This manual page was written by Anselm Lingnau and is now supported by Seymour Shlien -for the GNU/Linux system. -.SH VERSION -This man page describes abc2abc version 2.04 from January 22 2020. +.TH ABC2ABC 1 "07 June 2011" +.SH NAME +abc2abc \- a simple abc checker/re-formatter/transposer +.SH SYNOPSIS +\fBabc2abc\fP \fIfile\fP [ \fB-s\fP ] [ \fB-n\fP ] [ \fB-b\fP ] +[ \fB-r\fP ] [ \fB-e\fP ] [ \fB-t \fP\fIsemitones\fP ] [ \fB-nda\fP ] +[ \fB-u\fP ] [ \fB-d\fP ] [ \fB-v\fP ] [ \fB-V\fP\fIvoice number\fP] +[\fB-P\fP\fivoice number\fp] [\fB-nokeys\fP] +[ \fB-nokeyf\fP] [ \fB-usekey\fP\fI(sharps/flats)\fP] +[\fB-useclef\fP\fI(treble/clef)\fP] [ \fB-OCC\fP ] +.SH "DESCRIPTION" +.PP +.B abc2abc +is a simple abc checker/re-formatter/transposer. +It will check whether the \fIfile\fP given on the command line +obeys basic abc syntax. +.PP +If you want to check an abc tune, +it is recommended that you use +.B abc2midi +with the \fB-c\fP option. +This performs extra checks that +.B abc2abc +does not do. +.PP +The output of +.B abc2abc +goes to standard output. +Use redirection to save it to a file. +.SH OPTIONS +.TP +.B \-s +Rework spacing in the file (which affects how notes are beamed together +when the music is printed out). This option does not appear to be working +correctly. +.TP +.BI \-n " X" +Reformats the abc file with line breaks every \fIX\fP bars. +.TP +.B \-b +Don't do bar checking. +.TP +.B \-r +Don't do repeat checking. +.TP +.B \-e +Don't report errors. +.TP +.BI \-t " n" +Transpose tune by \fIn\fP semitones. This function will also +work with K: none or one of \-nokeys or \-nokeyf. +If a voice is assigned to channel 10 (drum channel) using a +%%MIDI channel 10 +command, then this voice is never transposed. + +.TP +.B \-nda +Convert double accidentals in guitar chord to another chord though +strictly not correct. +.TP +.B \-u +Update notation; the older notation \fB+ +\fP for chords is replaced by +\fB[]\fP and \fBs s\fP for slurs is replaced by \fB()\fP. +.TP +.B \-OCC +Accept the old notation for chord. Normally this is turned off, +since it conflicts with abc draft standard version 2.0 for +decorations (eg. +crescendo(+). +.TP +.B \-d +Re-notate the tune with all note lengths doubled. The unit length specified by the L: field +command is halved (e.g. L:1/8 to L:1/16). +.TP +.B \-v +Re-notate the tune with all note lengths halved. The unit length specified by the L: field +command is doubled (e.g. L:1/8 to L:1/4). +.TP +.B \-ver +Prints version number and exits. +.TP +.BI \-V " X[,Y...]" +For multivoiced abc files (i.e. contains V: field commands), only voices \fIX[,Y,...]\fP are copied. +.TP +.BI \-P " X,[,Y...]" +For multivoiced abc files (i.e. contains V: field commands), all voices except \fIX[,Y...]\fP remain the same. Voices X,Y... are modified according the other runtime parameters. +.TP +.BI \-X " n" +For a file containing many tunes, the X: reference numbers are renumbered sequentially +starting from number \fIn\fP. +.TP +.BI \-xref " n" +Only the tune with X: n is processed. +.TP +.B \-nokeys +No key signature will be assumed. Instead, sharps and naturals will +be placed wherever they are needed. +.TP +.B \-nokeyf +No key signature will be assumed. Instead, flats and naturals will +be placed wherever they are needed. +.TP +.B \-usekey " sf +This will force abc2abc to output the notes in the key signature +keys[sf] where sf specifies the number of flats (\-negative) or +sharps (+positive) in the key signature. It is a number between +\-5 and +5 inclusive. +.TP +.B \-useclef +This works with only the -t (transpose) and provided the abc +file does not already have a clef command in the K: field. It +does not support voices. +.PP +* Normally abc2abc will convert the deprecated notation for +decorations (eg. !ppp!) to the abc version 2.0 draft standard (eg. +ppp+). +If you do not wish to change to this standard include the \-OCC flag. + +.SH "SEE ALSO" +.IR abcmtex "(1), " abc2midi "(1), " midi2abc "(1), " mftext "(1)" +.SH AUTHOR +This manual page was written by Anselm Lingnau and is now supported by Seymour Shlien +for the GNU/Linux system. +.SH VERSION +This man page describes abc2abc version 2.04 from January 22 2020. diff -Nru abcmidi-20200706/doc/abc2midi.1 abcmidi-20200728/doc/abc2midi.1 --- abcmidi-20200706/doc/abc2midi.1 2019-12-09 12:33:31.000000000 +0000 +++ abcmidi-20200728/doc/abc2midi.1 2020-07-27 15:52:43.000000000 +0000 @@ -1,568 +1,568 @@ -.TH ABC2MIDI 1 "June 2017" -.SH NAME -\fBabc2midi\fP \- converts abc file to MIDI file(s) -.SH SYNOPSIS -abc2midi \fIinfile\fP [\fIrefnum\fP] [\-c] [\-v] [\-ver] [\-t] [\-n limit] [\-CS] [\-quiet] [\-silent] [\-Q tempo] [\-NFNP] [\-NFER] [\-NGRA] [\-NGUI] [\-STFW] [\-OCC] [\-NCOM] [\-HARP] [\-BF] [\-TT] [\-o outfile] \-CSM [filename] -.SH DESCRIPTION - The default action is to write a MIDI file for each abc tune - with the filename N.mid, where is the filestem - of the abc file and N is the tune reference number. If the \-o - option is used, only one file is written. This is the tune - specified by the reference number or, if no reference number - is given, the first tune in the file. -.SH OPTIONS -.TP -.B \fIrefnum\fP -process the tune with reference number \fIrefnum\fP -.TP -.B -c -selects checking only -.TP -.B -v n -selects verbose option where n is the level (optional) -.TP -.B -ver -prints version number and exits -.TP -.B -t -selects filenames derived from tune titles -.TP -.B -CS -use 2:1 instead of 3:1 for broken rhythms -.TP -.B -quiet -Suppresses some common warnings. -.TP -.B -silent -Suppresses other messages. -.TP -.B -n \fI X\fP -limits the length of the file name stem to X characters -.TP -.B -Q \fI tempo\fP -sets the default tempo in quarter notes per minute if it was not -specified in the abc header. -.TP -.B -NFNP -Ignore any dynamic indications !f! !ff! etc. -.TP -.B -NFER -Ignore any fermata indications (eg H or !fermata!). -.TP -.B -NGRA -Ignore any grace notes. -.TP -.B -NGUI -Ignore any guitar chords enclosed in double quotes. -.TP -.B -STFW -Place lyric text in separate MIDI tracks. -.TP -.B -NCOM -Suppress some comments in the output MIDI file. -.TP -.B -OCC -Accept old chord convention (eg +D2G2+ instead of [DG]2). -.TP -.B -BF -BarFly mode: invokes a stress model if possible. -.TP -.B -HARP -Roll ornaments=roll are generated for the harpist (same pitch) -.TP -.B -TT -Changes the tuning from A = 440 Hz. -.TP -.B -o \fIoutfile\fP -write output to \fIoutfile\fP -.TP -.B -CSM \fIinfile\fP -load a set of custom stress modes from a file -.SH FEATURES -.PP -* Broken rhythms (>, <), chords, n-tuples, slurring, ties, staccatto notes, -repeats, in-tune tempo/length/time signature changes are all supported. -.PP -* R:hornpipe or r:hornpipe is recognized and note timings are adjusted to -give a broken rhythm (ab is converted to a>b). -.PP -* Most errors in the abc input will generate a suitable error message in -the output and the converter keeps going. -.PP -* Comments and text fields in the abc source are converted to text events -in the MIDI output -.PP -* If guitar chords are present, they are used to generate an accompaniment -in the MIDI output. -.PP -* If there are mis-matched repeat signs in the abc, the program attempts to -fix them. However, it will not attempt this if a multi-part tune -description has been used or if multiple voices are in use. -.PP -* Karaoke MIDI files can be generated by using the w: field to include -lyrics. -.PP -* Nonnumeric voice id's, eg. V: soprano, as proposed for the new -abc standard is accepted. -.PP -* Invisible rests specified by x are treated the same way as -normal rests (z). -.PP -* Decorations may be indicated using either the deprecated -notation (eg. !fermata!) or the standard version 2.0 notation -(eg. +fermata+). -.PP -.SH LIMITATIONS -* No field is inherited from above the X: field of the tune. - - -.SH "ABC SYNTAX EXTENSIONS" -* There are some extensions to the abc syntax of the form -.PP -%%MIDI channel n -.PP -These control channel and program selection, transposing and various -other features of abc2midi. -.PP -Each of these should appear on a line by itself. All of them are allowed -within the abc tune body. By using these in combination with the part -notation, one can, for example, play a part transposed or in a different key. -.PP -The idea behind this syntax is that other programs will treat it as a -comment and ignore it. -.PP -%%MIDI channel n -.PP -selects melody channel n (in the range 1-16). -.PP -%%MIDI program [c] n -.PP -selects program n (in the range 0-127) on channel c. If c is not given, the -program is selected on the current melody channel. Most modern tone -generators follow the General MIDI standard which defines the instrument -type for each program number. -.PP -%%MIDI beat a b c n -.PP -controls the way note velocities are selected. The first note in a bar has -velocity a. Other "strong" notes have velocity b and all the rest have velocity -c. a, b and c must be in the range 0-127. The parameter n determines which -notes are "strong". If the time signature is x/y, then each note is given -a position number k = 0, 1, 2 .. x-1 within each bar. Note that the units for -n are not the unit note length. If k is a multiple of n, then the note is -"strong". The volume specifiers !ppp! to !fff! are equivalent to the -following : -.P -!ppp! = %%MIDI beat 30 20 10 1 -.br -!pp! = %%MIDI beat 45 35 20 1 -.br -!p! = %%MIDI beat 60 50 35 1 -.br -!mp! = %%MIDI beat 75 65 50 1 -.br -!mf! = %%MIDI beat 90 80 65 1 -.br -!f! = %%MIDI beat 105 95 80 1 -.br -!ff! = %%MIDI beat 120 110 95 1 -.br -!fff! = %%MIDI beat 127 125 110 1 - -.PP -%%MIDI beatmod n -.PP -Increments by n (or decrements if n is negative) the velocities a, b and -c described above. The instructions !crescendo(! and !crescendo)! -are equivalent to inserting a %%MIDI beatmod 15 wherever they -occur. (Alternatively you can use !<(! and !<)!.) Similarly the -instructions !diminuendo(! and !diminuendo)! are equivalent -to %%MIDI beatmod \-15. - -.PP -%%MIDI deltaloudness n -.PP -where n is a small positive number. By default the crescendo and -diminuendo instructions modify the beat variables a, b, and c by -15 velocity units. This instruction allows you to set this default -to value n. - -.PP -%%MIDI nobeataccents -.PP -For instruments such as church organ that have no greatly emphasized beat notes, -using this will force use of the 'b' velocity (see %%MIDI beat) -for every note irrespective of position in the bar. This allows dynamics -(ff, f, etc) to be used in the normal way. -.PP -%%MIDI beataccents -.PP -Revert to emphasizing notes the the usual way. (default) - -.PP -%%MIDI beatstring -.PP -This provides an alternative way of specifying where the strong and weak -stresses fall within a bar. 'f' means velocity a (normally strong), 'm' -means velocity b (medium velocity) and 'p' means velocity c (soft velocity). -For example, if the time signature is 7/8 with stresses on the first, fourth -and sixth notes in the bar, we could use the following -.PP -%%MIDI beatstring fppmpmp -.PP -%%MIDI transpose n -.PP -transposes the output by the specified number of semitones. n may be -positive or negative. -.PP -%%MIDI rtranspose n -.PP -Relative transpose by the specified number of semitones. i.e. -%%MIDI transpose a followed by %%MIDI rtranspose b results in a -transposition of a+b. %%MIDI transpose b will result in a transposition -of b semitones, regardless of any previous transposition. -.PP -%%MIDI c n -.PP -specifies the MIDI pitch which corresponds to c. The default is 60. This -number should normally be a multiple of 12. -.PP -%%MIDI grace a/b -.PP -sets the fraction of the next note that grace notes will take up. a -must be between 1 and b-1. The grace notes may not sound natural -in this approach, since the length of the individual grace notes -vary with the complexity of the grace and the length of the -following note. A different approach (which is now the default) -assumes that the grace notes always have a fixed duration. -To use the other approach you would specify, - -%%MIDI gracedivider b - -where b specifies how many parts to divide the unit length -specified by the L: field command. For example if b = 4 and -L: = 1/8, then every grace note would be 1/(8*4) or a 32nd -note. Time would be stolen from the note to which the grace -notes are applied. If that note is not long enough to handle -the grace then the grace notes would be assigned 0 duration. - - - -.PP -%%MIDI chordname name n1 n2 n3 n4 n5 n6 -.PP -Defines how to play a guitar chord called "name". n1 is usually 0 and -n2, n3 to n6 give the pitches of the other notes in semitones relative -to the root note. There may be fewer than 6 notes in the chord, but not -more.If "name" is already defined, this command re-defines it. Unlike -most other commands, chordname definitions stay in effect from where they -are defined to the end of the abc file. The following illustrates how -m, 7, m7 and maj7 could be set up if they were not already defined. -.PP -%%MIDI chordname m 0 3 7 -.br -%%MIDI chordname 7 0 4 7 10 -.br -%%MIDI chordname m7 0 3 7 10 -.br -%%MIDI chordname maj7 0 4 7 11 -.PP -%%MIDI gchord string -.PP -sets up how guitar chords are generated. The string is a sequence made of -of z's, c's f's and b's for rests, chords, fundamental and fundamental -plus chord notes respectively. This specifies how each bar is to be played. -An optional length is allowed to follow the z's, c's f's and b's e.g. czf2zf3. -If the abc contains guitar chords, then abc2midi automatically adds chords and -fundamentals after encountering the first guitar chord. It keeps using that -chord until a new chord is specified in the abc. Whenever the M: field is -encountered in the abc, an appropriate default string is set : -.P -For 2/4 or 4/4 time default is equivalent to : -%%MIDI gchord fzczfzcz -.P -For 3/4 time default is equivalent to : -%%MIDI gchord fzczcz -.P -For 6/8 time default is equivalent to : -%%MIDI gchord fzcfzc -.P -For 9/8 time default is equivalent to : -%%MIDI gchord fzcfzcfzc -.P - -The gchord command has been extended to allow you to play -the individual notes comprising the guitar chord. This allows -you to play broken chords or arpeggios. The new codes g,h,i,j, -G,H,I,J reference the individual notes starting from the -lowest note of the chord (not necessarily the root in the -case of inversions). For example for the C major chord, g -refers to C, h refers to E and i refers to G. For a gchord -command such as, -.P -%%MIDI gchord ghih -.P -Abc2midi will arpeggiate the C major guitar chord to -CEGE. The upper case letters G,H,I, and J refer to -the same notes except they are transposed down one -octave. Note for the first inversion of the C major -chord (indicated by "C/E"), E would be the lowest -note so g would reference the note E. -.P -Like other gchord codes, you may append a numeral indicating -the duration of the note. The same rules apply as before. -You can use any combination of the gchord codes, -(fcbghijGHIJz). - - -.PP -%%MIDI chordprog n -.PP -Sets the MIDI instrument for the chords to be n. If the command -includes the string octave=n where n is a number between \-2 and 2 -inclusive, then this will shift the pitch of the instrument by n -octaves. For example %%MIDI chordprog 10 octave=1.) - -.PP -%%MIDI bassprog n -.PP -Sets the MIDI instrument for the bass notes to be n. If the command -includes the string octave=n where n is a number between \-2 and 2 -inclusive, then this will shift the pitch of the instrument by n -octaves. For example %%MIDI bassprog 10 octave=\-1.) -.PP -%%MIDI chordvol n -.PP -Sets the volume (velocity) of the chord notes at n. -.PP -%%MIDI bassvol n -.PP -Sets the volume (velocity) of the bass notes at n. There is no corresponding -melodyvol command since there are 3 velocity values for melody, set using the -beat command. -.PP -%%MIDI gchordon -.PP -Turns on guitar chords (they are turned on by default at the start of a -tune). -.PP -%%MIDI gchordoff -.PP -Turns off guitar chords. -.PP -%%MIDI droneon -.PP -Turns on a continuous drone (used in bagpipe music) consisting -of two notes. By default the notes are A, and A,, played -on a bassoon at a velocity of 80. This can be configured -by the %%MIDI drone command described below. -.PP -%%MIDI droneoff -.PP -Turns off the continuous drone. -.PP -%%MIDI drone n1 n2 n3 n4 n5 -.PP -Sets the drone parameters where n1 is the MIDI program, n2 and -n3 specify the MIDI pitches of the two notes in the chord, and n4 -and n5 specify the MIDI velocities of the two notes. -If you do not set these parameters they are by default -70 45 33 80 80. A value of zero or less indicates that -the setting of this parameter should be left as it is. -.PP -%%MIDI drum string [drum programs] [drum velocities] -.PP -This sets up a drum pattern. The string determines when there is a drum beat -and the drum program values determine what each drum strike sounds like. -.PP -e.g. %%MIDI drum d2zdd 35 38 38 100 50 50 -.PP -The string may contain 'd' for a drum strike or 'z' for a rest. By default -a voice starts with no drum pattern and '%%MIDI drumon' is -needed to enable the drumming. The drum pattern is repeated during -each bar until '%%MIDI drumoff' is encountered. The %%MIDI drum -command may be used within a tune to change the drum pattern. -This command places the drum sounds on channel 10 and -assumes your tone generator complies with the General Midi standard - if -it does not, then you may hear tones instead of drum sounds. -.PP -In both the gchord and drum commands, the standard note length of -a single note f,c,z or d is not set by the L: command. Instead it -is adjusted so that the entire gchord string or drum string fits -exactly into one bar. In other words the duration of each note -is divided by the total duration of the string. This means that, -for example, the drum string "dd" is equivalent to drum string "d4d4". -You cannot currently specify fractions directly (eg. C3/2) -as done in the body of the music, but it is still possible to express -complex rhythms. For example, to indicate a rhythm such as -(3ddd d/d/d/d, you would write the string "d4d4d4d3d3d3d3". -.PP -%%MIDI drumbars n -.PP -The %%MIDI drum line can sound quite monotonous if it is repeated -each bar. To circumvent this problem a new MIDI command -%%MIDI drumbars n -where n is a small number will spread out the drum string over -n consecutive bars. By default drumbars is set to 1 maintaining -compatibility with existing abc files. You should take -care that the drumstring is evenly divisible between the -drumbar bars. Also the time signature should not change -between bars in a drumbar unit. (Sample abc file in doc/CHANGES -June 24 2008.) -.PP -%%MIDI gchordbars n -.PP -This command spreads the gchord string over n consecutive bars -just like drumbars (above). (A sample is found in doc/CHANGES -March 17 2009.) - - - - -.PP -With version 1.54 Dec 4 2004 of abc2midi, notes in chords -(eg. [FAc]) are not played in the same instant but offsetted -and shortened by 10 MIDI time units. Thus the first note -in the chord (eg. F) is played for the full indicated time, -the second note (eg. A) starts 10 MIDI units later and is shortened -by the same amount and the third note starts another 10 MIDI -units later and is shortened by another 10 units. This introduces -an "expressivo" option and avoids the heavy attack. (This -does not apply to gchords or multivoiced chords.) The amount -of the delay and shortening may be configured by the MIDI command - -.PP -%%MIDI chordattack n - -.PP -where n is a small number. If n is zero, then abc2midi should -behave as in earlier versions. The delay n is in MIDI time units -where there are 480 units in a quarter note beat. The program -may not run correctly if n is too large and there are short -chords. - -.PP -%%MIDI randomchordattack n -.PP -Like above except that the delay is a random variable uniformly -distributed between 0 and n-1. - -.PP -%%MIDI trim x/y -.PP -where x and y are two numbers. This command controls the articulation -of notes and chords by placing silent gaps between the notes. The length -of these gaps is determined by x/y and the unit length specified by the L: -command. These gaps are produced by shortening the notes by the same amount. -If the note is already shorter than the specified gap, then the gap -is set to half the length of the note. The fraction x/y indicates -a note duration in the same manner as specified in the abc file. -The actual duration is based on the unit length specified by the -L: field command. It is recommended that x/y be a fraction close -to zero. Note trimming is disabled inside slurs as specified by -parentheses. You can turn off all note trimming by setting x to 0, -eg 0/1. By default, note trimming is turned off at the beginning -of a tune or voice command. - -.PP -%%MIDI expand x/y -.PP -where x and y are two numbers defining a fraction less than 1. -This command controls the articulation of notes and chords in the -reverse manner. The notes are lengthened by this fraction so they -overlap the start of the next note. - - -.PP -%%MIDI drummap note midipitch -.PP -Please see abcguide.txt. -.PP -%%MIDI ptstress filename -.PP -This command loads file filename into abc2midi which contains -the Phil Taylor stress parameters and puts abc2midi in the mode -where it applies these stress parameters on every note. This -model runs in opposition to the standard beat model, so the -MIDI beat, beatstring, beatmod commands become ineffectual. -This also means that the dynamic indications !f! !pp! etc. -do not work any more. -.PP -There are two different implementations of the stress model. -Model 1 modifies the note articulation and takes -control of the MIDI trim parameters too. To revert back to -the standard model, put the command %%MIDI beataccents. -Model 2 modifies both the onset and ending of each note -allowing a musical beat to expand or contract in time. However, -the length of a musical measure should be preserved. Note -if you using model 2, which the current default, you must -include \-BF as one of the runtime parameters of abc2midi. -.PP -The model divides a bar into equal segments. For each segment, -a loudness or MIDI velocity is specified and a duration multiplier -is specified. If a note falls into a specific segment, it assumes -the velocity of that segment and its duration is modified -accordingly. If the note overlaps more than one segment, then -the note assumes the average of those segment values. -.PP -The input file specifies the number of segments and the loudness -and duration multipliers for each segment. The file has the -following format. The first value is the number of segments and each line -specifies the velocity and duration multiplier of the specific -segment. The velocity is limited to 127 and the duration is a -decimal number. The note durations is modified by varying the -gap between notes, so it is not possible to extend a note. This -preserves the regular tempo of the music. The program scales, -the note duration indications by dividing it by the maximum -value which here is 1.4. - -.PP -%%MIDI stressmodel n -.PP - -where n is either 1 or 2, selects the stress model implementation. - -.PP - other %%MIDI commands such as bendvelocity, bendstring, -controlstring have been introduced recently and are described -in the file abcguide.txt. - - - -.SH "COMPATIBILITY WITH DRAFT STANDARD 2.0" - -.PP -The proposed standard introduces a new copyright field -using the syntax - -.PP -%%abc-copyright (c) Copyright John Smith 2003 - -.PP -Abc2midi now inserts this in the MIDI file in the form of a -metatext copyright tag. Changes were made to the event_specific -function in store.c to process the copyright information. It -is also copied into the Karaoke track (if it is created) as -as @T field. - -.PP - - -.SH SEE ALSO -abc2ps(1), midi2abc(1), yaps(1). -.SH AUTHOR -James Allwright -.SH SUPPORTED - by Seymour Shlien -.SH VERSION -This man page describes abc2midi version 2.27 June 25 2006. -.SH COPYRIGHT -Copyright 1999 James Allwright -.PP -abc2midi is supplied "as is" without any warranty. It -is free software and can be used, copied, modified and -distributed without fee under the terms of the GNU General -Public License. -.PP -More complete documentation may be found in abcguide.txt -which comes with the abcMIDI distribution. +.TH ABC2MIDI 1 "June 2017" +.SH NAME +\fBabc2midi\fP \- converts abc file to MIDI file(s) +.SH SYNOPSIS +abc2midi \fIinfile\fP [\fIrefnum\fP] [\-c] [\-v] [\-ver] [\-t] [\-n limit] [\-CS] [\-quiet] [\-silent] [\-Q tempo] [\-NFNP] [\-NFER] [\-NGRA] [\-NGUI] [\-STFW] [\-OCC] [\-NCOM] [\-HARP] [\-BF] [\-TT] [\-o outfile] \-CSM [filename] +.SH DESCRIPTION + The default action is to write a MIDI file for each abc tune + with the filename N.mid, where is the filestem + of the abc file and N is the tune reference number. If the \-o + option is used, only one file is written. This is the tune + specified by the reference number or, if no reference number + is given, the first tune in the file. +.SH OPTIONS +.TP +.B \fIrefnum\fP +process the tune with reference number \fIrefnum\fP +.TP +.B -c +selects checking only +.TP +.B -v n +selects verbose option where n is the level (optional) +.TP +.B -ver +prints version number and exits +.TP +.B -t +selects filenames derived from tune titles +.TP +.B -CS +use 2:1 instead of 3:1 for broken rhythms +.TP +.B -quiet +Suppresses some common warnings. +.TP +.B -silent +Suppresses other messages. +.TP +.B -n \fI X\fP +limits the length of the file name stem to X characters +.TP +.B -Q \fI tempo\fP +sets the default tempo in quarter notes per minute if it was not +specified in the abc header. +.TP +.B -NFNP +Ignore any dynamic indications !f! !ff! etc. +.TP +.B -NFER +Ignore any fermata indications (eg H or !fermata!). +.TP +.B -NGRA +Ignore any grace notes. +.TP +.B -NGUI +Ignore any guitar chords enclosed in double quotes. +.TP +.B -STFW +Place lyric text in separate MIDI tracks. +.TP +.B -NCOM +Suppress some comments in the output MIDI file. +.TP +.B -OCC +Accept old chord convention (eg +D2G2+ instead of [DG]2). +.TP +.B -BF +BarFly mode: invokes a stress model if possible. +.TP +.B -HARP +Roll ornaments=roll are generated for the harpist (same pitch) +.TP +.B -TT +Changes the tuning from A = 440 Hz. +.TP +.B -o \fIoutfile\fP +write output to \fIoutfile\fP +.TP +.B -CSM \fIinfile\fP +load a set of custom stress modes from a file +.SH FEATURES +.PP +* Broken rhythms (>, <), chords, n-tuples, slurring, ties, staccatto notes, +repeats, in-tune tempo/length/time signature changes are all supported. +.PP +* R:hornpipe or r:hornpipe is recognized and note timings are adjusted to +give a broken rhythm (ab is converted to a>b). +.PP +* Most errors in the abc input will generate a suitable error message in +the output and the converter keeps going. +.PP +* Comments and text fields in the abc source are converted to text events +in the MIDI output +.PP +* If guitar chords are present, they are used to generate an accompaniment +in the MIDI output. +.PP +* If there are mis-matched repeat signs in the abc, the program attempts to +fix them. However, it will not attempt this if a multi-part tune +description has been used or if multiple voices are in use. +.PP +* Karaoke MIDI files can be generated by using the w: field to include +lyrics. +.PP +* Nonnumeric voice id's, eg. V: soprano, as proposed for the new +abc standard is accepted. +.PP +* Invisible rests specified by x are treated the same way as +normal rests (z). +.PP +* Decorations may be indicated using either the deprecated +notation (eg. !fermata!) or the standard version 2.0 notation +(eg. +fermata+). +.PP +.SH LIMITATIONS +* No field is inherited from above the X: field of the tune. + + +.SH "ABC SYNTAX EXTENSIONS" +* There are some extensions to the abc syntax of the form +.PP +%%MIDI channel n +.PP +These control channel and program selection, transposing and various +other features of abc2midi. +.PP +Each of these should appear on a line by itself. All of them are allowed +within the abc tune body. By using these in combination with the part +notation, one can, for example, play a part transposed or in a different key. +.PP +The idea behind this syntax is that other programs will treat it as a +comment and ignore it. +.PP +%%MIDI channel n +.PP +selects melody channel n (in the range 1-16). +.PP +%%MIDI program [c] n +.PP +selects program n (in the range 0-127) on channel c. If c is not given, the +program is selected on the current melody channel. Most modern tone +generators follow the General MIDI standard which defines the instrument +type for each program number. +.PP +%%MIDI beat a b c n +.PP +controls the way note velocities are selected. The first note in a bar has +velocity a. Other "strong" notes have velocity b and all the rest have velocity +c. a, b and c must be in the range 0-127. The parameter n determines which +notes are "strong". If the time signature is x/y, then each note is given +a position number k = 0, 1, 2 .. x-1 within each bar. Note that the units for +n are not the unit note length. If k is a multiple of n, then the note is +"strong". The volume specifiers !ppp! to !fff! are equivalent to the +following : +.P +!ppp! = %%MIDI beat 30 20 10 1 +.br +!pp! = %%MIDI beat 45 35 20 1 +.br +!p! = %%MIDI beat 60 50 35 1 +.br +!mp! = %%MIDI beat 75 65 50 1 +.br +!mf! = %%MIDI beat 90 80 65 1 +.br +!f! = %%MIDI beat 105 95 80 1 +.br +!ff! = %%MIDI beat 120 110 95 1 +.br +!fff! = %%MIDI beat 127 125 110 1 + +.PP +%%MIDI beatmod n +.PP +Increments by n (or decrements if n is negative) the velocities a, b and +c described above. The instructions !crescendo(! and !crescendo)! +are equivalent to inserting a %%MIDI beatmod 15 wherever they +occur. (Alternatively you can use !<(! and !<)!.) Similarly the +instructions !diminuendo(! and !diminuendo)! are equivalent +to %%MIDI beatmod \-15. + +.PP +%%MIDI deltaloudness n +.PP +where n is a small positive number. By default the crescendo and +diminuendo instructions modify the beat variables a, b, and c by +15 velocity units. This instruction allows you to set this default +to value n. + +.PP +%%MIDI nobeataccents +.PP +For instruments such as church organ that have no greatly emphasized beat notes, +using this will force use of the 'b' velocity (see %%MIDI beat) +for every note irrespective of position in the bar. This allows dynamics +(ff, f, etc) to be used in the normal way. +.PP +%%MIDI beataccents +.PP +Revert to emphasizing notes the the usual way. (default) + +.PP +%%MIDI beatstring +.PP +This provides an alternative way of specifying where the strong and weak +stresses fall within a bar. 'f' means velocity a (normally strong), 'm' +means velocity b (medium velocity) and 'p' means velocity c (soft velocity). +For example, if the time signature is 7/8 with stresses on the first, fourth +and sixth notes in the bar, we could use the following +.PP +%%MIDI beatstring fppmpmp +.PP +%%MIDI transpose n +.PP +transposes the output by the specified number of semitones. n may be +positive or negative. +.PP +%%MIDI rtranspose n +.PP +Relative transpose by the specified number of semitones. i.e. +%%MIDI transpose a followed by %%MIDI rtranspose b results in a +transposition of a+b. %%MIDI transpose b will result in a transposition +of b semitones, regardless of any previous transposition. +.PP +%%MIDI c n +.PP +specifies the MIDI pitch which corresponds to c. The default is 60. This +number should normally be a multiple of 12. +.PP +%%MIDI grace a/b +.PP +sets the fraction of the next note that grace notes will take up. a +must be between 1 and b-1. The grace notes may not sound natural +in this approach, since the length of the individual grace notes +vary with the complexity of the grace and the length of the +following note. A different approach (which is now the default) +assumes that the grace notes always have a fixed duration. +To use the other approach you would specify, + +%%MIDI gracedivider b + +where b specifies how many parts to divide the unit length +specified by the L: field command. For example if b = 4 and +L: = 1/8, then every grace note would be 1/(8*4) or a 32nd +note. Time would be stolen from the note to which the grace +notes are applied. If that note is not long enough to handle +the grace then the grace notes would be assigned 0 duration. + + + +.PP +%%MIDI chordname name n1 n2 n3 n4 n5 n6 +.PP +Defines how to play a guitar chord called "name". n1 is usually 0 and +n2, n3 to n6 give the pitches of the other notes in semitones relative +to the root note. There may be fewer than 6 notes in the chord, but not +more.If "name" is already defined, this command re-defines it. Unlike +most other commands, chordname definitions stay in effect from where they +are defined to the end of the abc file. The following illustrates how +m, 7, m7 and maj7 could be set up if they were not already defined. +.PP +%%MIDI chordname m 0 3 7 +.br +%%MIDI chordname 7 0 4 7 10 +.br +%%MIDI chordname m7 0 3 7 10 +.br +%%MIDI chordname maj7 0 4 7 11 +.PP +%%MIDI gchord string +.PP +sets up how guitar chords are generated. The string is a sequence made of +of z's, c's f's and b's for rests, chords, fundamental and fundamental +plus chord notes respectively. This specifies how each bar is to be played. +An optional length is allowed to follow the z's, c's f's and b's e.g. czf2zf3. +If the abc contains guitar chords, then abc2midi automatically adds chords and +fundamentals after encountering the first guitar chord. It keeps using that +chord until a new chord is specified in the abc. Whenever the M: field is +encountered in the abc, an appropriate default string is set : +.P +For 2/4 or 4/4 time default is equivalent to : +%%MIDI gchord fzczfzcz +.P +For 3/4 time default is equivalent to : +%%MIDI gchord fzczcz +.P +For 6/8 time default is equivalent to : +%%MIDI gchord fzcfzc +.P +For 9/8 time default is equivalent to : +%%MIDI gchord fzcfzcfzc +.P + +The gchord command has been extended to allow you to play +the individual notes comprising the guitar chord. This allows +you to play broken chords or arpeggios. The new codes g,h,i,j, +G,H,I,J reference the individual notes starting from the +lowest note of the chord (not necessarily the root in the +case of inversions). For example for the C major chord, g +refers to C, h refers to E and i refers to G. For a gchord +command such as, +.P +%%MIDI gchord ghih +.P +Abc2midi will arpeggiate the C major guitar chord to +CEGE. The upper case letters G,H,I, and J refer to +the same notes except they are transposed down one +octave. Note for the first inversion of the C major +chord (indicated by "C/E"), E would be the lowest +note so g would reference the note E. +.P +Like other gchord codes, you may append a numeral indicating +the duration of the note. The same rules apply as before. +You can use any combination of the gchord codes, +(fcbghijGHIJz). + + +.PP +%%MIDI chordprog n +.PP +Sets the MIDI instrument for the chords to be n. If the command +includes the string octave=n where n is a number between \-2 and 2 +inclusive, then this will shift the pitch of the instrument by n +octaves. For example %%MIDI chordprog 10 octave=1.) + +.PP +%%MIDI bassprog n +.PP +Sets the MIDI instrument for the bass notes to be n. If the command +includes the string octave=n where n is a number between \-2 and 2 +inclusive, then this will shift the pitch of the instrument by n +octaves. For example %%MIDI bassprog 10 octave=\-1.) +.PP +%%MIDI chordvol n +.PP +Sets the volume (velocity) of the chord notes at n. +.PP +%%MIDI bassvol n +.PP +Sets the volume (velocity) of the bass notes at n. There is no corresponding +melodyvol command since there are 3 velocity values for melody, set using the +beat command. +.PP +%%MIDI gchordon +.PP +Turns on guitar chords (they are turned on by default at the start of a +tune). +.PP +%%MIDI gchordoff +.PP +Turns off guitar chords. +.PP +%%MIDI droneon +.PP +Turns on a continuous drone (used in bagpipe music) consisting +of two notes. By default the notes are A, and A,, played +on a bassoon at a velocity of 80. This can be configured +by the %%MIDI drone command described below. +.PP +%%MIDI droneoff +.PP +Turns off the continuous drone. +.PP +%%MIDI drone n1 n2 n3 n4 n5 +.PP +Sets the drone parameters where n1 is the MIDI program, n2 and +n3 specify the MIDI pitches of the two notes in the chord, and n4 +and n5 specify the MIDI velocities of the two notes. +If you do not set these parameters they are by default +70 45 33 80 80. A value of zero or less indicates that +the setting of this parameter should be left as it is. +.PP +%%MIDI drum string [drum programs] [drum velocities] +.PP +This sets up a drum pattern. The string determines when there is a drum beat +and the drum program values determine what each drum strike sounds like. +.PP +e.g. %%MIDI drum d2zdd 35 38 38 100 50 50 +.PP +The string may contain 'd' for a drum strike or 'z' for a rest. By default +a voice starts with no drum pattern and '%%MIDI drumon' is +needed to enable the drumming. The drum pattern is repeated during +each bar until '%%MIDI drumoff' is encountered. The %%MIDI drum +command may be used within a tune to change the drum pattern. +This command places the drum sounds on channel 10 and +assumes your tone generator complies with the General Midi standard - if +it does not, then you may hear tones instead of drum sounds. +.PP +In both the gchord and drum commands, the standard note length of +a single note f,c,z or d is not set by the L: command. Instead it +is adjusted so that the entire gchord string or drum string fits +exactly into one bar. In other words the duration of each note +is divided by the total duration of the string. This means that, +for example, the drum string "dd" is equivalent to drum string "d4d4". +You cannot currently specify fractions directly (eg. C3/2) +as done in the body of the music, but it is still possible to express +complex rhythms. For example, to indicate a rhythm such as +(3ddd d/d/d/d, you would write the string "d4d4d4d3d3d3d3". +.PP +%%MIDI drumbars n +.PP +The %%MIDI drum line can sound quite monotonous if it is repeated +each bar. To circumvent this problem a new MIDI command +%%MIDI drumbars n +where n is a small number will spread out the drum string over +n consecutive bars. By default drumbars is set to 1 maintaining +compatibility with existing abc files. You should take +care that the drumstring is evenly divisible between the +drumbar bars. Also the time signature should not change +between bars in a drumbar unit. (Sample abc file in doc/CHANGES +June 24 2008.) +.PP +%%MIDI gchordbars n +.PP +This command spreads the gchord string over n consecutive bars +just like drumbars (above). (A sample is found in doc/CHANGES +March 17 2009.) + + + + +.PP +With version 1.54 Dec 4 2004 of abc2midi, notes in chords +(eg. [FAc]) are not played in the same instant but offsetted +and shortened by 10 MIDI time units. Thus the first note +in the chord (eg. F) is played for the full indicated time, +the second note (eg. A) starts 10 MIDI units later and is shortened +by the same amount and the third note starts another 10 MIDI +units later and is shortened by another 10 units. This introduces +an "expressivo" option and avoids the heavy attack. (This +does not apply to gchords or multivoiced chords.) The amount +of the delay and shortening may be configured by the MIDI command + +.PP +%%MIDI chordattack n + +.PP +where n is a small number. If n is zero, then abc2midi should +behave as in earlier versions. The delay n is in MIDI time units +where there are 480 units in a quarter note beat. The program +may not run correctly if n is too large and there are short +chords. + +.PP +%%MIDI randomchordattack n +.PP +Like above except that the delay is a random variable uniformly +distributed between 0 and n-1. + +.PP +%%MIDI trim x/y +.PP +where x and y are two numbers. This command controls the articulation +of notes and chords by placing silent gaps between the notes. The length +of these gaps is determined by x/y and the unit length specified by the L: +command. These gaps are produced by shortening the notes by the same amount. +If the note is already shorter than the specified gap, then the gap +is set to half the length of the note. The fraction x/y indicates +a note duration in the same manner as specified in the abc file. +The actual duration is based on the unit length specified by the +L: field command. It is recommended that x/y be a fraction close +to zero. Note trimming is disabled inside slurs as specified by +parentheses. You can turn off all note trimming by setting x to 0, +eg 0/1. By default, note trimming is turned off at the beginning +of a tune or voice command. + +.PP +%%MIDI expand x/y +.PP +where x and y are two numbers defining a fraction less than 1. +This command controls the articulation of notes and chords in the +reverse manner. The notes are lengthened by this fraction so they +overlap the start of the next note. + + +.PP +%%MIDI drummap note midipitch +.PP +Please see abcguide.txt. +.PP +%%MIDI ptstress filename +.PP +This command loads file filename into abc2midi which contains +the Phil Taylor stress parameters and puts abc2midi in the mode +where it applies these stress parameters on every note. This +model runs in opposition to the standard beat model, so the +MIDI beat, beatstring, beatmod commands become ineffectual. +This also means that the dynamic indications !f! !pp! etc. +do not work any more. +.PP +There are two different implementations of the stress model. +Model 1 modifies the note articulation and takes +control of the MIDI trim parameters too. To revert back to +the standard model, put the command %%MIDI beataccents. +Model 2 modifies both the onset and ending of each note +allowing a musical beat to expand or contract in time. However, +the length of a musical measure should be preserved. Note +if you using model 2, which the current default, you must +include \-BF as one of the runtime parameters of abc2midi. +.PP +The model divides a bar into equal segments. For each segment, +a loudness or MIDI velocity is specified and a duration multiplier +is specified. If a note falls into a specific segment, it assumes +the velocity of that segment and its duration is modified +accordingly. If the note overlaps more than one segment, then +the note assumes the average of those segment values. +.PP +The input file specifies the number of segments and the loudness +and duration multipliers for each segment. The file has the +following format. The first value is the number of segments and each line +specifies the velocity and duration multiplier of the specific +segment. The velocity is limited to 127 and the duration is a +decimal number. The note durations is modified by varying the +gap between notes, so it is not possible to extend a note. This +preserves the regular tempo of the music. The program scales, +the note duration indications by dividing it by the maximum +value which here is 1.4. + +.PP +%%MIDI stressmodel n +.PP + +where n is either 1 or 2, selects the stress model implementation. + +.PP + other %%MIDI commands such as bendvelocity, bendstring, +controlstring have been introduced recently and are described +in the file abcguide.txt. + + + +.SH "COMPATIBILITY WITH DRAFT STANDARD 2.0" + +.PP +The proposed standard introduces a new copyright field +using the syntax + +.PP +%%abc-copyright (c) Copyright John Smith 2003 + +.PP +Abc2midi now inserts this in the MIDI file in the form of a +metatext copyright tag. Changes were made to the event_specific +function in store.c to process the copyright information. It +is also copied into the Karaoke track (if it is created) as +as @T field. + +.PP + + +.SH SEE ALSO +abc2ps(1), midi2abc(1), yaps(1). +.SH AUTHOR +James Allwright +.SH SUPPORTED + by Seymour Shlien +.SH VERSION +This man page describes abc2midi version 2.27 June 25 2006. +.SH COPYRIGHT +Copyright 1999 James Allwright +.PP +abc2midi is supplied "as is" without any warranty. It +is free software and can be used, copied, modified and +distributed without fee under the terms of the GNU General +Public License. +.PP +More complete documentation may be found in abcguide.txt +which comes with the abcMIDI distribution. diff -Nru abcmidi-20200706/doc/abcguide.txt abcmidi-20200728/doc/abcguide.txt --- abcmidi-20200706/doc/abcguide.txt 2017-06-02 11:24:03.000000000 +0000 +++ abcmidi-20200728/doc/abcguide.txt 2020-07-27 15:52:43.000000000 +0000 @@ -1,1693 +1,1693 @@ -Guide to writing abc for abc2midi ---------------------------------- -Updated June 1 2017. %%MIDI program ranges from 0 to 127 not 1 to 128. - -The defining document for abc is the abc version 1.6 specification which can -be found at http://www.gre.ac.uk/~c.walshaw/abc2mtex/abc.txt . This document -is a description of abc as interpreted by abc2midi. - -An abc tune consists of a header followed by a body. Each line in the -header is a different field starting with a letter immediately followed -by : and then the text of the field. The body of the tune contains lines -of music, though it may also contain certain fields. The end of the tune is -marked by a blank line (so blank lines cannot appear within the tune header -or body). - -Comments are allowed in both the header and the body. A comment starts -with a % sign and continues to the end of the line. A comment may be on -a line of its own or at the end of a line of abc. - -The header should look something like this : - -X:1 -T:The Rose Tree -M:4/4 -L:1/8 -Q:1/4=120 -K:G - -X: is the reference number (each tune in a file should have a unique reference -number). T: is the title of the tune, M: is the time signature, L: is the -unit note length and K: is the key signature. Q: is the tempo field. In -the above example the tempo is given as 120 quarter-notes per minute. X: -must be the first field and K: must be the last field in the header. - -It is advisable to write the fields M:, L: and Q: in that order. The -reason for this is that M: can set up a unit note length which L: -overrides and the tempo field Q: can be written in other forms that -depend on unit note length. This order makes the meaning clear and -unambiguous. The original abc specification gives a rule for computing -a default value for the unit note length from the M: field. abc2midi -only uses this rule for choosing a unit note length in the header if -the L: field has been omitted. - - -The tempo is usually indicated with the Q: field command, -eg. Q:1/4=180 which is interpreted as 180 quarter beats per minute. -In compliance with the abc standard 2.0, you may also -indicate the tempo using directives such as "lento", -"allegro", "vivace" etc. - -The following table based on -http://www.music.vt.edu/musicdictionary/appendix/tempo/tempo1.html -was used to translate the indications: - -Larghissimo 1/4=40 Moderato 1/4=104 -Adagissimo 1/4=44 Allegretto 1/4=112 -Lentissimo 1/4=48 Allegro 1/4=120 -Largo 1/4=56 Vivace 1/4=168 -Adagio 1/4=59 Vivo 1/4=180 -Lento 1/4=62 Presto 1/4=192 -Larghetto 1/4=66 Allegrissimo 1/4=208 -Adagietto 1/4=76 Vivacissimo 1/4=220 -Andante 1/4=88 Prestissimo 1/4=240 -Andantino 1/4=96 - -Note: case is ignored (eg. ALLEGRO Allegro or allegro are treated -the same). The directive must be enclosed in double quotes. - -Reference: http://abc.sourceforge.net/standard/abc2-draft.html#Q:%20-%20tempo - -Examples: - -X:1 -T:tempo -M:2/4 -L:1/4 -K:G -Q: "Adagio" -CD|EF| -Q: "Adagio" 1/4=40 -GA|Bc| - -(In the second tempo command, the 1/4=40 overrides the default -1/4=59.) - - - -The symbols M:C and M:C| give common time (4/4) and cut time (2/2) -The symbol M:none omits the meter entirely (free meter). -It is also possible to specify a complex meter, e.g. M:(2+3+2)/8, to make explicit which beats should be accented. The parentheses around the numerator are optional. - -Other fields may also appear in the header. Common ones are C: composer -of the tune, D: discography, H: history of the tune, S: source, N: notes, -Z: transcription note, A: area from which the tune comes, B: book -and R: rhythm. - - -Key Signature: - -The K: is made up of a base note A-G possibly followed by b for flat or -# for sharp. This specifies a major key. A minor key can be specified -by adding an m while Mixolydian and Dorian modes can be specified by adding -Mix and Dor respectively e.g. - -K:Eb -K:EDor -K:EbDor - -The following table relates the number of sharps or flats you see on the -stave to what is in the K: field : - -7 sharps: C# A#m G#Mix D#Dor -6 sharps: F# D#m C#Mix G#Dor -5 sharps: B G#m F#Mix C#Dor -4 sharps: E C#m BMix F#Dor -3 sharps: A F#m EMix BDor -2 sharps: D Bm AMix EDor HP Hp -1 sharp : G Em DMix ADor -0 sharps: C Am GMix DDor -1 flat : F Dm CMix GDor -2 flats : Bb Gm FMix CDor -3 flats : Eb Cm BbMix FDor -4 flats : Ab Fm EbMix BbDor -5 flats : Db Bbm AbMix EbDor -6 flats : Gb Ebm DbMix AbDor -7 flats : Cb Abm GbMix DbDor - -As an extension, abc2midi also recognizes "Maj" for Major "Min" for Minor, -"Phr" for Phrygian, "Lyd" for Lydian, "Aeo" for Aeolian and "Loc" for -Locrian. Thus CMaj, EPhr, FLyd, AAeo and BLoc will all generate a stave with -no sharps or flats. If you use one of these modes in the key signature, it is -recommended that you add a comment giving the number of sharps or flats for -the benefit of other people who may not be familiar with modes. e.g. - -K: DLoc % 3 flats - -The key signature may be followed by modifiers. A modifier consists of ^^, ^ -=, _ or __ followed by a-g. As an example, ^g means every a should be played -sharp unless otherwise marked in the music. This adds ^g to the existing -key signature. For example - -K: G ^c^g - -will produce a key signature similar to A major. Following the abc draft -2.0 standard, inserting the string exp in the key signature will cause -abc2midi to override the existing key signature. Thus - -K: A exp _b - -will remove the f,c and g sharps and put a b-flat instead. You may use -both upper and lower case letters as key modifiers since they are -distinguished by abcm2ps. - - -Another extension of the K: field is that it can include a clef specifier -an octave specifier and a transpose specifier e.g. - -K:G clef=soprano octave=-1 transpose=-1 - -The clef is recognized by typesetting programs such as yaps and abc2mps -and in some situations it will cause abc2midi to transpose the notes -up or down by an octave. Recognized clefs are treble, -bass, baritone, tenor, alto, mezzo and soprano. There are also variants -of these clefs; treble-8, tenor-8, treble+8 covering ranges which are an -octave below and an octave above the normal treble clef. Other -variants are not recognized at this time. No transposition is -assumed for the bass clef since many abc files enter the notes -with all the commas. - -The octave specifier is a convenience to make entering music easier. -It allows the user to avoid repeatedly entering commas or apostrophes -when entering a sequence of low or high notes. Both yaps and abc2midi -will transpose the notes by the specified number of octaves during -the parsing stage. - -e.g. the passage B,,, C,, D,, E,, F,, could be written more compactly as - - I:octave=-2 - B, C D E F - I:octave=0 - - -Some instruments such as the Bb clarinet automatically transpose the -written music. For example in the case of the clarinet, the music is -written in the key of C but the instrument plays it in the key of Bb. -For multivoiced tunes, the %%MIDI transpose indication is not that useful -since it transposes all the voices by the specified amount. The -transpose=n subcommand in the K: field tells abc2midi to transpose -a particular voice by n semitones without affecting how it appears in -the printed score. For example for the tune, - -X:1 -T:tranposing -M:1/8 -L:2/4 -K:C -V:1 -K:C transpose=-1 -CDEF|GABc| -V:2 -K:C -CDEF|GABc -V:1 -CDEF|GABc| -V:2 -CDEF|GABc - -Voice 1 will be played one semitone lower than indicated. -These transposition features are disabled for channel 10 which -is reserved for percussion instruments. - - -Voice indication: - -Many multivoiced abc files now use the V: indication to -specify additional information such as the clef, name of -the voice (to appear in the score). Furthermore, the -V: indication may occur in the header (i.e. before the -first K: indication), for the sole purpose of specifying -such information. Abc2midi, yaps and abc2abc now recognizes -the subfields octave=, clef= and transpose= in the V: -field and treats them in the same manner as if they occurred -in a K: field (as discussed above). Thus the V: field may -appear as -V: 1 clef=treble+8 -or -[V: 3 transpose=3] -The V: field is also recognized in both the body and header -of the abc file. Note that not all abc applications may recognize -these extensions, so you use them at the risk that they may not be -treated as expected in some cases. Also in the event that -there is a conflicting indication in either the voice or -key signature field, eg. [V: 1 clef=transpose+8 octave=-1] -the clef indication predominates. - -These transposition features are disabled for channel 10 -which is reserved for percussion instruments. - - - -The Body of the Tune --------------------- - -Following the header is the tune. This is a textual notation for the things -you might see on a stave : - -Notes: - -A note consists of a pitch specifier followed by a length. Available pitch -specifiers are : - -C, D, E, F, G, A, B, C D E F G A B c d e f g a b c' d' e' f' g' a' b' - -This covers 4 octaves. abc2midi allows lower octaves to be reached by -adding extra , characters and higher octaves to be reached by adding -extra ' characters. However, this is not standard abc and may not be -supported by other abc utilities. - -You can raise or lower the pitch specifier a semitone by preceding it with -^ or _ respectively. The key signature and preceding sharps, flats and -barlines modify the default pitch in the same way as on a stave. Preceding -a note with = generates natural pitch and ^^ and __ can be used for double -sharp and double flat respectively. - -Microtones are indicated by following a ^ or _ with a fraction. -^/C is played as one quarter tone (i.e. half a semitone) above C. -_/C is played as one quarter tone (i.e. half a semitone) below C. -^^/4F is played as 1/4 of a semitone above F#. (The microtone always -follows any accidentals.) In general a microtone offset is indicated -by a fraction using the same convention as note length (described -below). Abcmidi uses the same microtone syntax as abcm2ps. -(See features.txt in the abcm2ps distribution.) - -Note that unlike accidentals, microtones do not propagate across a measure. -Microtones are implemented using the MIDI pitchwheel command. Since the -pitchwheel affects all notes played on a specific channel, a microtone -applied to any note in a chord specified by rectangular brackets, -(eg. [ACE]) will apply to all the notes in the chord. Microtone -accidentals override any sharpening or flattening induced by -key signatures. To illustrate - -K: G -^/F F ^^/2F F| - -does the following. Even though the key of G major causes F to be -sharp, the first note is F natural raised by half a semitone. -This effects the next note in making it F natural instead of F#. -The third note is F# raised by a half a semitone. The F# propagates -to the last note making it also F#. - -In accordance to the General MIDI recommendations the pitch range -of the pitchwheel is set to plus or minus two semitones. Therefore -abc2midi cannot go beyond this range. - -Microtones may be placed between tied notes, producing a pitch -bend effect. eg G- ^/G. - -Microtones have been introduced fairly recently into abcmidi (Mar 2005); -so far there is very little abc notated on the web which exploits -this feature. - - -The length is in general specified by a fraction following the pitch -specifier. However, the notation is made more concise by allowing much -of the fraction to be omitted. - -C - selects a note of 1 unit note length. -C2 - selects a note of 2 unit note lengths. -C/2 - selects a note of 1/2 unit note length. -C3/4 - selects a note of 3/4 unit note length. - -C/ is allowed as an abbreviation of C/2. -C// is allowed as an abbreviation of C/4. However, this is not standard -notation and is not allowed by all abc programs. - -No space is allowed within a note, but space may be used to separate -notes in the tune. - -Rests are written by using 'z' as the pitch specifier. - -z3 - a rest of 3 unit note lengths. - -Multiple bar rests can be created using 'Zn' where n is the number of -bars rest required. This is an extension to the standard syntax. - -Placing a dot before a note causes it to be played staccato. (.e) -Placing a M before a note indicates legato. (Me) -Placing a H before a note indicates a fermata. (He) -Placing a T before a note indicates that this note is trilled. (Te) -Placing a R before a note indicates that this note is rolled. (Re) - -Alternatively you may put !fermata! or !trill! before the -note. Other decorations have not been implemented in this manner. -The fermata may also be applied to a rest, (in order to handle -multivoiced files). - -Combining notes - -Three notes of the same length can be turned into a triplet by prefixing -them with (3. This has the effect of multiplying the note lengths by 2/3. -A chord can be represented by bracketting the notes together within [ and -] e.g. [a2e2]. An older notation which is also supported is to use + symbols -e.g. +a2e2+. The - symbol can be used to tie together notes of the same -pitch e.g. a2-a; this is equivalent to a3. - -From version 1.4 of abc2midi, the support of tuples and chords is -extended to include general tuple notation (p:q:r , [ and ] for chords -and a more flexible system of interpreting chords. - -(p:q:r means play the next r notes at q/p of their notated value. Thus -(3:2:3 is equivalent to an ordinary triplet (3 . - -It is possible in abc to write notes of different lengths within a chord -e.g. [ab2] . In this case, abc2midi takes the length of the first note -(or rest) in a chord is taken as the time before the next note is played. -However, if the music is to be typeset, this notation should be avoided -because the output will be ambiguous. Instead, the same effect can be -achieved using tied notes e.g. [ab2] c is equivalent to [ab-] [bc]. -A tie sign is always assumed to belong to the immediately previous note. - -A run of 2 or more different notes may be grouped together in a slur. -This usually means that the notes are to be played together as smoothly as -possible. In typeset music, a slur looks very similar to a tie, but in -abc, ( marks the start of a slur and ) marks the end. e.g. (ABc) . -abc2midi recognizes slurs, but they have no effect on the MIDI generated. - - - -Barlines and variant endings - -| is an ordinary barline -|| is a double barline -:| is "repeat last section". -|: is "repeat next section". -:: is "repeat last and next sections". -|1 or |[1 or | [1 is "first repeat ending". -:|2 or :|[2 or :| [2 is "second repeat ending". -|[1,3 is first and third ending -:|[2,4 is second and fourth ending -|] and [| are variants of ||. - -The notation :: is short for :| followed by |: . - -A tune with different ending for the first, second and third repeats has the -general form: - -|: common body of tune |1 first ending :|2 second ending :|3 third ending || - -You may also use this notation to indicate that the first ending is -played on the first and third repeat. For example - -|:common body of tune|1,3 first ending :|2 second ending :|4 forth ending| - -(The last variant ending should not end with a :|. In other words, -this will not work correctly. |: ... |[1,2 :|.) - -For multivoice abc files, you should be careful that the -different voices share the same repeat structure. When switching -voices (assuming they are interleaved) abc2midi does not cache -the repeat state. - - -Printed music commonly misses out a start repeat at the beginning of a tune. -abc2midi will try to fix up things if you miss out a start repeat in single -voice music. Where a tune starts with an anacrusis, abc2midi will always -fix a repeat to start at the anacrusis rather than the first barline. It is -recommended that you use matching start and end repeats rather than rely on -this behaviour. Missing start repeats are not supported in multiple voice -music. Also, abc2midi does not allow nested repeats. However, you can use -the more versatile part notation to achieve multiple repeats. - -It is not unusual to see music where a repeat does not coincide with the -end of a bar and the number of beats is not quite correct if the piece is -played exactly as written. A human player usually knows enough to correct -the mistake themselves, but abc2midi will play exactly what is written, so -care needs to be taken that this is corrected when the piece is transcribed -to abc. - - - -Rhythm field and Broken Rhythm Notation ---------------------------------------- - -R:hornpipe causes notes written in straight time to be played in dotted -time. The symbol > can be used to achieve a similar effect. For 4/4 -time this is applied to the 1/8 notes. For 2/4 time this is applied -to the 1/16 notes. - -a>b is notated as a3/2b/2 but played as a4/3b2/3. - -The symbols < >> << >>> <<< have similar meanings: - -a>b is notated and played as a7/4b/4. -a<>>b is notated and played as a15/8b/8. -a<< using the %%MIDI ratio command -described later. - -Beware that attempting "advanced" use of these symbols may mean your abc is -not portable between different abc programs. If the notes on either side of -the symbol are different lengths, this is reported as an error. If a and -b are not simple notes, or if there are other complications, then it is -safer to write the note lengths directly. - -Guitar chords : - -Anything in quotes is a guitar chord e.g. - -"A" "Gm" "B7" "Bm7" "D#aug" "Bbdim7" - -Guitar chords must use upper case A-G followed by optional # or b, then -the name of a chord type e.g. "m", "aug", "7". abc2midi currently -recognizes the following chord names : - - m, 7, m7, maj7, M7, 6, m6, aug, +, aug7, dim, dim7, 9, m9, maj9, M9, -11, dim9, sus, sus9, 7sus4, 7sus9, 5 - -You can also add your own; see the %%MIDI chordname command below. - -abc2midi automatically generates an accompaniment from the guitar chords. -There are a number of default chord/fundamental rhythms for common time -signatures which this uses, or you can set up your own. - -Lower case a-g followed by optional # or b will generate a single note, -the fundamental, only. - -The chord notation also allows chords such as "G/B" or "G/b". The -note following the / is interpreted in one of two ways: - -If the note following / does not exist as part of the given chord, it -is added to the chord below the root note of the chord (G in the above -example). - -If the note following / does exist in the chord, then the notes of the -chord are re-arranged so that it becomes the lowest pitch note of the -chord. This is known as an inversion of the chord. It does not matter -whether the note following the / is upper or lower case. They are -both treated the same. - -This notation has been extended so that finger numbers "1" "2" "3", -"4" or "5" are allowed. abc2midi ignores these, but a typesetting -program might support them. - -You may find some abc tunes that abuse this notation and use quotes -for things that are not guitar chords. Usually these are -tunes which have been typeset but never played by a computer. If you -don't want to just delete things in quotes, you can insert one of the -characters _, ^, @, < or > after the first quote e.g. "_Chorus" -"_Very Loud". This causes abc2midi to ignore the following text. However, -typesetting programs should recognize the first character as telling -them where to print the following text. - -Another extension is to allow musical instructions to appear in exclamation -marks e.g. !pizzicato! abc2midi currently supports the following : - !ppp! !pp! !p! !mp! !mf! !f! !ff! !fff! -Default volume is equivalent to !f!. In both the " " and ! ! fields, -abc2midi allows multiple terms separated by semi-colons e.g. "Am;1". - -The !breath! instruction causes the note to be played half length -followed by a rest of half its length (just like staccato). - -The !arpeggio! instruction affects the next chord and introduces -a larger delay between the onset of each note in the chord. (See -the text preceding %%MIDI chordattack in this file for more -explanations.) - -The !crescendo(! and !crescendo)! delineate the start and -end of a crescendo. Alternatively, you can use !<(! and !<)!. -Abc2midi does not implement a gradual loudness increase, -but instead increases the loudness at the start and end -of the crescendo by a fixed amount. (See %%MIDI beatmod -and %%MIDI deltaloudness for more information.) - -The !diminuendo(! and !diminuendo)! behave similarly but -reduce the loudness. These instructions have not been -implemented in yaps. - -!ped! and !ped-end! press and release the sustain pedal on -the piano. (The notes following !ped! and preceding !ped-end! are -held. This effect applies to all instruments besides the piano.) - -A line of music may contain any number of notes, barlines and guitar -chords. Spaces may be used to separate these. - -Some abc fields may appear within the body of the abc tune : - -K: - change key -L: - change unit note length -M: - change meter -Q: - change tempo -P: - part label -V: - voice label -w: - words to be matched syllable by syllable to notes - -Each field must be on a line by itself. - -Part Notation -------------- - -A part label must be a single character in the range A - Z. e.g. - -P:A - -A parts specifier in the header can be used to define the MIDI output as -some combination of the specified parts e.g. - -P:ABACABA - -You can use ( ) to repeat a part a specified number of times -e.g. P:A(AB)6 is equivalent to P:AABABABABABAB. If there are no brackets, -just the last part is repeated, so P:AAB3 is equicalent to P:AABBB. Dots -may be inserted into the part specifier to make it easier to read -e.g P:A.AB.AC - -If there is no parts specifier, the output is simply the parts (or just -the unlabelled music) in the order in which it appears in the tune body. - -You may if you wish have multiple voices sounding concurrently within each -part. These are indicated with V:N to indicate voice number N. - -e.g. - -V:1 - -V:2 - -and so on. - -A part label implicitly starts with V:1, as does the K: field which starts -the tune body, so you are not allowed to place a part label within a voice. -The duration of each of the voice parts must be the same for them to -synchronize correctly; abc2midi will warn you if they are not! From version -1.7 onwards, abc2midi allows any voice apart from voice 1 to be completely -omitted from a part. - -If no V: field appears after the first K:, whatever follows is assumed to -belong to voice 1 (the default voice). The V: field is an extension to -abc 1.5. - -The lines between one V:N field and the next V: field define a region -belonging to voice N. Within a part, you may have more than one such -region for each voice. The music for voice N within a part is all -the voice N regions taken in sequence. - -The M:, L: and K: fields in the header apply to all voices. After -the header, these fields apply only to the voice in which they appear. -In previous versions of abc2midi, an L:, for example, would affect -everything after it appeared up until the next L:, regardless of what -voice changes there were. - -The Q: tempo field should only appear in voice 1 and applies to all voices. - -The U: abbreviation field has limited use here. You can specify -an abbreviation such as -U: P = !trill! -and then in the body, eg. |DGAPF|, P will be replaced with !trill! -Only letters between H and Z can be used. Furthermore, you can -not redefine reserved letters such as H (fermata), L (unit length), -M (mordent) , R (roll) , T (trill). - - -Adding Lyrics to a Tune ------------------------ - -The W: field (upper case W) can be used for lyrics to be typeset separately -if the abc is printed out. The w: field (lower case w) in the body of a tune -supplies a line of lyrics to be matched syllable by syllable to the last line -of notes. These are usually printed below the notes if the abc is typeset. -abc2midi uses these to generate karaoke MIDI files. A karaoke MIDI file can -contain more than one set of lyrics in separate voices; an example is shown -at the end of this section. By default the lyrics are embedded in the same -MIDI track as the notes. If the runtime parameter -STFW is included, -then the lyrics will be placed in separate and adjoining MIDI tracks. -When the karaoke MIDI file is played using an appropriate player program, -the lyrics appear on the screen with the current syllable highlighted. -Within the lyrics, the following symbols may be used : - - break between words -- break between syllables within a word -| advance to next bar -_ indicates last syllable is to be held for another note -* indicates a one note rest for the singer. -~ appears as a space but connects syllables each side into one. -\- appears as - in the output -\ continuation character. Next w: field is part of the same line. - -A rest is not matched by any lyrics. -A tied note e.g. d2-d2 is treated as 2 notes (or however many parts it -is written as), despite the fact that it only plays as a single note. - -abc2midi ignores space characters if they occur either (a) between the -end of a word/syllable and a hyphen or underscore or (b) between a hyphen or -underscore and the beginning of a word/syllable. However, some software -incorrectly treats a hyphen as a separate word if there is a space between -it and the previous syllable, so, for example, you should write go-ing and -not go - ing to ensure that your abc is portable between programs. -Here are some examples taken from -http://abc.sourceforge.net/standard/abc2-draft.html - -w: syll-a-ble is aligned with three notes -w: syll-a--ble is aligned with four notes -w: syll-a -ble (equivalent to the previous line) -w: time__ is aligned with three notes -w: of~the~day is treated as one syllable (i.e. aligned with one note) - but appears as three separate words - - - -The following example illustrates most of these : - - gf|e2dc B2A2|B2G2 E2D2|.G2.G2 GABc|d4 B2 -w: Sa-ys my au-l' wan to your aul' wan\ -w: Will~ye come to the Wa-x-ies dar-gle? - -Note that the continuation character is used in a rather strange manner. -One w: field and all continuations will match one line of music, whether -or not the line of music ends with a continuation character. You can -think of the \ in a music line dividing that line into sections and \ -in a w: field further dividing these section into sub-sections. - -It is possible for a music line to be followed by several w: fields. -This can be used together with the part notation to create verses. -The first w: field is used the first time that part is played, then -the second and so on. If the tune uses repeats, these must be placed -at the end of a line of music in order to make sure that the start of -a w: field matches up with the repeat. - -Multivoiced lyrics example: - -X:1 -T:Multivoiced lyrics -M:2/4 -L:1/16 -K:C -V:1 -C4 C4 | E4 G4 | c8 |] -w: 1 2 3 4 5 -V:2 -C4 E4 | C4 B,4 | C8 |] -w: 11 12 13 14 15 - - -BarFly stress models --------------------- - -If you include the run time parameter -BF in execution string, abc2midi -will attempt to apply the BarFly stress model on the tune if it -recognizes the rhythm designator (eg. R: jig) and if the time signature -also matches the associated meter. There are two different implementations -of the stress model which have different effects. (See %%MIDI ptstress -below for more details.) You can specify the implementation to use by -following the -BF flag with either the numeral 1 or 2. If you do not -specify a model, the program will use model 2. More details can -be found on http://ifdo.ca/~seymour/runabc/abcguide/abc2midi_guide.html - - -abc2midi-specific commands (%%MIDI) ------------------------------------- - -abc2midi supports a number of commands of the form - -%%MIDI command - -Each of these should appear on a line by itself; however there -is now provision to pass the MIDI command in an inline I: field -(see CHANGES file, March 25 2005 entry.) All of them are allowed -within the abc tune body. By using these in combination with the part -notation, one can, for example, play a part transposed or in a different key. - -The idea behind this syntax is that other programs will treat it as a -comment and ignore it. - -%%MIDI channel n - -selects melody channel n (in the range 1-16). - -%%MIDI program [c] n - -selects program n (in the range 0-127) on channel c. If c is not given, the -program is selected on the current melody channel. Most modern tone -generators follow the General MIDI standard which defines the instrument -type for each program number. These instrument types are listed at the end -of this document. Note that for multivoiced files, the program command -is placed in the track associated with the voice previously declared. -If the %%MIDI indications affect channels in other tracks, it is -recommended that they are placed in the first track or first declared voice. -See the note in the CHANGES file for the date January 1 2005. - - -%%MIDI beat a b c n - -controls the way note velocities are selected. The first note in a bar has -velocity a. Other "strong" notes have velocity b and all the rest have velocity -c. a, b and c must be in the range 0-127. The parameter n determines which -notes are "strong". If the time signature is x/y, then each note is given -a position number k = 0, 1, 2 .. x-1 within each bar. Note that the units for -n are not the unit note length. If k is a multiple of n, then the note is -"strong". The volume specifiers !ppp! to !fff! are equivalent to the -following : - -!ppp! = %%MIDI beat 30 20 10 1 -!pp! = %%MIDI beat 45 35 20 1 -!p! = %%MIDI beat 60 50 35 1 -!mp! = %%MIDI beat 75 65 50 1 -!mf! = %%MIDI beat 90 80 65 1 -!f! = %%MIDI beat 105 95 80 1 -!ff! = %%MIDI beat 120 110 95 1 -!fff! = %%MIDI beat 127 125 110 1 - -%%MIDI beatmod n -increments by n (or decrements if n is negative) the velocities a, b and -c described above. It is also used in implementing crescendo and -diminuendo (eg. !<(!, !crescendo(! etc.) - - -%%MIDI nobeataccents - -For instruments such as church organ that have no greatly emphasized beat -notes, -using this will force use of the 'b' velocity (see %%MIDI beat) -for every note irrespective of position in the bar. This allows dynamics -(ff, f, etc) to be used in the normal way. - -%%MIDI beataccents - -Revert to emphasizing notes the the usual way. (default) - - -%%MIDI deltaloudness n -where n is a small positive number. By default the crescendo and -dimuendo instructions modify the beat variables a, b, and c by -15 velocity units. This instruction allows you to change this default. - -%%MIDI beatstring - -This provides an alternative way of specifying where the strong and weak -stresses fall within a bar. 'f' means velocity a (normally strong), 'm' -means velocity b (medium velocity) and 'p' means velocity c (soft velocity). -For example, if the time signature is 7/8 with stresses on the first, fourth -and sixth notes in the bar, we could use the following - -%%MIDI beatstring fppmpmp - - -%%MIDI transpose n - -transposes the output by the specified number of semitones. n may be -positive or negative. - -%%MIDI rtranspose n - -Relative transpose by the specified number of semitones. i.e. -%%MIDI transpose a followed by %%MIDI rtranspose b results in a -transposition of a+b. %%MIDI transpose b will result in a transposition -of b semitones, regardless of any previous transposition. - -%%MIDI c n - -specifies the MIDI pitch which corresponds to c. The default is 60. This -number should normally be a multiple of 12. - -%%MIDI grace a/b - -sets the fraction of the next note that grace notes will take up. a -must be between 1 and b-1. The grace notes may not sound natural -in this approach, since the length of the individual grace notes -vary with the complexity of the grace and the length of the -following note. A different approach (which is now the default) -assumes that the grace notes always have fixed duration specified -by a fraction of the unit length. To use the other approach -you would specify, - -%%MIDI gracedivider b - -where b specifies how many parts to divide the unit length -specified by the L: field command. For example if b = 4 and -L: = 1/8, then every grace note would be 1/(8*4) or a 32nd -note. Time would be stolen from the note to which the grace -note is applied. If that note is not long enough to handle -the grace then the grace notes would be assigned 0 duration. - -%%MIDI chordname name n1 n2 n3 n4 n5 n6 - -Defines how to play a guitar chord called "name". n1 is usually 0 and -n2, n3 to n6 give the pitches of the other notes in semitones relative -to the root note. There may be fewer than 6 notes in the chord, but not -more.If "name" is already defined, this command re-defines it. Unlike -most other commands, chordname definitions stay in effect from where they -are defined to the end of the abc file. The following illustrates how -m, 7, m7 and maj7 could be set up if they were not already defined. -%%MIDI chordname m 0 3 7 -%%MIDI chordname 7 0 4 7 10 -%%MIDI chordname m7 0 3 7 10 -%%MIDI chordname maj7 0 4 7 11 - -%%MIDI gchord string - -sets up how guitar chords are generated. The string is a sequence made of -of z's, c's f's and b's for rests, chords, fundamental and fundamental -plus chord notes respectively. This specifies how each bar is to be played. -An optional length is allowed to follow the z's, c's, f's and b's e.g. czf2zf3. -If the abc contains guitar chords, then abc2midi automatically adds chords and -fundamentals after encountering the first guitar chord. It keeps using that -chord until a new chord is specified in the abc. Whenever the M: field is -encountered in the abc, an appropriate default string is set : - -For 2/4 or 4/4 time default is equivalent to : -%%MIDI gchord fzczfzcz - -For 3/4 time default is equivalent to : -%%MIDI gchord fzczcz - -For 6/8 time default is equivalent to : -%%MIDI gchord fzcfzc - -For 9/8 time default is equivalent to : -%%MIDI gchord fzcfzcfzc - -Please note, that the default gchord string is reissued any -time a time signature change is specified in the body of the music. -This means if one of the bars has an extra beat you included -a M: declaration before and after the measure, the gchord string -would be reset to the default string for that time signature -and not the one that you had declared. It is necessary for -you to send another %%MIDI gchord declaration after the -the time signature in order to set this back the way you want -it. This is one of the changes introduced into abc2midi so -that the accompaniment track always follows the meter of the -music for the regular time signatures. - -The gchord command has been extended to allow you to play -the individual notes comprising the guitar chord. This allows -you to play broken chords or arpeggios. The new codes g,h,i,j, -G,H,I,J reference the individual notes starting from the -lowest note of the chord (not necessarily the root in the -case of inverses). For example for the C major chord, g -refers to C, h refers to E and i refers to G. For a gchord -command such as, - -%%MIDI gchord ghih - -Abc2midi will arpeggiate the C major guitar chord to -CEGE. The upper case letters G,H,I, and J refer to -the same notes except they are transposed down one -octave. Note for the first inversion of the C major -chord (indicated by "C/E"), E would be the lowest -note so g would reference the note E. - -Like other gchord codes, you may append a numeral indicating -the duration of the note. The same rules apply as before. -You can use any combination of the gchord codes, -(fcbghijGHIJz). - -Another recent extension to gchords is the presence -of gchords in separate voices. Here is an example: - -X:1 -T: gchord multivoice extension -M: 4/4 -L: 1/4 -K: G -V: 1 -%%MIDI gchord ghih -"G" z4| z4|\ -%%MIDI gchordoff -z4| -V:2 -%%MIDI chordprog 12 -%%MIDI gchord GHIHG -z4|"D" z4|z4| - - - - -%%MIDI chordprog n - -Sets the MIDI instrument for the chord notes to be n. If the -command includes the string octave=n where n is a number between --2 and +2, then the chord notes will be shifted n octaves from its -usual position, eg. (%%MIDI chordprog 32 octave=1). Any other -descriptors will be ignored, eg (%%MIDI chordprog 0 Acoustic Piano). - -%%MIDI bassprog n - -Sets the MIDI instrument for the bass notes to be n. If the -command includes the string octave=n where n is a number between --2 and +2, then the bass note will be shifted n octaves from its -usual position. eg. (%%MIDI bassprog 32 octave=-1). - -%%MIDI chordvol n - -Sets the volume (velocity) of the chord notes at n. - -%%MIDI bassvol n - -Sets the volume (velocity) of the bass notes at n. There is no corresponding -melodyvol command since there are 3 velocity values for melody, set using the -beat command. - -%%MIDI gchordon - -Turns on guitar chords (they are turned on by default at the start of a -tune). - -%%MIDI gchordoff - -Turns off guitar chords. - -%%MIDI fermatafixed -Directs abc2midi to expand a fermata by one unit length. -Thus HC3 becomes C4. - -%%MIDI fermataproportional -This is the default. A fermata doubles the length of a -note so HC3 becomes C6. - -%%MIDI droneon - -This turns on a continuous drone used in bagpipe music. The -drone consists of two notes (by default A, and A,,) played -on a bassoon at a MIDI loudness (velocity) 80. If you -can configure the drone sound using the %%MIDI drone command -described below. - -%%MIDI droneoff - -This turns off the drone. - -%%MIDI drone n1 n2 n3 n4 n5 - -Configures the drone chord. n1 = MIDI program, -n2 = MIDI pitch 1, n2 = MIDI pitch 2, n4 = MIDI velocity 1, -and n5 = MIDI velocity 2. By default they have already -been set to 70 45 33 80 80. - - - -%%MIDI drum string [drum programs] [drum velocities] - -This sets up a drum pattern. The string determines when there is a drum beat -and the drum program values determine what each drum strike sounds like. - -e.g. %%MIDI drum d2zdd 35 38 38 100 50 50 - -The string may contain 'd' for a drum strike or 'z' for a rest. By default -a voice starts with no drum pattern. Like gchord, a command - -%%MIDI drumon - -is needed to enable the drumming. The drum pattern is repeated -during each bar until a - -%%MIDI drumoff - -is encountered. The %%MIDI drum command may be used within a tune -to change the drum pattern. This command places the drum sounds on -channel 10 and assumes your tone generator complies with the -General Midi standard - if it does not, then you may hear tones -instead of drum sounds. (Note the old method of using the -instruction !drum! and !nodrum! is being deprecated.) - -In both the gchord and drum commands, the standard note length of -a single note f,c,z or d is not set by the L: command. Instead it -is adjusted so that the entire gchord string or drum string fits -exactly into one bar. In other words the duration of each note -is divided by the total duration of the string. This means that, -for example, the drum string "dd" is equivalent to drum string "d4d4". -You cannot currently specify fractions directly (eg. C3/2) -as done in the body of the music, but it is still possible to express -complex rhythms. For example, to indicate a rhythm such as -(3ddd d/d/d/d, you would write the string "d4d4d4d3d3d3d3". - -For reference, the percussion instruments defined in the General MIDI -standard are given below. - -35 Acoustic Bass Drum 59 Ride Cymbal 2 -36 Bass Drum 1 60 Hi Bongo -37 Side Stick 61 Low Bongo -38 Acoustic Snare 62 Mute Hi Conga -39 Hand Clap 63 Open Hi Conga -40 Electric Snare 64 Low Conga -41 Low Floor Tom 65 High Timbale -42 Closed Hi Hat 66 Low Timbale -43 High Floor Tom 67 High Agogo -44 Pedal Hi-Hat 68 Low Agogo -45 Low Tom 69 Cabasa -46 Open Hi-Hat 70 Maracas -47 Low-Mid Tom 71 Short Whistle -48 Hi Mid Tom 72 Long Whistle -49 Crash Cymbal 1 73 Short Guiro -50 High Tom 74 Long Guiro -51 Ride Cymbal 1 75 Claves -52 Chinese Cymbal 76 Hi Wood Block -53 Ride Bell 77 Low Wood Block -54 Tambourine 78 Mute Cuica -55 Splash Cymbal 79 Open Cuica -56 Cowbell 80 Mute Triangle -57 Crash Cymbal 2 81 Open Triangle -58 Vibraslap - -Note you are able to change this mapping using the -the MIDI command %%MIDI drummap described below. - - -%%MIDI drummap note midipitch - -where the pitch of the note is notated using abc notation -and midipitch is a number between 35 and 81 inclusive referring -to the above table. This command is used if you -are notating a drum track, i.e. a voice played on -channel 10. Rather than being forced to use the note -corresponding to the desired percussion instrument, (for -example C (MIDI pitch 60) for hi bongo, you can use can -change the mapping to use a more convenient pitch. -for example to access bass drum 1 (MIDI pitch 36) you -would require the note C,, which is awkward to display -in common music notation. You can change the mapping to -say _D using -%%MIDI drummap _D 36. - -An example is provided in the file CHANGES (November 6 2005). - - -%%MIDI drumbars n - -The %%MIDI drum line can sound quite monotonous if it is repeated -each bar. To circumvent this problem a new MIDI command -%%MIDI drumbars n -where n is a small number will spread out the drum string over -n consecutive bars. By default drumbars is set to 1 maintaining -compatibility with existing abc files. You should take -care that the drumstring is evenly divisible between the -drumbar bars. Also the time signature should not change -between bars in a drumbar unit. (Sample abc file in CHANGES -June 24 2008.) - -%%MIDI gchordbars n - -This command spreads the gchord string over n consecutive bars -of equal length. The gchord string should be evenly divisible -by n or else the gchords will not work properly. A sample -abc file is found in CHANGES March 17 2009. - -%%MIDI control [bass/chord] n1 n2 - -This generates a MIDI control event. If the word "control" is followed by -"bass" or "chord", the event will be applied to the bass or chord channel, -otherwise it will be applied to the current channel. n1 and n2 are numbers -in the range 0-127. Generally, n1 selects a control parameter and n2 is -the value to which it is set. A couple of examples : - -%%MIDI control 7 50 - -will set the main volume of the channel to 50 - -%%MIDI control 10 0 - -will set the pan parameter (left/right balance) to 0. - -See the manual for your MIDI tone generator to find out what control events -are supported. - -%%MIDI portamento [bass/chord] n - -This will turn on the MIDI portamento controller and set the speed -of sliding between pitches to n. Like %%MIDI control, if the word -portamento is followed by "bass" or "chord", the event will be -applied to the bass or chord channel, otherwise it will be -applied to the current channel. The parameter n should be between -0 and 63. Large values imply a slow transition between pitches. -I have found the resulting effect to be rather weird, especially -for large pitch intervals. - -%%MIDI noportamento [bass/chord] - -This will turn off the portamento controller (current default). - -%%MIDI pitchbend [bass/chord] - -This generates a pitchbend event on the current channel, or on the bass -or chord channel as specified. The value given by the following two bytes -indicates the pitch change. - -%%MIDI nobarlines - -This is a somewhat obscure option to support early music without barlines. -Normally, an accidental applied to one note e.g. ^c will apply to every note -at the same point in the scale until the end of the bar (so C,, C, C c c' -would all be sharpened). This option turns off this behaviour, so that an -accidental applies only to the next note. It should be used in the header of -any tune requiring this behaviour. - -%%MIDI barlines - -This turns off the effect of %%MIDI nobarlines in the middle of a tune. This -is the default behaviour assumed at the start of every tune. - -%%MIDI ratio n m - -This sets the ratio of note lengths in broken rhythm (e.g. a>b). -The default behaviour is for note a to sound for twice as long as note b. -This can be achieved with - -%%MIDI ratio 2 1 - -and hornpipes are commonly played with approximately this ratio. However, -for other musical styles, a different ratio may be appropriate. If you -are using abc2midi to export music to another program for printing, then -you may wish to use a ratio of 3:1 which is how hornpipes are usually -notated. This can be achieved with - -%%MIDI ratio 3 1 - - -Arpegiation: - -With version 1.54 Dec 4 2004 of abc2midi, notes in chords -(eg. [FAc]) are not played in the same instant but offsetted -and shortened by 10 MIDI time units. Thus the first note -in the chord (eg. F) is played for the full indicated time, -the second note (eg. A) starts 10 MIDI units later and is shortened -by the same amount and the third note starts another 10 MIDI -units later and is shortened by another 10 units. This introduces -an "expressivo" option and avoids the heavy attack. (This -does not apply to gchords or multivoiced chords.) The amount -of the delay and shortening may be configured by the MIDI command - -%%MIDI chordattack n - -where n is a small number. If n is zero, then abc2midi should -behave as in earlier versions. The delay n is in MIDI time units -where there are 480 units in a quarter note beat. The program -may not run correctly if n is too large and there are short -chords. - -%%MIDI randomchordattack n - -where n is a small number. This is similar to above, except the -delay of each note in the chord varies randomly between 0 and n-1. -Therefore each chord is played differently. - - -Articulation: - -%%MIDI trim x/y - -where x and y are two numbers. This command controls the articulation -of notes and chords by placing silent gaps between the notes. The length -of these gaps is determined by x/y and the unit length specified by the L: -command. These gaps are produced by shortening the notes by the same amount. -If the note is already shorter than the specified gap, then the gap -is set to half the length of the note. The fraction x/y indicates -a note duration in the same manner as specified in the abc file. -The actual duration is based on the unit length specified by the -L: field command. It is recommended that x/y be a fraction close -to zero. The denominator, y does not need to be a power of 2. -Trimming is disabled inside slurs as indicated by parentheses. - -You can turn off all note trimming by setting x to 0, -eg 0/1. By default, note trimming is turned off at the beginning -of a tune or voice command. - - - - -To avoid the problem of breaking up a music line -in order to place a %%MIDI command, for example, -A2 B2|\ -%%MIDI drumon -C2 D2| -you can place the MIDI instruction inside an info field -using the following syntax. - -A2 B2|[I:MIDI= drumon] C2 D2| - -The '=' following the MIDI is very important. The info field -syntax allows you to place several MIDI commands in one inline -block, for example -[I: MIDI = program 73 MIDI=chordprog 29] -(Spaces are optional.) -Other examples can be seen in the file CHANGES in the -following the March 25 2005 entry. - -Another recent extension is the allowance of drum patterns -in separate voices. Here is an example: - -X:1 -T: drum multivoice extension -M: 4/4 -L: 1/4 -K: G -V:1 -%%MIDI drum dddd 45 45 45 45 70 50 60 50 -%%MIDI drumon -z4|z4|\ -%%MIDI drumoff -z4| -V:2 -%%MIDI drum ddddd 54 54 54 54 54 70 50 50 60 50 -z4|\ -%%MIDI drumon -z4|z4| - - - - -%%MIDI temperamentlinear octave_cents fifth_cents - -This command allows you to change the temperament of the -scale. Octave_cents specifies the size of an octave -in cents of a semitone or 1/1200 of an octave. Fifth_cents -specifies in the size of a fifth (normally 700 cents). -For example: -%%MIDI temperament 1200.5 698.0 -will produce a slightly stretched scale with narrowed fifths. - -More details on linear tempered scales can be found -on the site -http://www.xs4all.nl/~huygensf/scala/ -The pitch of each note is modified using a MIDI pitchbend -command to comply with the scale. In order to handle chords, -each note of the chord must be played on a different MIDI -channel. - -The normal musical scale has a temperament of -%%MIDI temperamentlinear 1200 700 - -but to avoid unnecessary pitchbends you should restore -to the normal temperament using the command -%%MIDI termperamentnormal - - -%%MIDI tuninsystem comma53 -quantizes the pitches of the notes using the comma53 system where -an octave is divided into 53 equally spaced tones. - - -%%MIDI makechordchannels n - -If you are not modifying the temperament or inserting -microtones you can ignore this command. Normally -in voice chords containing microtones e.g. [_/CE_/G] -will not be handled correctly because all the notes in the -chord are played on the same MIDI channel. If you include -the above command where n here is 2, then two separate channels -will be allocated for playing the other two notes in this chord. -These separate channels will be used whether the pitches -of the notes are bent or not. Once you allocate those channels -with this command, they are no longer available and since -there are only 16 MIDI channels you can easily run out. -Furthermore, you need to allocate chordchannels for any -other voices which may have in voice chords. The channel -allocation process will automatically propagate the program -assignment (musical instrument) to the other chordchannels, -so be sure you set the program before using this command. -If you need to change the program assignments, you can -find out the channel numbers that were assigned by running -abc2midi in verbose mode using the -v command in the -execution string. - - -%%MIDI ptstress filename - -This command loads file filename into abc2midi which contains -the Phil Taylor stress parameters and puts abc2midi in the mode -where it applies these stress parameters on every note. This -model runs in opposition to the standard beat model, so the -MIDI beat, beatstring, beatmod commands become ineffectual. -This also means that the dynamic indications !f! !pp! etc. -do not work any more. - -There are two different implementations of the stress model. -Model 1 modifies the note articulation and takes -control of the MIDI trim parameters too. To revert back to -the standard model, put the command %%MIDI beataccents. -Model 2 modifies both the onset and ending of each note -allowing a musical beat to expand or contract in time. However, -the length of a musical measure should be preserved. Note -if you using model 2, which the current default, you must -include -BF as one of the runtime parameters of abc2midi. - -The model divides a bar into equal segments. For each segment, -a loudness or MIDI velocity is specified and a duration multiplier -is specified. If a note falls into a specific segment, it assumes -the velocity of that segment and its duration is modified -accordingly. If the note overlaps more than one segment, then -the note assumes the average of those segment values. - -The input file specifies the number of segments and the loudness -and duration multipliers for each segment. The file has the -following format: - -8 -110 1.4 -90 0.6 -110 1.4 -90 0.6 -110 1.4 -90 0.6 -110 1.4 -90 0.6 - -where the first value is the number of segments and each line -specifies the velocity and duration multiplier of the specific -segment. The velocity is limited to 127 and the duration is a -decimal number. The note durations is modified by varying the -gap between notes, so it is not possible to extend a note. This -preserves the regular tempo of the music. The program scales, -the note duration indications by dividing it by the maximum -value which here is 1.4. - - -%%MIDI stressmodel n - -where n is either 1 or 2, selects the stress model implementation. - - -%%MIDI expand m/n - -This command,causes all the following notes be lengthened by -the amount factor m/n so that they overlap the next note. -Thus the next note is started at the proper time, but the -previous note ends after the beggining of the note. -This overlap causes a nice effect for particular MIDI instruments -such as choir voices. The %%MIDI expand function behaves in -the opposite manner as the %%MIDI trim function. - - -%%MIDI snt k pitch - -Since many MIDI devices do not support this function, use of -this command is not recommended. The command changes the tuning -of a single note using the "universal system exclusive messages". -k is the MIDI pitch being retuned (a number between 0 to 127) -and pitch is a floating point number representing the pitch's new -value in MIDI pitch units. - - -%%MIDI bendvelocity n1 n2 - -This command defines how to bend the a particular note following -the !bend! decoration. The pitch of the note shifts up or down -as the note is playing. n1 is the velocity of the pitch change -and n2 is the acceleration (how fast the velocity changes). -The bend is accomplished by modifying the MIDI pitchwheel where -the 8192 is the neutral. Minimum and maximum values -are 0 and 16383 correspond to two semitones shifts in either -directions. The velocity and acceleration may be positive or -negative. - - -%%MIDI bendstring n1 n2 n3 n4 etc - -This command is a more general way for defining how a note -is bent. It can also apply with !shape! decoration -discussed later. The note is split into n equal segments corresponding -to each of the n1, n2, etc values. The n1, n2, ... values are the -increments (or decrements if they are negative) which are added -to the pitchwheel value. Thus -%%MIDI bendstring 1000 1000 -500 -500 -will split a note into 4 parts and the pitchwheel values in -each part will be 9192, 10192, 9692, and 9192. - - - -%%MIDI controlstring m n1 n2 n3 ... - -This command defines how the m th MIDI controller changes for the -note following the !shape! decoration. The note is divided -into n segments where n is the number of values following -the m or controller number. n1, n2, n3, ... are the values for -controller m in each segment. This allows you to shape the sound -of the note. For example, by changing the modwheel or the expression. -All the values m, n1, n2 are numbers between 0 and 127. - - - - - - -Global settings for abc2midi ----------------------------- - -If you are creating an abc file with many tunes, abc2ps and abcm2ps -allows you to declare certain settings that apply to all tunes -by placing them at the beginning of the file prior to the -start of the tune. Abc2midi provides this feature but presently -only to a limited extent. The following MIDI commands will -change the defaults for all tunes if they are placed outside -of any tune. - -%%MIDI C ... -%%MIDI nobarlines ... -%%MIDI barlines ... -%%MIDI fermatafixed -%%MIDI fermataproportional -%%MIDI ratio ... -%%MIDI chordname ... -%%MIDI deltaloudness ... - -All other MIDI commands placed outside of a tune will be -ineffective and return a warning message - -"cannot handle this MIDI directive here" - -(For more details see CHANGES, May 06 2005 entry.) - -Any of these defaults can be changed as many times as you like -provided that they are occur outside a tune which is usually -delineated by a X: reference number and a blank line. - -Voice Splitting ---------------- - -Abcm2ps allows a voice to separate into two or more voices -in a specific bar using the symbol '&'. This feature -now works in abc2midi. Abc2midi places the split voice -into a separate MIDI track with intervening rests. -When a voice splits, the new voice inherits the program -number (musical instrument) from the parent voice. - - -Compatibility with proposed abc standard version 2.0 ----------------------------------------------------- - -The proposed standard introduces a new copyright field -using the syntax - -%%abc-copyright (c) Copyright John Smith 2003 - -Abc2midi now inserts this in the MIDI file in the form of a -metatext copyright tag. Changes were made to the event_specific -function in store.c to process the copyright information. It -is also copied into the Karaoke track (if it is created) as -as @T field. - - - - -Typesetting abc ---------------- -If you want to typeset your abc, there are some more features of abc syntax -that you need to know: - -If 2 notes appear consecutively with no space between them, they will be -grouped together under the same beam. A space between them prevents them -sharing a beam. - -A new line of stave music is generated by the newline at the end of a -line of abc music. To suppress this, put a \ character at the end of the -abc music line. - -An abc music line should end either at a bar line or at the space -between two notes which indicates they do not share a beam. This is true -whether or not the abc line ends with a \ character. - -Error Messages and Warnings ---------------------------- - -abc2midi attempts to perform various checks on the abc and -reports any problems via error and warning messages: - -A warning message indicates there is something strange in the abc - -possibly an error or possibly non-standard usage. - -An error message means that abc2midi thinks there is definitely an -error in the abc and the MIDI generated may not be correct. - -In a small number of cases, an error may cause abc2midi to stop. -This is usually either because it has run out of memory or because -there is some problem with reading or writing a file. - - - -Bar counting and checking -------------------------- - -Conventionally bars are numbered starting from one. If the first -bar is incomplete (anacrusis), then it is counted as zero. -Abc2ps and abcm2ps follows this convention but abcMIDI does not. -All bars are counted starting from zero and furthermore if there -is an incomplete bar just before a repeat (:| |: or ::), the -bar count is not incremented. It is difficult to change this -convention since abcMIDI assigns a number to the bar ahead -when it sees a bar line. It would be necessary to introduce -some look ahead for the first bar in the tune. - -If abc2midi reports a problem in a specific bar, you can use -yaps with the -k option (for print bar numbers) to locate this -bar. If you use another program such as abc2ps, then the bar -number in the displayed or printed version may be one unit -higher. - -The most common error seems to be a missing beat or having -an extra beat in a bar. In the vicinity of a repeat, abc2midi -tries to complete the first bar using the incomplete bar at -the end of the repeat. If the first bar can be completed, then -no warning is reported. The first bar may be complete the first -time the section is played but incomplete in the second repeat. -Complications occur when the left repeat symbol (|:) is -missing and abc2midi has to assume it is at the beginning. -More complications occur when there is a key change after -the repeat or the music is split into parts A,B,C, etc. -Yaps does the bar checking differently when it encounters -repeats so that it may not resolve as many incomplete bars -and report more warnings. - -The bar checking is present for providing warnings. For some -music, an extra beat may be intentional and it is not -marked by a meter change. If you are only printing the -music, there is probably no problem; however, if you are -producing a MIDI file and there is guitar (gchord) accompaniment -then a break or missing beat might be noticeable. - - - -General MIDI Program Number/Instrument Name -------------------------------------------- - - 0. Acoustic Grand Piano - 1. Bright Acoustic Piano - 2. Electric Grand Piano - 3. Honky-tonk Piano - 4. Electric Piano 1 - 5. Electric Piano 2 - 6. Harpsichord - 7. Clavi - 8. Celesta - 9. Glockenspiel - 10. Music Box - 11. Vibraphone - 12. Marimba - 13. Xylophone - 14. Tubular Bells - 15. Dulcimer - 16. Drawbar Organ - 17. Percussive Organ - 18. Rock Organ - 19. Church Organ - 20. Reed Organ - 21. Accordion - 22. Harmonica - 23. Tango Accordion - 24. Acoustic Guitar (nylon) - 25. Acoustic Guitar (steel) - 26. Electric Guitar (jazz) - 27. Electric Guitar (clean) - 28. Electric Guitar (muted) - 29. Overdriven Guitar - 30. Distortion Guitar - 31. Guitar harmonics - 32. Acoustic Bass - 33. Electric Bass (finger) - 34. Electric Bass (pick) - 35. Fretless Bass - 36. Slap Bass 1 - 37. Slap Bass 2 - 38. Synth Bass 1 - 39. Synth Bass 2 - 40. Violin - 41. Viola - 42. Cello - 43. Contrabass - 44. Tremolo Strings - 45. Pizzicato Strings - 46. Orchestral Harp - 47. Timpani - 48. String Ensemble 1 - 49. String Ensemble 2 - 50. SynthStrings 1 - 51. SynthStrings 2 - 52. Choir Aahs - 53. Voice Oohs - 54. Synth Voice - 55. Orchestra Hit - 56. Trumpet - 57. Trombone - 58. Tuba - 59. Muted Trumpet - 60. French Horn - 61. Brass Section - 62. SynthBrass 1 - 63. SynthBrass 2 - 64. Soprano Sax - 65. Alto Sax - 66. Tenor Sax - 67. Baritone Sax - 68. Oboe - 69. English Horn - 70. Bassoon - 71. Clarinet - 72. Piccolo - 73. Flute - 74. Recorder - 75. Pan Flute - 76. Blown Bottle - 77. Shakuhachi - 78. Whistle - 79. Ocarina - 80. Lead 1 (square) - 81. Lead 2 (sawtooth) - 82. Lead 3 (calliope) - 83. Lead 4 (chiff) - 84. Lead 5 (charang) - 85. Lead 6 (voice) - 86. Lead 7 (fifths) - 87. Lead 8 (bass + lead) - 88. Pad 1 (new age) - 89. Pad 2 (warm) - 90. Pad 3 (polysynth) - 91. Pad 4 (choir) - 92. Pad 5 (bowed) - 93. Pad 6 (metallic) - 94. Pad 7 (halo) - 95. Pad 8 (sweep) - 96. FX 1 (rain) - 97. FX 2 (soundtrack) - 98. FX 3 (crystal) - 99. FX 4 (atmosphere) - 100. FX 5 (brightness) - 101. FX 6 (goblins) - 102. FX 7 (echoes) - 103. FX 8 (sci-fi) - 104. Sitar - 105. Banjo - 106. Shamisen - 107. Koto - 108. Kalimba - 109. Bag pipe - 110. Fiddle - 111. Shanai - 112. Tinkle Bell - 113. Agogo - 114. Steel Drums - 115. Woodblock - 116. Taiko Drum - 117. Melodic Tom - 118. Synth Drum - 119. Reverse Cymbal - 120. Guitar Fret Noise - 121. Breath Noise - 122. Seashore - 123. Bird Tweet - 124. Telephone Ring - 125. Helicopter - 126. Applause - 127. Gunshot - ---------------------------------------------------------------------- -This reference written 1995-1998 by James Allwright +Guide to writing abc for abc2midi +--------------------------------- +Updated June 1 2017. %%MIDI program ranges from 0 to 127 not 1 to 128. + +The defining document for abc is the abc version 1.6 specification which can +be found at http://www.gre.ac.uk/~c.walshaw/abc2mtex/abc.txt . This document +is a description of abc as interpreted by abc2midi. + +An abc tune consists of a header followed by a body. Each line in the +header is a different field starting with a letter immediately followed +by : and then the text of the field. The body of the tune contains lines +of music, though it may also contain certain fields. The end of the tune is +marked by a blank line (so blank lines cannot appear within the tune header +or body). + +Comments are allowed in both the header and the body. A comment starts +with a % sign and continues to the end of the line. A comment may be on +a line of its own or at the end of a line of abc. + +The header should look something like this : + +X:1 +T:The Rose Tree +M:4/4 +L:1/8 +Q:1/4=120 +K:G + +X: is the reference number (each tune in a file should have a unique reference +number). T: is the title of the tune, M: is the time signature, L: is the +unit note length and K: is the key signature. Q: is the tempo field. In +the above example the tempo is given as 120 quarter-notes per minute. X: +must be the first field and K: must be the last field in the header. + +It is advisable to write the fields M:, L: and Q: in that order. The +reason for this is that M: can set up a unit note length which L: +overrides and the tempo field Q: can be written in other forms that +depend on unit note length. This order makes the meaning clear and +unambiguous. The original abc specification gives a rule for computing +a default value for the unit note length from the M: field. abc2midi +only uses this rule for choosing a unit note length in the header if +the L: field has been omitted. + + +The tempo is usually indicated with the Q: field command, +eg. Q:1/4=180 which is interpreted as 180 quarter beats per minute. +In compliance with the abc standard 2.0, you may also +indicate the tempo using directives such as "lento", +"allegro", "vivace" etc. + +The following table based on +http://www.music.vt.edu/musicdictionary/appendix/tempo/tempo1.html +was used to translate the indications: + +Larghissimo 1/4=40 Moderato 1/4=104 +Adagissimo 1/4=44 Allegretto 1/4=112 +Lentissimo 1/4=48 Allegro 1/4=120 +Largo 1/4=56 Vivace 1/4=168 +Adagio 1/4=59 Vivo 1/4=180 +Lento 1/4=62 Presto 1/4=192 +Larghetto 1/4=66 Allegrissimo 1/4=208 +Adagietto 1/4=76 Vivacissimo 1/4=220 +Andante 1/4=88 Prestissimo 1/4=240 +Andantino 1/4=96 + +Note: case is ignored (eg. ALLEGRO Allegro or allegro are treated +the same). The directive must be enclosed in double quotes. + +Reference: http://abc.sourceforge.net/standard/abc2-draft.html#Q:%20-%20tempo + +Examples: + +X:1 +T:tempo +M:2/4 +L:1/4 +K:G +Q: "Adagio" +CD|EF| +Q: "Adagio" 1/4=40 +GA|Bc| + +(In the second tempo command, the 1/4=40 overrides the default +1/4=59.) + + + +The symbols M:C and M:C| give common time (4/4) and cut time (2/2) +The symbol M:none omits the meter entirely (free meter). +It is also possible to specify a complex meter, e.g. M:(2+3+2)/8, to make explicit which beats should be accented. The parentheses around the numerator are optional. + +Other fields may also appear in the header. Common ones are C: composer +of the tune, D: discography, H: history of the tune, S: source, N: notes, +Z: transcription note, A: area from which the tune comes, B: book +and R: rhythm. + + +Key Signature: + +The K: is made up of a base note A-G possibly followed by b for flat or +# for sharp. This specifies a major key. A minor key can be specified +by adding an m while Mixolydian and Dorian modes can be specified by adding +Mix and Dor respectively e.g. + +K:Eb +K:EDor +K:EbDor + +The following table relates the number of sharps or flats you see on the +stave to what is in the K: field : + +7 sharps: C# A#m G#Mix D#Dor +6 sharps: F# D#m C#Mix G#Dor +5 sharps: B G#m F#Mix C#Dor +4 sharps: E C#m BMix F#Dor +3 sharps: A F#m EMix BDor +2 sharps: D Bm AMix EDor HP Hp +1 sharp : G Em DMix ADor +0 sharps: C Am GMix DDor +1 flat : F Dm CMix GDor +2 flats : Bb Gm FMix CDor +3 flats : Eb Cm BbMix FDor +4 flats : Ab Fm EbMix BbDor +5 flats : Db Bbm AbMix EbDor +6 flats : Gb Ebm DbMix AbDor +7 flats : Cb Abm GbMix DbDor + +As an extension, abc2midi also recognizes "Maj" for Major "Min" for Minor, +"Phr" for Phrygian, "Lyd" for Lydian, "Aeo" for Aeolian and "Loc" for +Locrian. Thus CMaj, EPhr, FLyd, AAeo and BLoc will all generate a stave with +no sharps or flats. If you use one of these modes in the key signature, it is +recommended that you add a comment giving the number of sharps or flats for +the benefit of other people who may not be familiar with modes. e.g. + +K: DLoc % 3 flats + +The key signature may be followed by modifiers. A modifier consists of ^^, ^ +=, _ or __ followed by a-g. As an example, ^g means every a should be played +sharp unless otherwise marked in the music. This adds ^g to the existing +key signature. For example + +K: G ^c^g + +will produce a key signature similar to A major. Following the abc draft +2.0 standard, inserting the string exp in the key signature will cause +abc2midi to override the existing key signature. Thus + +K: A exp _b + +will remove the f,c and g sharps and put a b-flat instead. You may use +both upper and lower case letters as key modifiers since they are +distinguished by abcm2ps. + + +Another extension of the K: field is that it can include a clef specifier +an octave specifier and a transpose specifier e.g. + +K:G clef=soprano octave=-1 transpose=-1 + +The clef is recognized by typesetting programs such as yaps and abc2mps +and in some situations it will cause abc2midi to transpose the notes +up or down by an octave. Recognized clefs are treble, +bass, baritone, tenor, alto, mezzo and soprano. There are also variants +of these clefs; treble-8, tenor-8, treble+8 covering ranges which are an +octave below and an octave above the normal treble clef. Other +variants are not recognized at this time. No transposition is +assumed for the bass clef since many abc files enter the notes +with all the commas. + +The octave specifier is a convenience to make entering music easier. +It allows the user to avoid repeatedly entering commas or apostrophes +when entering a sequence of low or high notes. Both yaps and abc2midi +will transpose the notes by the specified number of octaves during +the parsing stage. + +e.g. the passage B,,, C,, D,, E,, F,, could be written more compactly as + + I:octave=-2 + B, C D E F + I:octave=0 + + +Some instruments such as the Bb clarinet automatically transpose the +written music. For example in the case of the clarinet, the music is +written in the key of C but the instrument plays it in the key of Bb. +For multivoiced tunes, the %%MIDI transpose indication is not that useful +since it transposes all the voices by the specified amount. The +transpose=n subcommand in the K: field tells abc2midi to transpose +a particular voice by n semitones without affecting how it appears in +the printed score. For example for the tune, + +X:1 +T:tranposing +M:1/8 +L:2/4 +K:C +V:1 +K:C transpose=-1 +CDEF|GABc| +V:2 +K:C +CDEF|GABc +V:1 +CDEF|GABc| +V:2 +CDEF|GABc + +Voice 1 will be played one semitone lower than indicated. +These transposition features are disabled for channel 10 which +is reserved for percussion instruments. + + +Voice indication: + +Many multivoiced abc files now use the V: indication to +specify additional information such as the clef, name of +the voice (to appear in the score). Furthermore, the +V: indication may occur in the header (i.e. before the +first K: indication), for the sole purpose of specifying +such information. Abc2midi, yaps and abc2abc now recognizes +the subfields octave=, clef= and transpose= in the V: +field and treats them in the same manner as if they occurred +in a K: field (as discussed above). Thus the V: field may +appear as +V: 1 clef=treble+8 +or +[V: 3 transpose=3] +The V: field is also recognized in both the body and header +of the abc file. Note that not all abc applications may recognize +these extensions, so you use them at the risk that they may not be +treated as expected in some cases. Also in the event that +there is a conflicting indication in either the voice or +key signature field, eg. [V: 1 clef=transpose+8 octave=-1] +the clef indication predominates. + +These transposition features are disabled for channel 10 +which is reserved for percussion instruments. + + + +The Body of the Tune +-------------------- + +Following the header is the tune. This is a textual notation for the things +you might see on a stave : + +Notes: + +A note consists of a pitch specifier followed by a length. Available pitch +specifiers are : + +C, D, E, F, G, A, B, C D E F G A B c d e f g a b c' d' e' f' g' a' b' + +This covers 4 octaves. abc2midi allows lower octaves to be reached by +adding extra , characters and higher octaves to be reached by adding +extra ' characters. However, this is not standard abc and may not be +supported by other abc utilities. + +You can raise or lower the pitch specifier a semitone by preceding it with +^ or _ respectively. The key signature and preceding sharps, flats and +barlines modify the default pitch in the same way as on a stave. Preceding +a note with = generates natural pitch and ^^ and __ can be used for double +sharp and double flat respectively. + +Microtones are indicated by following a ^ or _ with a fraction. +^/C is played as one quarter tone (i.e. half a semitone) above C. +_/C is played as one quarter tone (i.e. half a semitone) below C. +^^/4F is played as 1/4 of a semitone above F#. (The microtone always +follows any accidentals.) In general a microtone offset is indicated +by a fraction using the same convention as note length (described +below). Abcmidi uses the same microtone syntax as abcm2ps. +(See features.txt in the abcm2ps distribution.) + +Note that unlike accidentals, microtones do not propagate across a measure. +Microtones are implemented using the MIDI pitchwheel command. Since the +pitchwheel affects all notes played on a specific channel, a microtone +applied to any note in a chord specified by rectangular brackets, +(eg. [ACE]) will apply to all the notes in the chord. Microtone +accidentals override any sharpening or flattening induced by +key signatures. To illustrate + +K: G +^/F F ^^/2F F| + +does the following. Even though the key of G major causes F to be +sharp, the first note is F natural raised by half a semitone. +This effects the next note in making it F natural instead of F#. +The third note is F# raised by a half a semitone. The F# propagates +to the last note making it also F#. + +In accordance to the General MIDI recommendations the pitch range +of the pitchwheel is set to plus or minus two semitones. Therefore +abc2midi cannot go beyond this range. + +Microtones may be placed between tied notes, producing a pitch +bend effect. eg G- ^/G. + +Microtones have been introduced fairly recently into abcmidi (Mar 2005); +so far there is very little abc notated on the web which exploits +this feature. + + +The length is in general specified by a fraction following the pitch +specifier. However, the notation is made more concise by allowing much +of the fraction to be omitted. + +C - selects a note of 1 unit note length. +C2 - selects a note of 2 unit note lengths. +C/2 - selects a note of 1/2 unit note length. +C3/4 - selects a note of 3/4 unit note length. + +C/ is allowed as an abbreviation of C/2. +C// is allowed as an abbreviation of C/4. However, this is not standard +notation and is not allowed by all abc programs. + +No space is allowed within a note, but space may be used to separate +notes in the tune. + +Rests are written by using 'z' as the pitch specifier. + +z3 - a rest of 3 unit note lengths. + +Multiple bar rests can be created using 'Zn' where n is the number of +bars rest required. This is an extension to the standard syntax. + +Placing a dot before a note causes it to be played staccato. (.e) +Placing a M before a note indicates legato. (Me) +Placing a H before a note indicates a fermata. (He) +Placing a T before a note indicates that this note is trilled. (Te) +Placing a R before a note indicates that this note is rolled. (Re) + +Alternatively you may put !fermata! or !trill! before the +note. Other decorations have not been implemented in this manner. +The fermata may also be applied to a rest, (in order to handle +multivoiced files). + +Combining notes + +Three notes of the same length can be turned into a triplet by prefixing +them with (3. This has the effect of multiplying the note lengths by 2/3. +A chord can be represented by bracketting the notes together within [ and +] e.g. [a2e2]. An older notation which is also supported is to use + symbols +e.g. +a2e2+. The - symbol can be used to tie together notes of the same +pitch e.g. a2-a; this is equivalent to a3. + +From version 1.4 of abc2midi, the support of tuples and chords is +extended to include general tuple notation (p:q:r , [ and ] for chords +and a more flexible system of interpreting chords. + +(p:q:r means play the next r notes at q/p of their notated value. Thus +(3:2:3 is equivalent to an ordinary triplet (3 . + +It is possible in abc to write notes of different lengths within a chord +e.g. [ab2] . In this case, abc2midi takes the length of the first note +(or rest) in a chord is taken as the time before the next note is played. +However, if the music is to be typeset, this notation should be avoided +because the output will be ambiguous. Instead, the same effect can be +achieved using tied notes e.g. [ab2] c is equivalent to [ab-] [bc]. +A tie sign is always assumed to belong to the immediately previous note. + +A run of 2 or more different notes may be grouped together in a slur. +This usually means that the notes are to be played together as smoothly as +possible. In typeset music, a slur looks very similar to a tie, but in +abc, ( marks the start of a slur and ) marks the end. e.g. (ABc) . +abc2midi recognizes slurs, but they have no effect on the MIDI generated. + + + +Barlines and variant endings + +| is an ordinary barline +|| is a double barline +:| is "repeat last section". +|: is "repeat next section". +:: is "repeat last and next sections". +|1 or |[1 or | [1 is "first repeat ending". +:|2 or :|[2 or :| [2 is "second repeat ending". +|[1,3 is first and third ending +:|[2,4 is second and fourth ending +|] and [| are variants of ||. + +The notation :: is short for :| followed by |: . + +A tune with different ending for the first, second and third repeats has the +general form: + +|: common body of tune |1 first ending :|2 second ending :|3 third ending || + +You may also use this notation to indicate that the first ending is +played on the first and third repeat. For example + +|:common body of tune|1,3 first ending :|2 second ending :|4 forth ending| + +(The last variant ending should not end with a :|. In other words, +this will not work correctly. |: ... |[1,2 :|.) + +For multivoice abc files, you should be careful that the +different voices share the same repeat structure. When switching +voices (assuming they are interleaved) abc2midi does not cache +the repeat state. + + +Printed music commonly misses out a start repeat at the beginning of a tune. +abc2midi will try to fix up things if you miss out a start repeat in single +voice music. Where a tune starts with an anacrusis, abc2midi will always +fix a repeat to start at the anacrusis rather than the first barline. It is +recommended that you use matching start and end repeats rather than rely on +this behaviour. Missing start repeats are not supported in multiple voice +music. Also, abc2midi does not allow nested repeats. However, you can use +the more versatile part notation to achieve multiple repeats. + +It is not unusual to see music where a repeat does not coincide with the +end of a bar and the number of beats is not quite correct if the piece is +played exactly as written. A human player usually knows enough to correct +the mistake themselves, but abc2midi will play exactly what is written, so +care needs to be taken that this is corrected when the piece is transcribed +to abc. + + + +Rhythm field and Broken Rhythm Notation +--------------------------------------- + +R:hornpipe causes notes written in straight time to be played in dotted +time. The symbol > can be used to achieve a similar effect. For 4/4 +time this is applied to the 1/8 notes. For 2/4 time this is applied +to the 1/16 notes. + +a>b is notated as a3/2b/2 but played as a4/3b2/3. + +The symbols < >> << >>> <<< have similar meanings: + +a>b is notated and played as a7/4b/4. +a<>>b is notated and played as a15/8b/8. +a<< using the %%MIDI ratio command +described later. + +Beware that attempting "advanced" use of these symbols may mean your abc is +not portable between different abc programs. If the notes on either side of +the symbol are different lengths, this is reported as an error. If a and +b are not simple notes, or if there are other complications, then it is +safer to write the note lengths directly. + +Guitar chords : + +Anything in quotes is a guitar chord e.g. + +"A" "Gm" "B7" "Bm7" "D#aug" "Bbdim7" + +Guitar chords must use upper case A-G followed by optional # or b, then +the name of a chord type e.g. "m", "aug", "7". abc2midi currently +recognizes the following chord names : + + m, 7, m7, maj7, M7, 6, m6, aug, +, aug7, dim, dim7, 9, m9, maj9, M9, +11, dim9, sus, sus9, 7sus4, 7sus9, 5 + +You can also add your own; see the %%MIDI chordname command below. + +abc2midi automatically generates an accompaniment from the guitar chords. +There are a number of default chord/fundamental rhythms for common time +signatures which this uses, or you can set up your own. + +Lower case a-g followed by optional # or b will generate a single note, +the fundamental, only. + +The chord notation also allows chords such as "G/B" or "G/b". The +note following the / is interpreted in one of two ways: + +If the note following / does not exist as part of the given chord, it +is added to the chord below the root note of the chord (G in the above +example). + +If the note following / does exist in the chord, then the notes of the +chord are re-arranged so that it becomes the lowest pitch note of the +chord. This is known as an inversion of the chord. It does not matter +whether the note following the / is upper or lower case. They are +both treated the same. + +This notation has been extended so that finger numbers "1" "2" "3", +"4" or "5" are allowed. abc2midi ignores these, but a typesetting +program might support them. + +You may find some abc tunes that abuse this notation and use quotes +for things that are not guitar chords. Usually these are +tunes which have been typeset but never played by a computer. If you +don't want to just delete things in quotes, you can insert one of the +characters _, ^, @, < or > after the first quote e.g. "_Chorus" +"_Very Loud". This causes abc2midi to ignore the following text. However, +typesetting programs should recognize the first character as telling +them where to print the following text. + +Another extension is to allow musical instructions to appear in exclamation +marks e.g. !pizzicato! abc2midi currently supports the following : + !ppp! !pp! !p! !mp! !mf! !f! !ff! !fff! +Default volume is equivalent to !f!. In both the " " and ! ! fields, +abc2midi allows multiple terms separated by semi-colons e.g. "Am;1". + +The !breath! instruction causes the note to be played half length +followed by a rest of half its length (just like staccato). + +The !arpeggio! instruction affects the next chord and introduces +a larger delay between the onset of each note in the chord. (See +the text preceding %%MIDI chordattack in this file for more +explanations.) + +The !crescendo(! and !crescendo)! delineate the start and +end of a crescendo. Alternatively, you can use !<(! and !<)!. +Abc2midi does not implement a gradual loudness increase, +but instead increases the loudness at the start and end +of the crescendo by a fixed amount. (See %%MIDI beatmod +and %%MIDI deltaloudness for more information.) + +The !diminuendo(! and !diminuendo)! behave similarly but +reduce the loudness. These instructions have not been +implemented in yaps. + +!ped! and !ped-end! press and release the sustain pedal on +the piano. (The notes following !ped! and preceding !ped-end! are +held. This effect applies to all instruments besides the piano.) + +A line of music may contain any number of notes, barlines and guitar +chords. Spaces may be used to separate these. + +Some abc fields may appear within the body of the abc tune : + +K: - change key +L: - change unit note length +M: - change meter +Q: - change tempo +P: - part label +V: - voice label +w: - words to be matched syllable by syllable to notes + +Each field must be on a line by itself. + +Part Notation +------------- + +A part label must be a single character in the range A - Z. e.g. + +P:A + +A parts specifier in the header can be used to define the MIDI output as +some combination of the specified parts e.g. + +P:ABACABA + +You can use ( ) to repeat a part a specified number of times +e.g. P:A(AB)6 is equivalent to P:AABABABABABAB. If there are no brackets, +just the last part is repeated, so P:AAB3 is equicalent to P:AABBB. Dots +may be inserted into the part specifier to make it easier to read +e.g P:A.AB.AC + +If there is no parts specifier, the output is simply the parts (or just +the unlabelled music) in the order in which it appears in the tune body. + +You may if you wish have multiple voices sounding concurrently within each +part. These are indicated with V:N to indicate voice number N. + +e.g. + +V:1 + +V:2 + +and so on. + +A part label implicitly starts with V:1, as does the K: field which starts +the tune body, so you are not allowed to place a part label within a voice. +The duration of each of the voice parts must be the same for them to +synchronize correctly; abc2midi will warn you if they are not! From version +1.7 onwards, abc2midi allows any voice apart from voice 1 to be completely +omitted from a part. + +If no V: field appears after the first K:, whatever follows is assumed to +belong to voice 1 (the default voice). The V: field is an extension to +abc 1.5. + +The lines between one V:N field and the next V: field define a region +belonging to voice N. Within a part, you may have more than one such +region for each voice. The music for voice N within a part is all +the voice N regions taken in sequence. + +The M:, L: and K: fields in the header apply to all voices. After +the header, these fields apply only to the voice in which they appear. +In previous versions of abc2midi, an L:, for example, would affect +everything after it appeared up until the next L:, regardless of what +voice changes there were. + +The Q: tempo field should only appear in voice 1 and applies to all voices. + +The U: abbreviation field has limited use here. You can specify +an abbreviation such as +U: P = !trill! +and then in the body, eg. |DGAPF|, P will be replaced with !trill! +Only letters between H and Z can be used. Furthermore, you can +not redefine reserved letters such as H (fermata), L (unit length), +M (mordent) , R (roll) , T (trill). + + +Adding Lyrics to a Tune +----------------------- + +The W: field (upper case W) can be used for lyrics to be typeset separately +if the abc is printed out. The w: field (lower case w) in the body of a tune +supplies a line of lyrics to be matched syllable by syllable to the last line +of notes. These are usually printed below the notes if the abc is typeset. +abc2midi uses these to generate karaoke MIDI files. A karaoke MIDI file can +contain more than one set of lyrics in separate voices; an example is shown +at the end of this section. By default the lyrics are embedded in the same +MIDI track as the notes. If the runtime parameter -STFW is included, +then the lyrics will be placed in separate and adjoining MIDI tracks. +When the karaoke MIDI file is played using an appropriate player program, +the lyrics appear on the screen with the current syllable highlighted. +Within the lyrics, the following symbols may be used : + + break between words +- break between syllables within a word +| advance to next bar +_ indicates last syllable is to be held for another note +* indicates a one note rest for the singer. +~ appears as a space but connects syllables each side into one. +\- appears as - in the output +\ continuation character. Next w: field is part of the same line. + +A rest is not matched by any lyrics. +A tied note e.g. d2-d2 is treated as 2 notes (or however many parts it +is written as), despite the fact that it only plays as a single note. + +abc2midi ignores space characters if they occur either (a) between the +end of a word/syllable and a hyphen or underscore or (b) between a hyphen or +underscore and the beginning of a word/syllable. However, some software +incorrectly treats a hyphen as a separate word if there is a space between +it and the previous syllable, so, for example, you should write go-ing and +not go - ing to ensure that your abc is portable between programs. +Here are some examples taken from +http://abc.sourceforge.net/standard/abc2-draft.html + +w: syll-a-ble is aligned with three notes +w: syll-a--ble is aligned with four notes +w: syll-a -ble (equivalent to the previous line) +w: time__ is aligned with three notes +w: of~the~day is treated as one syllable (i.e. aligned with one note) + but appears as three separate words + + + +The following example illustrates most of these : + + gf|e2dc B2A2|B2G2 E2D2|.G2.G2 GABc|d4 B2 +w: Sa-ys my au-l' wan to your aul' wan\ +w: Will~ye come to the Wa-x-ies dar-gle? + +Note that the continuation character is used in a rather strange manner. +One w: field and all continuations will match one line of music, whether +or not the line of music ends with a continuation character. You can +think of the \ in a music line dividing that line into sections and \ +in a w: field further dividing these section into sub-sections. + +It is possible for a music line to be followed by several w: fields. +This can be used together with the part notation to create verses. +The first w: field is used the first time that part is played, then +the second and so on. If the tune uses repeats, these must be placed +at the end of a line of music in order to make sure that the start of +a w: field matches up with the repeat. + +Multivoiced lyrics example: + +X:1 +T:Multivoiced lyrics +M:2/4 +L:1/16 +K:C +V:1 +C4 C4 | E4 G4 | c8 |] +w: 1 2 3 4 5 +V:2 +C4 E4 | C4 B,4 | C8 |] +w: 11 12 13 14 15 + + +BarFly stress models +-------------------- + +If you include the run time parameter -BF in execution string, abc2midi +will attempt to apply the BarFly stress model on the tune if it +recognizes the rhythm designator (eg. R: jig) and if the time signature +also matches the associated meter. There are two different implementations +of the stress model which have different effects. (See %%MIDI ptstress +below for more details.) You can specify the implementation to use by +following the -BF flag with either the numeral 1 or 2. If you do not +specify a model, the program will use model 2. More details can +be found on http://ifdo.ca/~seymour/runabc/abcguide/abc2midi_guide.html + + +abc2midi-specific commands (%%MIDI) +------------------------------------ + +abc2midi supports a number of commands of the form + +%%MIDI command + +Each of these should appear on a line by itself; however there +is now provision to pass the MIDI command in an inline I: field +(see CHANGES file, March 25 2005 entry.) All of them are allowed +within the abc tune body. By using these in combination with the part +notation, one can, for example, play a part transposed or in a different key. + +The idea behind this syntax is that other programs will treat it as a +comment and ignore it. + +%%MIDI channel n + +selects melody channel n (in the range 1-16). + +%%MIDI program [c] n + +selects program n (in the range 0-127) on channel c. If c is not given, the +program is selected on the current melody channel. Most modern tone +generators follow the General MIDI standard which defines the instrument +type for each program number. These instrument types are listed at the end +of this document. Note that for multivoiced files, the program command +is placed in the track associated with the voice previously declared. +If the %%MIDI indications affect channels in other tracks, it is +recommended that they are placed in the first track or first declared voice. +See the note in the CHANGES file for the date January 1 2005. + + +%%MIDI beat a b c n + +controls the way note velocities are selected. The first note in a bar has +velocity a. Other "strong" notes have velocity b and all the rest have velocity +c. a, b and c must be in the range 0-127. The parameter n determines which +notes are "strong". If the time signature is x/y, then each note is given +a position number k = 0, 1, 2 .. x-1 within each bar. Note that the units for +n are not the unit note length. If k is a multiple of n, then the note is +"strong". The volume specifiers !ppp! to !fff! are equivalent to the +following : + +!ppp! = %%MIDI beat 30 20 10 1 +!pp! = %%MIDI beat 45 35 20 1 +!p! = %%MIDI beat 60 50 35 1 +!mp! = %%MIDI beat 75 65 50 1 +!mf! = %%MIDI beat 90 80 65 1 +!f! = %%MIDI beat 105 95 80 1 +!ff! = %%MIDI beat 120 110 95 1 +!fff! = %%MIDI beat 127 125 110 1 + +%%MIDI beatmod n +increments by n (or decrements if n is negative) the velocities a, b and +c described above. It is also used in implementing crescendo and +diminuendo (eg. !<(!, !crescendo(! etc.) + + +%%MIDI nobeataccents + +For instruments such as church organ that have no greatly emphasized beat +notes, +using this will force use of the 'b' velocity (see %%MIDI beat) +for every note irrespective of position in the bar. This allows dynamics +(ff, f, etc) to be used in the normal way. + +%%MIDI beataccents + +Revert to emphasizing notes the the usual way. (default) + + +%%MIDI deltaloudness n +where n is a small positive number. By default the crescendo and +dimuendo instructions modify the beat variables a, b, and c by +15 velocity units. This instruction allows you to change this default. + +%%MIDI beatstring + +This provides an alternative way of specifying where the strong and weak +stresses fall within a bar. 'f' means velocity a (normally strong), 'm' +means velocity b (medium velocity) and 'p' means velocity c (soft velocity). +For example, if the time signature is 7/8 with stresses on the first, fourth +and sixth notes in the bar, we could use the following + +%%MIDI beatstring fppmpmp + + +%%MIDI transpose n + +transposes the output by the specified number of semitones. n may be +positive or negative. + +%%MIDI rtranspose n + +Relative transpose by the specified number of semitones. i.e. +%%MIDI transpose a followed by %%MIDI rtranspose b results in a +transposition of a+b. %%MIDI transpose b will result in a transposition +of b semitones, regardless of any previous transposition. + +%%MIDI c n + +specifies the MIDI pitch which corresponds to c. The default is 60. This +number should normally be a multiple of 12. + +%%MIDI grace a/b + +sets the fraction of the next note that grace notes will take up. a +must be between 1 and b-1. The grace notes may not sound natural +in this approach, since the length of the individual grace notes +vary with the complexity of the grace and the length of the +following note. A different approach (which is now the default) +assumes that the grace notes always have fixed duration specified +by a fraction of the unit length. To use the other approach +you would specify, + +%%MIDI gracedivider b + +where b specifies how many parts to divide the unit length +specified by the L: field command. For example if b = 4 and +L: = 1/8, then every grace note would be 1/(8*4) or a 32nd +note. Time would be stolen from the note to which the grace +note is applied. If that note is not long enough to handle +the grace then the grace notes would be assigned 0 duration. + +%%MIDI chordname name n1 n2 n3 n4 n5 n6 + +Defines how to play a guitar chord called "name". n1 is usually 0 and +n2, n3 to n6 give the pitches of the other notes in semitones relative +to the root note. There may be fewer than 6 notes in the chord, but not +more.If "name" is already defined, this command re-defines it. Unlike +most other commands, chordname definitions stay in effect from where they +are defined to the end of the abc file. The following illustrates how +m, 7, m7 and maj7 could be set up if they were not already defined. +%%MIDI chordname m 0 3 7 +%%MIDI chordname 7 0 4 7 10 +%%MIDI chordname m7 0 3 7 10 +%%MIDI chordname maj7 0 4 7 11 + +%%MIDI gchord string + +sets up how guitar chords are generated. The string is a sequence made of +of z's, c's f's and b's for rests, chords, fundamental and fundamental +plus chord notes respectively. This specifies how each bar is to be played. +An optional length is allowed to follow the z's, c's, f's and b's e.g. czf2zf3. +If the abc contains guitar chords, then abc2midi automatically adds chords and +fundamentals after encountering the first guitar chord. It keeps using that +chord until a new chord is specified in the abc. Whenever the M: field is +encountered in the abc, an appropriate default string is set : + +For 2/4 or 4/4 time default is equivalent to : +%%MIDI gchord fzczfzcz + +For 3/4 time default is equivalent to : +%%MIDI gchord fzczcz + +For 6/8 time default is equivalent to : +%%MIDI gchord fzcfzc + +For 9/8 time default is equivalent to : +%%MIDI gchord fzcfzcfzc + +Please note, that the default gchord string is reissued any +time a time signature change is specified in the body of the music. +This means if one of the bars has an extra beat you included +a M: declaration before and after the measure, the gchord string +would be reset to the default string for that time signature +and not the one that you had declared. It is necessary for +you to send another %%MIDI gchord declaration after the +the time signature in order to set this back the way you want +it. This is one of the changes introduced into abc2midi so +that the accompaniment track always follows the meter of the +music for the regular time signatures. + +The gchord command has been extended to allow you to play +the individual notes comprising the guitar chord. This allows +you to play broken chords or arpeggios. The new codes g,h,i,j, +G,H,I,J reference the individual notes starting from the +lowest note of the chord (not necessarily the root in the +case of inverses). For example for the C major chord, g +refers to C, h refers to E and i refers to G. For a gchord +command such as, + +%%MIDI gchord ghih + +Abc2midi will arpeggiate the C major guitar chord to +CEGE. The upper case letters G,H,I, and J refer to +the same notes except they are transposed down one +octave. Note for the first inversion of the C major +chord (indicated by "C/E"), E would be the lowest +note so g would reference the note E. + +Like other gchord codes, you may append a numeral indicating +the duration of the note. The same rules apply as before. +You can use any combination of the gchord codes, +(fcbghijGHIJz). + +Another recent extension to gchords is the presence +of gchords in separate voices. Here is an example: + +X:1 +T: gchord multivoice extension +M: 4/4 +L: 1/4 +K: G +V: 1 +%%MIDI gchord ghih +"G" z4| z4|\ +%%MIDI gchordoff +z4| +V:2 +%%MIDI chordprog 12 +%%MIDI gchord GHIHG +z4|"D" z4|z4| + + + + +%%MIDI chordprog n + +Sets the MIDI instrument for the chord notes to be n. If the +command includes the string octave=n where n is a number between +-2 and +2, then the chord notes will be shifted n octaves from its +usual position, eg. (%%MIDI chordprog 32 octave=1). Any other +descriptors will be ignored, eg (%%MIDI chordprog 0 Acoustic Piano). + +%%MIDI bassprog n + +Sets the MIDI instrument for the bass notes to be n. If the +command includes the string octave=n where n is a number between +-2 and +2, then the bass note will be shifted n octaves from its +usual position. eg. (%%MIDI bassprog 32 octave=-1). + +%%MIDI chordvol n + +Sets the volume (velocity) of the chord notes at n. + +%%MIDI bassvol n + +Sets the volume (velocity) of the bass notes at n. There is no corresponding +melodyvol command since there are 3 velocity values for melody, set using the +beat command. + +%%MIDI gchordon + +Turns on guitar chords (they are turned on by default at the start of a +tune). + +%%MIDI gchordoff + +Turns off guitar chords. + +%%MIDI fermatafixed +Directs abc2midi to expand a fermata by one unit length. +Thus HC3 becomes C4. + +%%MIDI fermataproportional +This is the default. A fermata doubles the length of a +note so HC3 becomes C6. + +%%MIDI droneon + +This turns on a continuous drone used in bagpipe music. The +drone consists of two notes (by default A, and A,,) played +on a bassoon at a MIDI loudness (velocity) 80. If you +can configure the drone sound using the %%MIDI drone command +described below. + +%%MIDI droneoff + +This turns off the drone. + +%%MIDI drone n1 n2 n3 n4 n5 + +Configures the drone chord. n1 = MIDI program, +n2 = MIDI pitch 1, n2 = MIDI pitch 2, n4 = MIDI velocity 1, +and n5 = MIDI velocity 2. By default they have already +been set to 70 45 33 80 80. + + + +%%MIDI drum string [drum programs] [drum velocities] + +This sets up a drum pattern. The string determines when there is a drum beat +and the drum program values determine what each drum strike sounds like. + +e.g. %%MIDI drum d2zdd 35 38 38 100 50 50 + +The string may contain 'd' for a drum strike or 'z' for a rest. By default +a voice starts with no drum pattern. Like gchord, a command + +%%MIDI drumon + +is needed to enable the drumming. The drum pattern is repeated +during each bar until a + +%%MIDI drumoff + +is encountered. The %%MIDI drum command may be used within a tune +to change the drum pattern. This command places the drum sounds on +channel 10 and assumes your tone generator complies with the +General Midi standard - if it does not, then you may hear tones +instead of drum sounds. (Note the old method of using the +instruction !drum! and !nodrum! is being deprecated.) + +In both the gchord and drum commands, the standard note length of +a single note f,c,z or d is not set by the L: command. Instead it +is adjusted so that the entire gchord string or drum string fits +exactly into one bar. In other words the duration of each note +is divided by the total duration of the string. This means that, +for example, the drum string "dd" is equivalent to drum string "d4d4". +You cannot currently specify fractions directly (eg. C3/2) +as done in the body of the music, but it is still possible to express +complex rhythms. For example, to indicate a rhythm such as +(3ddd d/d/d/d, you would write the string "d4d4d4d3d3d3d3". + +For reference, the percussion instruments defined in the General MIDI +standard are given below. + +35 Acoustic Bass Drum 59 Ride Cymbal 2 +36 Bass Drum 1 60 Hi Bongo +37 Side Stick 61 Low Bongo +38 Acoustic Snare 62 Mute Hi Conga +39 Hand Clap 63 Open Hi Conga +40 Electric Snare 64 Low Conga +41 Low Floor Tom 65 High Timbale +42 Closed Hi Hat 66 Low Timbale +43 High Floor Tom 67 High Agogo +44 Pedal Hi-Hat 68 Low Agogo +45 Low Tom 69 Cabasa +46 Open Hi-Hat 70 Maracas +47 Low-Mid Tom 71 Short Whistle +48 Hi Mid Tom 72 Long Whistle +49 Crash Cymbal 1 73 Short Guiro +50 High Tom 74 Long Guiro +51 Ride Cymbal 1 75 Claves +52 Chinese Cymbal 76 Hi Wood Block +53 Ride Bell 77 Low Wood Block +54 Tambourine 78 Mute Cuica +55 Splash Cymbal 79 Open Cuica +56 Cowbell 80 Mute Triangle +57 Crash Cymbal 2 81 Open Triangle +58 Vibraslap + +Note you are able to change this mapping using the +the MIDI command %%MIDI drummap described below. + + +%%MIDI drummap note midipitch + +where the pitch of the note is notated using abc notation +and midipitch is a number between 35 and 81 inclusive referring +to the above table. This command is used if you +are notating a drum track, i.e. a voice played on +channel 10. Rather than being forced to use the note +corresponding to the desired percussion instrument, (for +example C (MIDI pitch 60) for hi bongo, you can use can +change the mapping to use a more convenient pitch. +for example to access bass drum 1 (MIDI pitch 36) you +would require the note C,, which is awkward to display +in common music notation. You can change the mapping to +say _D using +%%MIDI drummap _D 36. + +An example is provided in the file CHANGES (November 6 2005). + + +%%MIDI drumbars n + +The %%MIDI drum line can sound quite monotonous if it is repeated +each bar. To circumvent this problem a new MIDI command +%%MIDI drumbars n +where n is a small number will spread out the drum string over +n consecutive bars. By default drumbars is set to 1 maintaining +compatibility with existing abc files. You should take +care that the drumstring is evenly divisible between the +drumbar bars. Also the time signature should not change +between bars in a drumbar unit. (Sample abc file in CHANGES +June 24 2008.) + +%%MIDI gchordbars n + +This command spreads the gchord string over n consecutive bars +of equal length. The gchord string should be evenly divisible +by n or else the gchords will not work properly. A sample +abc file is found in CHANGES March 17 2009. + +%%MIDI control [bass/chord] n1 n2 + +This generates a MIDI control event. If the word "control" is followed by +"bass" or "chord", the event will be applied to the bass or chord channel, +otherwise it will be applied to the current channel. n1 and n2 are numbers +in the range 0-127. Generally, n1 selects a control parameter and n2 is +the value to which it is set. A couple of examples : + +%%MIDI control 7 50 + +will set the main volume of the channel to 50 + +%%MIDI control 10 0 + +will set the pan parameter (left/right balance) to 0. + +See the manual for your MIDI tone generator to find out what control events +are supported. + +%%MIDI portamento [bass/chord] n + +This will turn on the MIDI portamento controller and set the speed +of sliding between pitches to n. Like %%MIDI control, if the word +portamento is followed by "bass" or "chord", the event will be +applied to the bass or chord channel, otherwise it will be +applied to the current channel. The parameter n should be between +0 and 63. Large values imply a slow transition between pitches. +I have found the resulting effect to be rather weird, especially +for large pitch intervals. + +%%MIDI noportamento [bass/chord] + +This will turn off the portamento controller (current default). + +%%MIDI pitchbend [bass/chord] + +This generates a pitchbend event on the current channel, or on the bass +or chord channel as specified. The value given by the following two bytes +indicates the pitch change. + +%%MIDI nobarlines + +This is a somewhat obscure option to support early music without barlines. +Normally, an accidental applied to one note e.g. ^c will apply to every note +at the same point in the scale until the end of the bar (so C,, C, C c c' +would all be sharpened). This option turns off this behaviour, so that an +accidental applies only to the next note. It should be used in the header of +any tune requiring this behaviour. + +%%MIDI barlines + +This turns off the effect of %%MIDI nobarlines in the middle of a tune. This +is the default behaviour assumed at the start of every tune. + +%%MIDI ratio n m + +This sets the ratio of note lengths in broken rhythm (e.g. a>b). +The default behaviour is for note a to sound for twice as long as note b. +This can be achieved with + +%%MIDI ratio 2 1 + +and hornpipes are commonly played with approximately this ratio. However, +for other musical styles, a different ratio may be appropriate. If you +are using abc2midi to export music to another program for printing, then +you may wish to use a ratio of 3:1 which is how hornpipes are usually +notated. This can be achieved with + +%%MIDI ratio 3 1 + + +Arpegiation: + +With version 1.54 Dec 4 2004 of abc2midi, notes in chords +(eg. [FAc]) are not played in the same instant but offsetted +and shortened by 10 MIDI time units. Thus the first note +in the chord (eg. F) is played for the full indicated time, +the second note (eg. A) starts 10 MIDI units later and is shortened +by the same amount and the third note starts another 10 MIDI +units later and is shortened by another 10 units. This introduces +an "expressivo" option and avoids the heavy attack. (This +does not apply to gchords or multivoiced chords.) The amount +of the delay and shortening may be configured by the MIDI command + +%%MIDI chordattack n + +where n is a small number. If n is zero, then abc2midi should +behave as in earlier versions. The delay n is in MIDI time units +where there are 480 units in a quarter note beat. The program +may not run correctly if n is too large and there are short +chords. + +%%MIDI randomchordattack n + +where n is a small number. This is similar to above, except the +delay of each note in the chord varies randomly between 0 and n-1. +Therefore each chord is played differently. + + +Articulation: + +%%MIDI trim x/y + +where x and y are two numbers. This command controls the articulation +of notes and chords by placing silent gaps between the notes. The length +of these gaps is determined by x/y and the unit length specified by the L: +command. These gaps are produced by shortening the notes by the same amount. +If the note is already shorter than the specified gap, then the gap +is set to half the length of the note. The fraction x/y indicates +a note duration in the same manner as specified in the abc file. +The actual duration is based on the unit length specified by the +L: field command. It is recommended that x/y be a fraction close +to zero. The denominator, y does not need to be a power of 2. +Trimming is disabled inside slurs as indicated by parentheses. + +You can turn off all note trimming by setting x to 0, +eg 0/1. By default, note trimming is turned off at the beginning +of a tune or voice command. + + + + +To avoid the problem of breaking up a music line +in order to place a %%MIDI command, for example, +A2 B2|\ +%%MIDI drumon +C2 D2| +you can place the MIDI instruction inside an info field +using the following syntax. + +A2 B2|[I:MIDI= drumon] C2 D2| + +The '=' following the MIDI is very important. The info field +syntax allows you to place several MIDI commands in one inline +block, for example +[I: MIDI = program 73 MIDI=chordprog 29] +(Spaces are optional.) +Other examples can be seen in the file CHANGES in the +following the March 25 2005 entry. + +Another recent extension is the allowance of drum patterns +in separate voices. Here is an example: + +X:1 +T: drum multivoice extension +M: 4/4 +L: 1/4 +K: G +V:1 +%%MIDI drum dddd 45 45 45 45 70 50 60 50 +%%MIDI drumon +z4|z4|\ +%%MIDI drumoff +z4| +V:2 +%%MIDI drum ddddd 54 54 54 54 54 70 50 50 60 50 +z4|\ +%%MIDI drumon +z4|z4| + + + + +%%MIDI temperamentlinear octave_cents fifth_cents + +This command allows you to change the temperament of the +scale. Octave_cents specifies the size of an octave +in cents of a semitone or 1/1200 of an octave. Fifth_cents +specifies in the size of a fifth (normally 700 cents). +For example: +%%MIDI temperament 1200.5 698.0 +will produce a slightly stretched scale with narrowed fifths. + +More details on linear tempered scales can be found +on the site +http://www.xs4all.nl/~huygensf/scala/ +The pitch of each note is modified using a MIDI pitchbend +command to comply with the scale. In order to handle chords, +each note of the chord must be played on a different MIDI +channel. + +The normal musical scale has a temperament of +%%MIDI temperamentlinear 1200 700 + +but to avoid unnecessary pitchbends you should restore +to the normal temperament using the command +%%MIDI termperamentnormal + + +%%MIDI tuninsystem comma53 +quantizes the pitches of the notes using the comma53 system where +an octave is divided into 53 equally spaced tones. + + +%%MIDI makechordchannels n + +If you are not modifying the temperament or inserting +microtones you can ignore this command. Normally +in voice chords containing microtones e.g. [_/CE_/G] +will not be handled correctly because all the notes in the +chord are played on the same MIDI channel. If you include +the above command where n here is 2, then two separate channels +will be allocated for playing the other two notes in this chord. +These separate channels will be used whether the pitches +of the notes are bent or not. Once you allocate those channels +with this command, they are no longer available and since +there are only 16 MIDI channels you can easily run out. +Furthermore, you need to allocate chordchannels for any +other voices which may have in voice chords. The channel +allocation process will automatically propagate the program +assignment (musical instrument) to the other chordchannels, +so be sure you set the program before using this command. +If you need to change the program assignments, you can +find out the channel numbers that were assigned by running +abc2midi in verbose mode using the -v command in the +execution string. + + +%%MIDI ptstress filename + +This command loads file filename into abc2midi which contains +the Phil Taylor stress parameters and puts abc2midi in the mode +where it applies these stress parameters on every note. This +model runs in opposition to the standard beat model, so the +MIDI beat, beatstring, beatmod commands become ineffectual. +This also means that the dynamic indications !f! !pp! etc. +do not work any more. + +There are two different implementations of the stress model. +Model 1 modifies the note articulation and takes +control of the MIDI trim parameters too. To revert back to +the standard model, put the command %%MIDI beataccents. +Model 2 modifies both the onset and ending of each note +allowing a musical beat to expand or contract in time. However, +the length of a musical measure should be preserved. Note +if you using model 2, which the current default, you must +include -BF as one of the runtime parameters of abc2midi. + +The model divides a bar into equal segments. For each segment, +a loudness or MIDI velocity is specified and a duration multiplier +is specified. If a note falls into a specific segment, it assumes +the velocity of that segment and its duration is modified +accordingly. If the note overlaps more than one segment, then +the note assumes the average of those segment values. + +The input file specifies the number of segments and the loudness +and duration multipliers for each segment. The file has the +following format: + +8 +110 1.4 +90 0.6 +110 1.4 +90 0.6 +110 1.4 +90 0.6 +110 1.4 +90 0.6 + +where the first value is the number of segments and each line +specifies the velocity and duration multiplier of the specific +segment. The velocity is limited to 127 and the duration is a +decimal number. The note durations is modified by varying the +gap between notes, so it is not possible to extend a note. This +preserves the regular tempo of the music. The program scales, +the note duration indications by dividing it by the maximum +value which here is 1.4. + + +%%MIDI stressmodel n + +where n is either 1 or 2, selects the stress model implementation. + + +%%MIDI expand m/n + +This command,causes all the following notes be lengthened by +the amount factor m/n so that they overlap the next note. +Thus the next note is started at the proper time, but the +previous note ends after the beggining of the note. +This overlap causes a nice effect for particular MIDI instruments +such as choir voices. The %%MIDI expand function behaves in +the opposite manner as the %%MIDI trim function. + + +%%MIDI snt k pitch + +Since many MIDI devices do not support this function, use of +this command is not recommended. The command changes the tuning +of a single note using the "universal system exclusive messages". +k is the MIDI pitch being retuned (a number between 0 to 127) +and pitch is a floating point number representing the pitch's new +value in MIDI pitch units. + + +%%MIDI bendvelocity n1 n2 + +This command defines how to bend the a particular note following +the !bend! decoration. The pitch of the note shifts up or down +as the note is playing. n1 is the velocity of the pitch change +and n2 is the acceleration (how fast the velocity changes). +The bend is accomplished by modifying the MIDI pitchwheel where +the 8192 is the neutral. Minimum and maximum values +are 0 and 16383 correspond to two semitones shifts in either +directions. The velocity and acceleration may be positive or +negative. + + +%%MIDI bendstring n1 n2 n3 n4 etc + +This command is a more general way for defining how a note +is bent. It can also apply with !shape! decoration +discussed later. The note is split into n equal segments corresponding +to each of the n1, n2, etc values. The n1, n2, ... values are the +increments (or decrements if they are negative) which are added +to the pitchwheel value. Thus +%%MIDI bendstring 1000 1000 -500 -500 +will split a note into 4 parts and the pitchwheel values in +each part will be 9192, 10192, 9692, and 9192. + + + +%%MIDI controlstring m n1 n2 n3 ... + +This command defines how the m th MIDI controller changes for the +note following the !shape! decoration. The note is divided +into n segments where n is the number of values following +the m or controller number. n1, n2, n3, ... are the values for +controller m in each segment. This allows you to shape the sound +of the note. For example, by changing the modwheel or the expression. +All the values m, n1, n2 are numbers between 0 and 127. + + + + + + +Global settings for abc2midi +---------------------------- + +If you are creating an abc file with many tunes, abc2ps and abcm2ps +allows you to declare certain settings that apply to all tunes +by placing them at the beginning of the file prior to the +start of the tune. Abc2midi provides this feature but presently +only to a limited extent. The following MIDI commands will +change the defaults for all tunes if they are placed outside +of any tune. + +%%MIDI C ... +%%MIDI nobarlines ... +%%MIDI barlines ... +%%MIDI fermatafixed +%%MIDI fermataproportional +%%MIDI ratio ... +%%MIDI chordname ... +%%MIDI deltaloudness ... + +All other MIDI commands placed outside of a tune will be +ineffective and return a warning message + +"cannot handle this MIDI directive here" + +(For more details see CHANGES, May 06 2005 entry.) + +Any of these defaults can be changed as many times as you like +provided that they are occur outside a tune which is usually +delineated by a X: reference number and a blank line. + +Voice Splitting +--------------- + +Abcm2ps allows a voice to separate into two or more voices +in a specific bar using the symbol '&'. This feature +now works in abc2midi. Abc2midi places the split voice +into a separate MIDI track with intervening rests. +When a voice splits, the new voice inherits the program +number (musical instrument) from the parent voice. + + +Compatibility with proposed abc standard version 2.0 +---------------------------------------------------- + +The proposed standard introduces a new copyright field +using the syntax + +%%abc-copyright (c) Copyright John Smith 2003 + +Abc2midi now inserts this in the MIDI file in the form of a +metatext copyright tag. Changes were made to the event_specific +function in store.c to process the copyright information. It +is also copied into the Karaoke track (if it is created) as +as @T field. + + + + +Typesetting abc +--------------- +If you want to typeset your abc, there are some more features of abc syntax +that you need to know: + +If 2 notes appear consecutively with no space between them, they will be +grouped together under the same beam. A space between them prevents them +sharing a beam. + +A new line of stave music is generated by the newline at the end of a +line of abc music. To suppress this, put a \ character at the end of the +abc music line. + +An abc music line should end either at a bar line or at the space +between two notes which indicates they do not share a beam. This is true +whether or not the abc line ends with a \ character. + +Error Messages and Warnings +--------------------------- + +abc2midi attempts to perform various checks on the abc and +reports any problems via error and warning messages: + +A warning message indicates there is something strange in the abc - +possibly an error or possibly non-standard usage. + +An error message means that abc2midi thinks there is definitely an +error in the abc and the MIDI generated may not be correct. + +In a small number of cases, an error may cause abc2midi to stop. +This is usually either because it has run out of memory or because +there is some problem with reading or writing a file. + + + +Bar counting and checking +------------------------- + +Conventionally bars are numbered starting from one. If the first +bar is incomplete (anacrusis), then it is counted as zero. +Abc2ps and abcm2ps follows this convention but abcMIDI does not. +All bars are counted starting from zero and furthermore if there +is an incomplete bar just before a repeat (:| |: or ::), the +bar count is not incremented. It is difficult to change this +convention since abcMIDI assigns a number to the bar ahead +when it sees a bar line. It would be necessary to introduce +some look ahead for the first bar in the tune. + +If abc2midi reports a problem in a specific bar, you can use +yaps with the -k option (for print bar numbers) to locate this +bar. If you use another program such as abc2ps, then the bar +number in the displayed or printed version may be one unit +higher. + +The most common error seems to be a missing beat or having +an extra beat in a bar. In the vicinity of a repeat, abc2midi +tries to complete the first bar using the incomplete bar at +the end of the repeat. If the first bar can be completed, then +no warning is reported. The first bar may be complete the first +time the section is played but incomplete in the second repeat. +Complications occur when the left repeat symbol (|:) is +missing and abc2midi has to assume it is at the beginning. +More complications occur when there is a key change after +the repeat or the music is split into parts A,B,C, etc. +Yaps does the bar checking differently when it encounters +repeats so that it may not resolve as many incomplete bars +and report more warnings. + +The bar checking is present for providing warnings. For some +music, an extra beat may be intentional and it is not +marked by a meter change. If you are only printing the +music, there is probably no problem; however, if you are +producing a MIDI file and there is guitar (gchord) accompaniment +then a break or missing beat might be noticeable. + + + +General MIDI Program Number/Instrument Name +------------------------------------------- + + 0. Acoustic Grand Piano + 1. Bright Acoustic Piano + 2. Electric Grand Piano + 3. Honky-tonk Piano + 4. Electric Piano 1 + 5. Electric Piano 2 + 6. Harpsichord + 7. Clavi + 8. Celesta + 9. Glockenspiel + 10. Music Box + 11. Vibraphone + 12. Marimba + 13. Xylophone + 14. Tubular Bells + 15. Dulcimer + 16. Drawbar Organ + 17. Percussive Organ + 18. Rock Organ + 19. Church Organ + 20. Reed Organ + 21. Accordion + 22. Harmonica + 23. Tango Accordion + 24. Acoustic Guitar (nylon) + 25. Acoustic Guitar (steel) + 26. Electric Guitar (jazz) + 27. Electric Guitar (clean) + 28. Electric Guitar (muted) + 29. Overdriven Guitar + 30. Distortion Guitar + 31. Guitar harmonics + 32. Acoustic Bass + 33. Electric Bass (finger) + 34. Electric Bass (pick) + 35. Fretless Bass + 36. Slap Bass 1 + 37. Slap Bass 2 + 38. Synth Bass 1 + 39. Synth Bass 2 + 40. Violin + 41. Viola + 42. Cello + 43. Contrabass + 44. Tremolo Strings + 45. Pizzicato Strings + 46. Orchestral Harp + 47. Timpani + 48. String Ensemble 1 + 49. String Ensemble 2 + 50. SynthStrings 1 + 51. SynthStrings 2 + 52. Choir Aahs + 53. Voice Oohs + 54. Synth Voice + 55. Orchestra Hit + 56. Trumpet + 57. Trombone + 58. Tuba + 59. Muted Trumpet + 60. French Horn + 61. Brass Section + 62. SynthBrass 1 + 63. SynthBrass 2 + 64. Soprano Sax + 65. Alto Sax + 66. Tenor Sax + 67. Baritone Sax + 68. Oboe + 69. English Horn + 70. Bassoon + 71. Clarinet + 72. Piccolo + 73. Flute + 74. Recorder + 75. Pan Flute + 76. Blown Bottle + 77. Shakuhachi + 78. Whistle + 79. Ocarina + 80. Lead 1 (square) + 81. Lead 2 (sawtooth) + 82. Lead 3 (calliope) + 83. Lead 4 (chiff) + 84. Lead 5 (charang) + 85. Lead 6 (voice) + 86. Lead 7 (fifths) + 87. Lead 8 (bass + lead) + 88. Pad 1 (new age) + 89. Pad 2 (warm) + 90. Pad 3 (polysynth) + 91. Pad 4 (choir) + 92. Pad 5 (bowed) + 93. Pad 6 (metallic) + 94. Pad 7 (halo) + 95. Pad 8 (sweep) + 96. FX 1 (rain) + 97. FX 2 (soundtrack) + 98. FX 3 (crystal) + 99. FX 4 (atmosphere) + 100. FX 5 (brightness) + 101. FX 6 (goblins) + 102. FX 7 (echoes) + 103. FX 8 (sci-fi) + 104. Sitar + 105. Banjo + 106. Shamisen + 107. Koto + 108. Kalimba + 109. Bag pipe + 110. Fiddle + 111. Shanai + 112. Tinkle Bell + 113. Agogo + 114. Steel Drums + 115. Woodblock + 116. Taiko Drum + 117. Melodic Tom + 118. Synth Drum + 119. Reverse Cymbal + 120. Guitar Fret Noise + 121. Breath Noise + 122. Seashore + 123. Bird Tweet + 124. Telephone Ring + 125. Helicopter + 126. Applause + 127. Gunshot + +--------------------------------------------------------------------- +This reference written 1995-1998 by James Allwright diff -Nru abcmidi-20200706/doc/AUTHORS abcmidi-20200728/doc/AUTHORS --- abcmidi-20200706/doc/AUTHORS 2008-05-30 23:22:22.000000000 +0000 +++ abcmidi-20200728/doc/AUTHORS 2020-07-27 15:52:43.000000000 +0000 @@ -1,26 +1,26 @@ - -This file contains additional documentation for the abcMIDI package : - -Unix Man Pages --------------- -* abc2midi.1 man pages for abc2midi written by Christoph Dalitz. -* midi2abc.1 man page for midi2abc written by Christoph Dalitz. -* abc2abc.1 man page for abc2abc written by Anselm Lingnau. -* mftext.1 man page for mftext written by Anselm Lingnau. - -These man pages are now supported by Seymour Shlien - -If you wish to preview these man pages use groff with the -man -option. The output is normally in postscript format, but you -can override this using the -a option. For example, -groff -man -a abc2midi.1 - - -To install the man pages, copy them to appropriate directory (typically -this will be /usr/man/man1). You may also need to run mandb after -copying them across. - -Code Documentation ------------------- -* abc2midi.txt : notes on the abc2midi code written by Seymour Shlien. -* midi2abc.txt : notes on the midi2abc code written by Seymour Shlien. + +This file contains additional documentation for the abcMIDI package : + +Unix Man Pages +-------------- +* abc2midi.1 man pages for abc2midi written by Christoph Dalitz. +* midi2abc.1 man page for midi2abc written by Christoph Dalitz. +* abc2abc.1 man page for abc2abc written by Anselm Lingnau. +* mftext.1 man page for mftext written by Anselm Lingnau. + +These man pages are now supported by Seymour Shlien + +If you wish to preview these man pages use groff with the -man +option. The output is normally in postscript format, but you +can override this using the -a option. For example, +groff -man -a abc2midi.1 + + +To install the man pages, copy them to appropriate directory (typically +this will be /usr/man/man1). You may also need to run mandb after +copying them across. + +Code Documentation +------------------ +* abc2midi.txt : notes on the abc2midi code written by Seymour Shlien. +* midi2abc.txt : notes on the midi2abc code written by Seymour Shlien. diff -Nru abcmidi-20200706/doc/CHANGES abcmidi-20200728/doc/CHANGES --- abcmidi-20200706/doc/CHANGES 2020-07-05 14:19:02.000000000 +0000 +++ abcmidi-20200728/doc/CHANGES 2020-07-28 13:55:06.000000000 +0000 @@ -13745,4 +13745,48 @@ You can find a test file detune.abc in the samples folder. +July 14 2020 +yaps: cleaned up numerous gcc warnings such as +drawtune.c:3245:16: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast] + lineno = (int)(ft->item); + +changed + lineno = (long)(ft->item); +etc. + + + +July 17 2020 + +store.c: in order to improve the accuracy of the MIDI temperamentequal +and MIDI temperamentlinear representation, the calculations were +performed in floating point rather than integer arithmetic. The constant +SEMISIZE = 4096 was eliminated, and the global variables octave_size, +fifth_size, sharp_size, and microstep_size were turned into floating +point variables. A new function compute_fifth_size () was introduced, +which computes the perfect fifth interval quantized into the ndiv +comma units. The result is returned in units of cents where one +standard octave is 1200 cents. If you run abc2midi with -v 1 +(verbose level 1), the size of the fifth interval and other variables +is printed. + +This is a significant change affecting the code in the functions +event_midi() and pitchof_b(). + + +July 24 2020 + +store.c Hudson Lacerda corrected the algorithm in compute_fifth_size() +and fixed a bug that I had introduced. + +July 28 2020 + +store.c Minor correction in store.c when the microtone denominator is +equal to 100 (in pitchof_b()). + + + + + + diff -Nru abcmidi-20200706/doc/history.txt abcmidi-20200728/doc/history.txt --- abcmidi-20200706/doc/history.txt 2016-03-15 19:59:46.000000000 +0000 +++ abcmidi-20200728/doc/history.txt 2020-07-27 15:52:43.000000000 +0000 @@ -1,525 +1,525 @@ -This file has not been updated since I have taken over -the support of the abcMIDI package. To see the list -of the latest versions see the readme.txt file. To see -a log of all the changes I have made see the file CHANGES. -The abcguide.txt and the unix (linux) man files (eg. abc2midi.1) -contain the latest user guides. The abcguide.txt probably -contains the most detail. The official version number -of the abcMIDI package is given in the file VERSION. - -Seymour Shlien 26 October 2003. -------------------------------------------------------------- - - -current versions : -midi2abc version 2.4 -abc2midi version 1.24 -abc2abc version 1.19 -yaps version 1.12 - -Improvements made to abcMIDI package ------------------------------------- -1. abc2midi was found not to handle comments at end of lines properly. -This was fixed in the source code with release 1.2.1 of abc2midi. - -2. midi2abc was generating %MIDI instead of %%MIDI for a program change. -This was fixed in release 1.6.1 - -3. Release 1.2.2 of abc2midi introduced the -c checking only option and - some new error checking, as well as code to deduce repeats. - -4. abc2abc 1.0 was released on 25th July 1996 - -5. Release 1.2.3 of abc2midi allowed more flexible use of ::, :| and |:, - improved handling of tuples and other minor improvements. - -6. Added #ifdefs for Mac port. - -7. Added -xm and -xl options to midi2abc plus a straight quantization - algorithm for better handling of MIDI output by another package. - -8. Added -Q option to midi2abc. - -9. Removed the default heuristic algorithm and use a straight quantization - algorithm by default, with code to find a good note length by brute-force - trial - Original algorithm sometimes made mistakes in quantizing. - -10. Added V: field to allow multi-track output from abc2midi in version 1.3 - -11. Changed Error messages to Warning messages for some error conditions in - abc2midi. - -12. Improved bar length checking to prevent errors being generated by the - anacrusis in abc2midi. - -13. Added checking for equal track lengths in abc2midi. - -14. Made midi2abc fail cleanly when it runs out of memory for notes. - -15. Fixed part-handling bug in abc2midi. - -16. Improved handling of bar placement in midi2abc. - -17. Fixed various type inconsistencies in midifile.c to improve portability. - -18. abc2midi was not handling propagation of accidentals within a bar e.g. -|^cdc|. This was fixed. - -19. Added handling for the 7 key signature modes and re-wrote parsekey in -the process. - -20. Fixed bug in hornpipe handling routine (tomidi.c). - -21. Fixed bug in default unit calculation (toabc.c). - -22. Changed midi2abc to look at last note to decide between equally likely - key signatures and suggest a mode for the tune if it does not appear - to be major mode. - -23. Changed strchr to index in mftext.c to aid portability. - -24. Changed handling of grace notes to report more error conditions. - -25. Changed midi2abc to generate voice fields for format 1 files and also - to assume the same length anacrusis in each voice. - -26. -xa option added to midi2abc to deduce anacrusis by looking for a strong - beat. - -27. Release 1.4 of abc2midi allows [ and ] for chords, ( ) for slurs and also - introduced a queue mechanism for allowing general polyphony. - -28. Release 1.4 of abc2midi appears to a fix a problem with chord generation. - -29. Release 1.4 of abc2midi recognizes J, H, L and R as note decorations. - -30. Fixed bug in hornpipe routine. - -31. Fixed bug in note decoration recognition. - -32. Added support for multiple fields in " " and ! ! separated by ; . - -33. Fixed problem with grace notes before chords. - -34. Added support for new K: field standard - -35. Fixed bug that caused spurious ties from notes at the end of a slur -field. - -36. Release 1.4.2 of abc2midi made on 15th January 1997. - -36. Correction to midi2abc quantization algorithm - -37. abc2midi changed to allow TAB character instead of space. - -38. -n option added to abc2midi (not the PC version, which already uses -almost the maximum possible memory). - -39. Support added for augmented and diminished chords. - -40. Support added for repeats in part notation e.g. P:A(AB)6. - -41. Added transpose option to abc2abc. - -42. Release 1.4.3 of abc2midi made on 28th April 1997. - -43. Added support for w: field in abc2midi - -44. Fixed problem in chord handling in abc2abc - -45. %%MIDI gchord allowed in mid-tune - -46. added %%MIDI chordprog, bassprog, chordvol, bassvol - -47. length specifiers allowed in %%MIDI gchord string - -48. fixed bar-length checking in abc2abc to do chords and grace notes - -49. Made the linebreaking option more intelligent in abc2abc. - -50. Made abc2abc linebreak at P: - -51. added -u option to abc2abc - -52. added %%MIDI control command to abc2midi - -53. added support for karaoke MIDI to abc2midi. - -54. changed karaoke support to act line by line. - -55. Minor bug fix to karaoke support in version 1.5.1 - -56. Fixed bug in part handling pointed out by Steve Allen. - -58. Complete re-write of midi2abc to use dynamic data structures. -version 2.0 (beta) released on 8th June 1998. - -59. Added -k (specify key) and -ga (guess anacrusis) options to midi2abc. -Fixed bug in printing rests. - -60. version 2.0 of midi2abc released on 12th June 1998. - -61. non-portable error diagnosis removed from mftext and midi2abc. - -62. midi2abc fixed to always produce legimite length notes. - -63. Parser changed to allow arbitrarily long lines. - -64. midi2abc changed to recognize rests before each track. - -65. midi2abc changed to used c = 72 (and undo abc2midi pitches). - -66. Parser changed to allow decorations to be applied to chords. - -67. abc2midi changed to allow broken rhythm between chords. - -68. abc2midi changed to improve accuracy of reported line numbers. - -69. abc2midi mallocs more space for notes or text when needed. - -70. support for staccato notes in chord. - -71. improved tie handling code. - -72. fix to allow all channels to be used (suggested by De Clark). - -73. Static limit on number of syllables in w: fields removed. - -74. Allowed . in part specifier (P: field in header). - -75. Used %%MIDI beat to implement !ppp! to !fff!. - -76. Added -o option to abc2midi to specify filename. - -77. Corrected problem with Voice/Part conflict reported by Brendan Macmillan. - -78. Removed bug in line number generation for error reports. - -79. Version 1.5.8 of abc2midi released on 21st Sept 1998. - -80. Further bug fixing for Voice/Part problem. - -81. Trap buffer overflow in NOFTELL version - problem reported by Richard -Robinson - -82. Clean up NOFTELL modifications to remove linker problems (pcc version). - -83. New NOFTELL mechanism introduced - do a dummy write track to determine -track length in abc2midi. - -84. P:X in the header allowed to be a part marker in abc2midi. - -85. Changed save_note() so that chord generation takes account of transpose -- problem report and bug fix by Seymour Shlien - -86. Tidied up note off queue handling in abc2midi. Fixes problem with PCC -version NOFTELL fix. - -87. Changed min to long in midi2abc guesslengths(). Fixes PCC divide by -zero error. - -88. Added rtranspose to abc2midi. - -89. Cleaned up midi2abc and midifilelib code (no implicit declarations of -parameter types). - -90. Voices inherit global transpose, tempo and meter instead of values from -previous track. - -91. Removed spaces from after tie signs in midi2abc : abc spec. does not -allow spaces in chords. - -92. voicecontext structure introduced to make K: and L: fields local to -their own voice in file parsing stage. - -93. printf() fix: DJGPP seems to require %ld and not %Ld in printf to print -longs. - -94. exit value for all codes made 1 on error and 0 on normal termination. - -95. re-write of karaoke code to integrate it into main mywritetrack() code. -Also allows multiple verses and has better error-checking. - -96. Added %%MIDI chordname and library of named chords to abc2midi - -97. Fixed bug in | handling in abc2midi w: field. - -98. Better checking for M: field in abc2midi - -99. chord handling and broken rhythm handling moved to first pass of -abc2midi. - -100. Further bug fixes for abc2midi w: field. - -101. %%MIDI nobarlines and %%MIDI barlines options added to abc2midi - -based on changes suggested by Laura Conrad. - -102. Added support for M:none. - -103. Voice numbers changed from absolute voice numbers to numerical labels. - -104. Accompaniment written to separate track by abc2midi. - -105. Support put in the parser for abc2ps's [X: ] inline field notation. - -106. Fix to stop bogus Q: fields crashing abc2midi. - -107. Support for "_Ignored words" in abc2midi. - -108. abc2midi allows voices after voice 1 to be missing in some parts. - -109. Changed mf_sec2ticks() in midifile.c to get rid of rounding errors -that were affecting the PCC version of midi2abc. - -110. Fixed problem with tracks not starting at time=0 in abc2midi. - -111. Fixed overflow problem with long H: fields in abc2midi. - -112. [1 allowed to go mid-bar and [2 allowed to be separated from :| -in abc2midi. More robust repeat-fixing algorithm to take account of |1 and -:|2. - -113. Fix to midifile.c suggested by Seymour Shlien to fix problem with -porting to MS visual C++. - -114. Added support for trills in abc2midi. - -116. Fixed bug in midi2abc that caused it to crash if input MIDI file - contained long metatext strings. - -117. Added support for lyrics to tied notes in abc2midi. - -118. Fixed bug that mangled %%MIDI lines in abc2abc and also stopped - abc2abc filtering out comments outside tune. - -119. Added support for clefs in K: field. - -120. Replaced K:cclef with proper clefs; baritone, tenor, alto, mezzo - and soprano. - -121. Minor improvements to parseabc.c - recognizes multiple '/' in - length specifier and warns about c, or C'. - -122. Added support for the I: field to parseabc.c and changed abc2midi - to recognize I:octave=X. - -123. Added -d and -v options to abc2abc for changing unit note lengths. - -124. Parser changed to report an error for \ * ** or ! in mid music line. - -125. midi2abc changed to look for a filename if -f option is missing. - -126. part-handling changed in abc2midi to handle an arbitrary number of - parts. - -127. Support for variant endings more complicated than [1 and [2. Also - allow || to occur mid-bar. - -128. Support for Mac-style text files (with \r as end of line). - -129. -o and -sum options added to midi2abc by Seymour Shlien. - -130. Fixed bug in Part/variant ending interaction in abc2midi. - -131. Fixed bug in yaps' handling of in-tune M: fields. - -132. Added font selection commands to yaps. - -133. Added support for grace notes within broken rhythm to abc2midi. - -134. Added page numbering to yaps. - -135. Fixed bug in yaps which caused fonts to get lost after a newpage. - -136. Added vskip command to yaps. - -137. Added %% font and font-spacing commands to yaps. - -138. Minor changes to support Pacific C compiler. - -139. Added support in yaps for ties and slurs straddling 2 music lines. - -140. Added -x (number tunes with number in X: field) to yaps. - -150. Fix to yaps to handle guitar chords with characters in the -range 128-255. - -151. Added proper tuple support to yaps - uses half-brackets when notes -in the tuple are not a beamed set. - -152. Changed guitar chord transposition in abc2abc to handle A/B and A(B) -as well as not transposing anything with a leading underscore or other -special symbols. - -153. Added support for flipping note heads and spacing out accidentals in -yaps. - -154. Fix for K: parsing code which was running past the end-of-line character -in some cases and generating extra error messages in abc2midi. - -155. Added support for treble-8, treble+8 and similar variants in yaps. - -156. Fix for V: fields in header causing abc2midi to crash. - -157. Changed K: field parser to accept clef=, octave= and transpose= . - -160. Added support for %%staffsep to yaps. - -161. Fix to make yaps bar-length checking cope with in-tune metre changes. - -162. Changed yaps so that all tune spacing is calculated before output file - is opened. This is used to generate encapsulated PostScript and to - start a new page instead of breaking a tune across pages. - -163. Added %%MIDI beatstring to abc2midi - using code by Martijn Versteegh. - -164. Fixed BoundingBox generation in yaps. - -165. Added explicit function type declarations and re-ordered some functions - as well as adding genmidi.h and queues.h to make code compile under - Pacific C and hopefully improve portability. - -166. Added the %%MIDI drum command and !drum! and !nodrum! to abc2midi. - -167. Added support for %%titleleft and %%titlecaps to yaps. - -168. Fixed crash caused by !drum! with no %%MIDI drum statement. - -169. Fixed obscure division by zero error in midi2abc. - -170. Added landscape mode to yaps. - -171. Shortened tempo note tail length in yaps so that it never collides - with the title. - -172. Corrected positioning of C clef in yaps. - -177. Added support for -n X to give X bars per line in abc2abc, with - correct handling of associated w: field. - -178. Added support for dotted rests to yaps. - -179. Fixed bug with broken rhythm in yaps. - -180. Fixed bug causing abc2abc to crash with comments at end of w: field. - (code is still not ideal). - -181. Created parser2.c for common code between abc2midi and yaps. - -182. Made erroneous note-off events a non-fatal error in midi2abc. - -184. Added drum track detection to midi2abc, with output that selects - channel 10 for drums. - -185. Removed channel number from midi2abc %%MIDI program output, so that - abc2midi matches instrument to the correct voice in midi2abc output. - -186. Added -s option to midi2abc to retain very short notes. - -187. Support for abc2midi to handle very long filenames (provided by - Colin Watson). - -188. Fix to allow abc2midi/abc2abc/yaps parser to handle space between - key A-G and mode. - -189. Fix for abc2midi handling of non-consecutive voice numbers. - -190. More user-friendly error message when chord buffer overflows in - abc2midi. - -191. Support for w: line continuation characters in yaps. - -192. Fixed problem with trill generation in abc2midi (was the wrong - length). - -193. Fixed inconsistency in midi2abc.c causing a one octave transpose - if the -k switch was specified. - -194. Changed abc2midi to support _, ^, <, > and @ in guitar chords. - -195. Added support for / in abc2midi guitar chords. - -196. Fix for Q:1/2=X in yaps. - -197. Added support for breve notes and breve rests to yaps. - -198. Changed yaps to accept rests in tuples. - -199. Changed parser to accept tuples written as (n::r . - -200. Fix for handling ties across chords in yaps. - -201. Improved handling of M:none in yaps. - -202. Fixes for bar-length checking in yaps. - -203. Added %%MIDI ratio to abc2midi. - -204. Applied fix supplied by Ingo Saitz for change of tempo not being - handled properly by abc2midi in multi-voice music. - -205. Further fix for bar-length checking in yaps. - -206. Added code change by Seymour Shlien to midi2abc for -sr option to -handle MIDI with gaps between notes that come out as rests. - -207 Added %%chordsabove and %%chordsbelow to yaps. - -208. Added handling for U: field (abbreviation) to parser. - -209. Fix to abc2midi to stop accidentals in accompaniment chords from - being applied elsewhere in the bar. - -210. Changed K: field parsing so that it only starts a tune if it - contains a key signature. - -211. Trapped condition where double ties could cause segmentation faults - - thanks to Seymour Shlien for tracing down this problem. - -212. Added -t and -n options to abc2midi for better handling of filenames. - -213. Made parser more robust when handling L: field - thanks to Seymour - Shlien for telling me that this could cause division by zero errors. - -214. Fix for problem reported by Ronan Keryell - gchord generation now - works after a meter change in the tune. - -215. Added %%MIDI pitchbend command using code supplied by Peter Spath. - -216. Modified readstr() in abc2midi/yaps parser to avoid potential buffer - overflow problem. - -217. Added support for strings before and after Q: tempo field in yaps, - abc2midi and abc2abc. Also added support for yaps to print mid-tune - tempo change. - -218. Added support for N: field to yaps. - -219. Fixed problem with channel allocation in abc2midi following bug - report from Ronan Keryell. - -220. Fixed abc2midi problem with broken rhythm > applied to chords - thanks to bug report by Ronan Keryell. - -221. Added to support for multiple bar rests with Zn notation to - abc2abc, abc2midi and yaps. - -222. Fix to abc2abc guitar chord transposing thanks to bug report by - Atte Jensen. - -223. Fix to handling of reserved characters H-Z in abc2abc following - bug report by Luis Pablo Gasparotto. - -224. Tidied abc2abc code by creating emit_() routines. - -225. Fixed bug with broken rhythm marker able to insert itself in tied - notes. Thanks to Michel Chasseriau for reporting this. - -226. Added support for voice filtering (-V option) to abc2abc. Many of - of the code changes for this were made by Seymour Shlien. - -227. Added warning message to abc2midi when T: is found outside a tune. - -228. Added -nt option to midi2abc to ignore triplets and broken rhythm. - -229. Added -X n option to abc2abc. +This file has not been updated since I have taken over +the support of the abcMIDI package. To see the list +of the latest versions see the readme.txt file. To see +a log of all the changes I have made see the file CHANGES. +The abcguide.txt and the unix (linux) man files (eg. abc2midi.1) +contain the latest user guides. The abcguide.txt probably +contains the most detail. The official version number +of the abcMIDI package is given in the file VERSION. + +Seymour Shlien 26 October 2003. +------------------------------------------------------------- + + +current versions : +midi2abc version 2.4 +abc2midi version 1.24 +abc2abc version 1.19 +yaps version 1.12 + +Improvements made to abcMIDI package +------------------------------------ +1. abc2midi was found not to handle comments at end of lines properly. +This was fixed in the source code with release 1.2.1 of abc2midi. + +2. midi2abc was generating %MIDI instead of %%MIDI for a program change. +This was fixed in release 1.6.1 + +3. Release 1.2.2 of abc2midi introduced the -c checking only option and + some new error checking, as well as code to deduce repeats. + +4. abc2abc 1.0 was released on 25th July 1996 + +5. Release 1.2.3 of abc2midi allowed more flexible use of ::, :| and |:, + improved handling of tuples and other minor improvements. + +6. Added #ifdefs for Mac port. + +7. Added -xm and -xl options to midi2abc plus a straight quantization + algorithm for better handling of MIDI output by another package. + +8. Added -Q option to midi2abc. + +9. Removed the default heuristic algorithm and use a straight quantization + algorithm by default, with code to find a good note length by brute-force + trial - Original algorithm sometimes made mistakes in quantizing. + +10. Added V: field to allow multi-track output from abc2midi in version 1.3 + +11. Changed Error messages to Warning messages for some error conditions in + abc2midi. + +12. Improved bar length checking to prevent errors being generated by the + anacrusis in abc2midi. + +13. Added checking for equal track lengths in abc2midi. + +14. Made midi2abc fail cleanly when it runs out of memory for notes. + +15. Fixed part-handling bug in abc2midi. + +16. Improved handling of bar placement in midi2abc. + +17. Fixed various type inconsistencies in midifile.c to improve portability. + +18. abc2midi was not handling propagation of accidentals within a bar e.g. +|^cdc|. This was fixed. + +19. Added handling for the 7 key signature modes and re-wrote parsekey in +the process. + +20. Fixed bug in hornpipe handling routine (tomidi.c). + +21. Fixed bug in default unit calculation (toabc.c). + +22. Changed midi2abc to look at last note to decide between equally likely + key signatures and suggest a mode for the tune if it does not appear + to be major mode. + +23. Changed strchr to index in mftext.c to aid portability. + +24. Changed handling of grace notes to report more error conditions. + +25. Changed midi2abc to generate voice fields for format 1 files and also + to assume the same length anacrusis in each voice. + +26. -xa option added to midi2abc to deduce anacrusis by looking for a strong + beat. + +27. Release 1.4 of abc2midi allows [ and ] for chords, ( ) for slurs and also + introduced a queue mechanism for allowing general polyphony. + +28. Release 1.4 of abc2midi appears to a fix a problem with chord generation. + +29. Release 1.4 of abc2midi recognizes J, H, L and R as note decorations. + +30. Fixed bug in hornpipe routine. + +31. Fixed bug in note decoration recognition. + +32. Added support for multiple fields in " " and ! ! separated by ; . + +33. Fixed problem with grace notes before chords. + +34. Added support for new K: field standard + +35. Fixed bug that caused spurious ties from notes at the end of a slur +field. + +36. Release 1.4.2 of abc2midi made on 15th January 1997. + +36. Correction to midi2abc quantization algorithm + +37. abc2midi changed to allow TAB character instead of space. + +38. -n option added to abc2midi (not the PC version, which already uses +almost the maximum possible memory). + +39. Support added for augmented and diminished chords. + +40. Support added for repeats in part notation e.g. P:A(AB)6. + +41. Added transpose option to abc2abc. + +42. Release 1.4.3 of abc2midi made on 28th April 1997. + +43. Added support for w: field in abc2midi + +44. Fixed problem in chord handling in abc2abc + +45. %%MIDI gchord allowed in mid-tune + +46. added %%MIDI chordprog, bassprog, chordvol, bassvol + +47. length specifiers allowed in %%MIDI gchord string + +48. fixed bar-length checking in abc2abc to do chords and grace notes + +49. Made the linebreaking option more intelligent in abc2abc. + +50. Made abc2abc linebreak at P: + +51. added -u option to abc2abc + +52. added %%MIDI control command to abc2midi + +53. added support for karaoke MIDI to abc2midi. + +54. changed karaoke support to act line by line. + +55. Minor bug fix to karaoke support in version 1.5.1 + +56. Fixed bug in part handling pointed out by Steve Allen. + +58. Complete re-write of midi2abc to use dynamic data structures. +version 2.0 (beta) released on 8th June 1998. + +59. Added -k (specify key) and -ga (guess anacrusis) options to midi2abc. +Fixed bug in printing rests. + +60. version 2.0 of midi2abc released on 12th June 1998. + +61. non-portable error diagnosis removed from mftext and midi2abc. + +62. midi2abc fixed to always produce legimite length notes. + +63. Parser changed to allow arbitrarily long lines. + +64. midi2abc changed to recognize rests before each track. + +65. midi2abc changed to used c = 72 (and undo abc2midi pitches). + +66. Parser changed to allow decorations to be applied to chords. + +67. abc2midi changed to allow broken rhythm between chords. + +68. abc2midi changed to improve accuracy of reported line numbers. + +69. abc2midi mallocs more space for notes or text when needed. + +70. support for staccato notes in chord. + +71. improved tie handling code. + +72. fix to allow all channels to be used (suggested by De Clark). + +73. Static limit on number of syllables in w: fields removed. + +74. Allowed . in part specifier (P: field in header). + +75. Used %%MIDI beat to implement !ppp! to !fff!. + +76. Added -o option to abc2midi to specify filename. + +77. Corrected problem with Voice/Part conflict reported by Brendan Macmillan. + +78. Removed bug in line number generation for error reports. + +79. Version 1.5.8 of abc2midi released on 21st Sept 1998. + +80. Further bug fixing for Voice/Part problem. + +81. Trap buffer overflow in NOFTELL version - problem reported by Richard +Robinson + +82. Clean up NOFTELL modifications to remove linker problems (pcc version). + +83. New NOFTELL mechanism introduced - do a dummy write track to determine +track length in abc2midi. + +84. P:X in the header allowed to be a part marker in abc2midi. + +85. Changed save_note() so that chord generation takes account of transpose +- problem report and bug fix by Seymour Shlien + +86. Tidied up note off queue handling in abc2midi. Fixes problem with PCC +version NOFTELL fix. + +87. Changed min to long in midi2abc guesslengths(). Fixes PCC divide by +zero error. + +88. Added rtranspose to abc2midi. + +89. Cleaned up midi2abc and midifilelib code (no implicit declarations of +parameter types). + +90. Voices inherit global transpose, tempo and meter instead of values from +previous track. + +91. Removed spaces from after tie signs in midi2abc : abc spec. does not +allow spaces in chords. + +92. voicecontext structure introduced to make K: and L: fields local to +their own voice in file parsing stage. + +93. printf() fix: DJGPP seems to require %ld and not %Ld in printf to print +longs. + +94. exit value for all codes made 1 on error and 0 on normal termination. + +95. re-write of karaoke code to integrate it into main mywritetrack() code. +Also allows multiple verses and has better error-checking. + +96. Added %%MIDI chordname and library of named chords to abc2midi + +97. Fixed bug in | handling in abc2midi w: field. + +98. Better checking for M: field in abc2midi + +99. chord handling and broken rhythm handling moved to first pass of +abc2midi. + +100. Further bug fixes for abc2midi w: field. + +101. %%MIDI nobarlines and %%MIDI barlines options added to abc2midi - +based on changes suggested by Laura Conrad. + +102. Added support for M:none. + +103. Voice numbers changed from absolute voice numbers to numerical labels. + +104. Accompaniment written to separate track by abc2midi. + +105. Support put in the parser for abc2ps's [X: ] inline field notation. + +106. Fix to stop bogus Q: fields crashing abc2midi. + +107. Support for "_Ignored words" in abc2midi. + +108. abc2midi allows voices after voice 1 to be missing in some parts. + +109. Changed mf_sec2ticks() in midifile.c to get rid of rounding errors +that were affecting the PCC version of midi2abc. + +110. Fixed problem with tracks not starting at time=0 in abc2midi. + +111. Fixed overflow problem with long H: fields in abc2midi. + +112. [1 allowed to go mid-bar and [2 allowed to be separated from :| +in abc2midi. More robust repeat-fixing algorithm to take account of |1 and +:|2. + +113. Fix to midifile.c suggested by Seymour Shlien to fix problem with +porting to MS visual C++. + +114. Added support for trills in abc2midi. + +116. Fixed bug in midi2abc that caused it to crash if input MIDI file + contained long metatext strings. + +117. Added support for lyrics to tied notes in abc2midi. + +118. Fixed bug that mangled %%MIDI lines in abc2abc and also stopped + abc2abc filtering out comments outside tune. + +119. Added support for clefs in K: field. + +120. Replaced K:cclef with proper clefs; baritone, tenor, alto, mezzo + and soprano. + +121. Minor improvements to parseabc.c - recognizes multiple '/' in + length specifier and warns about c, or C'. + +122. Added support for the I: field to parseabc.c and changed abc2midi + to recognize I:octave=X. + +123. Added -d and -v options to abc2abc for changing unit note lengths. + +124. Parser changed to report an error for \ * ** or ! in mid music line. + +125. midi2abc changed to look for a filename if -f option is missing. + +126. part-handling changed in abc2midi to handle an arbitrary number of + parts. + +127. Support for variant endings more complicated than [1 and [2. Also + allow || to occur mid-bar. + +128. Support for Mac-style text files (with \r as end of line). + +129. -o and -sum options added to midi2abc by Seymour Shlien. + +130. Fixed bug in Part/variant ending interaction in abc2midi. + +131. Fixed bug in yaps' handling of in-tune M: fields. + +132. Added font selection commands to yaps. + +133. Added support for grace notes within broken rhythm to abc2midi. + +134. Added page numbering to yaps. + +135. Fixed bug in yaps which caused fonts to get lost after a newpage. + +136. Added vskip command to yaps. + +137. Added %% font and font-spacing commands to yaps. + +138. Minor changes to support Pacific C compiler. + +139. Added support in yaps for ties and slurs straddling 2 music lines. + +140. Added -x (number tunes with number in X: field) to yaps. + +150. Fix to yaps to handle guitar chords with characters in the +range 128-255. + +151. Added proper tuple support to yaps - uses half-brackets when notes +in the tuple are not a beamed set. + +152. Changed guitar chord transposition in abc2abc to handle A/B and A(B) +as well as not transposing anything with a leading underscore or other +special symbols. + +153. Added support for flipping note heads and spacing out accidentals in +yaps. + +154. Fix for K: parsing code which was running past the end-of-line character +in some cases and generating extra error messages in abc2midi. + +155. Added support for treble-8, treble+8 and similar variants in yaps. + +156. Fix for V: fields in header causing abc2midi to crash. + +157. Changed K: field parser to accept clef=, octave= and transpose= . + +160. Added support for %%staffsep to yaps. + +161. Fix to make yaps bar-length checking cope with in-tune metre changes. + +162. Changed yaps so that all tune spacing is calculated before output file + is opened. This is used to generate encapsulated PostScript and to + start a new page instead of breaking a tune across pages. + +163. Added %%MIDI beatstring to abc2midi - using code by Martijn Versteegh. + +164. Fixed BoundingBox generation in yaps. + +165. Added explicit function type declarations and re-ordered some functions + as well as adding genmidi.h and queues.h to make code compile under + Pacific C and hopefully improve portability. + +166. Added the %%MIDI drum command and !drum! and !nodrum! to abc2midi. + +167. Added support for %%titleleft and %%titlecaps to yaps. + +168. Fixed crash caused by !drum! with no %%MIDI drum statement. + +169. Fixed obscure division by zero error in midi2abc. + +170. Added landscape mode to yaps. + +171. Shortened tempo note tail length in yaps so that it never collides + with the title. + +172. Corrected positioning of C clef in yaps. + +177. Added support for -n X to give X bars per line in abc2abc, with + correct handling of associated w: field. + +178. Added support for dotted rests to yaps. + +179. Fixed bug with broken rhythm in yaps. + +180. Fixed bug causing abc2abc to crash with comments at end of w: field. + (code is still not ideal). + +181. Created parser2.c for common code between abc2midi and yaps. + +182. Made erroneous note-off events a non-fatal error in midi2abc. + +184. Added drum track detection to midi2abc, with output that selects + channel 10 for drums. + +185. Removed channel number from midi2abc %%MIDI program output, so that + abc2midi matches instrument to the correct voice in midi2abc output. + +186. Added -s option to midi2abc to retain very short notes. + +187. Support for abc2midi to handle very long filenames (provided by + Colin Watson). + +188. Fix to allow abc2midi/abc2abc/yaps parser to handle space between + key A-G and mode. + +189. Fix for abc2midi handling of non-consecutive voice numbers. + +190. More user-friendly error message when chord buffer overflows in + abc2midi. + +191. Support for w: line continuation characters in yaps. + +192. Fixed problem with trill generation in abc2midi (was the wrong + length). + +193. Fixed inconsistency in midi2abc.c causing a one octave transpose + if the -k switch was specified. + +194. Changed abc2midi to support _, ^, <, > and @ in guitar chords. + +195. Added support for / in abc2midi guitar chords. + +196. Fix for Q:1/2=X in yaps. + +197. Added support for breve notes and breve rests to yaps. + +198. Changed yaps to accept rests in tuples. + +199. Changed parser to accept tuples written as (n::r . + +200. Fix for handling ties across chords in yaps. + +201. Improved handling of M:none in yaps. + +202. Fixes for bar-length checking in yaps. + +203. Added %%MIDI ratio to abc2midi. + +204. Applied fix supplied by Ingo Saitz for change of tempo not being + handled properly by abc2midi in multi-voice music. + +205. Further fix for bar-length checking in yaps. + +206. Added code change by Seymour Shlien to midi2abc for -sr option to +handle MIDI with gaps between notes that come out as rests. + +207 Added %%chordsabove and %%chordsbelow to yaps. + +208. Added handling for U: field (abbreviation) to parser. + +209. Fix to abc2midi to stop accidentals in accompaniment chords from + being applied elsewhere in the bar. + +210. Changed K: field parsing so that it only starts a tune if it + contains a key signature. + +211. Trapped condition where double ties could cause segmentation faults + - thanks to Seymour Shlien for tracing down this problem. + +212. Added -t and -n options to abc2midi for better handling of filenames. + +213. Made parser more robust when handling L: field - thanks to Seymour + Shlien for telling me that this could cause division by zero errors. + +214. Fix for problem reported by Ronan Keryell - gchord generation now + works after a meter change in the tune. + +215. Added %%MIDI pitchbend command using code supplied by Peter Spath. + +216. Modified readstr() in abc2midi/yaps parser to avoid potential buffer + overflow problem. + +217. Added support for strings before and after Q: tempo field in yaps, + abc2midi and abc2abc. Also added support for yaps to print mid-tune + tempo change. + +218. Added support for N: field to yaps. + +219. Fixed problem with channel allocation in abc2midi following bug + report from Ronan Keryell. + +220. Fixed abc2midi problem with broken rhythm > applied to chords + thanks to bug report by Ronan Keryell. + +221. Added to support for multiple bar rests with Zn notation to + abc2abc, abc2midi and yaps. + +222. Fix to abc2abc guitar chord transposing thanks to bug report by + Atte Jensen. + +223. Fix to handling of reserved characters H-Z in abc2abc following + bug report by Luis Pablo Gasparotto. + +224. Tidied abc2abc code by creating emit_() routines. + +225. Fixed bug with broken rhythm marker able to insert itself in tied + notes. Thanks to Michel Chasseriau for reporting this. + +226. Added support for voice filtering (-V option) to abc2abc. Many of + of the code changes for this were made by Seymour Shlien. + +227. Added warning message to abc2midi when T: is found outside a tune. + +228. Added -nt option to midi2abc to ignore triplets and broken rhythm. + +229. Added -X n option to abc2abc. diff -Nru abcmidi-20200706/doc/midi2abc.1 abcmidi-20200728/doc/midi2abc.1 --- abcmidi-20200706/doc/midi2abc.1 2019-06-13 16:18:59.000000000 +0000 +++ abcmidi-20200728/doc/midi2abc.1 2020-07-27 15:52:43.000000000 +0000 @@ -1,264 +1,264 @@ -.TH MIDI2ABC 1 "1 January 2017" -.SH NAME -\fBmidi2abc\fP \- program to convert MIDI format files to abc notation -.SH SYNOPSIS -midi2abc \-f \fIinfile\fP [\-xa] [\-ga] -[\-a \fIacbeats\fP] [\-m \fItime signature\fP] -[\-ppu \fiparts per unit\fP] [\-aul \fidenominator of unit length\fP] -[\-gu] [\-b \fIbars\fP] [\-Q \fItempo\fP] [\-u \fipulses\fP] -[\-k \fIkey\fP] [\-c \fIchannel\fP] [\-obpl] [\-bpl \fibars\fP] [\-bps \fPbars\fP] -[\-o \fIfilename\fP] [\-s] [\-sr \fiunits\fP] [\-sum] [\-nb] [\-nt] -[\-splitvoices] [\-midigram] [\-mftext] [-mftextpulses] [\-nogr] [\-title \fistring\fP] -[\-origin \fistring\fP] - - - -.SH DESCRIPTION -\fImidi2abc\fP takes a MIDI format file and converts it to something as close -as possible to abc text format. The user then has to add text fields not -present in the MIDI header and possibly tidy up the abc note output. -.PP -The output of midi2abc is printed to the screen. To save it to a file, use -the redirection operator, (e.g. \fImidi2abc \-f file.mid > file.abc\fP) or -specify the output file using the \-o option. -.PP -Use only one or none of the options \-u \-gu, \-b and \-Q. Midi2abc normally -converts the MIDI time units into quantum units normally corresponding to the -abc 1/16th note or 1/32nd note. If none of these is present, the -program will use the PPQN information in the MIDI header to compute the suitable -conversion factor. For most MIDI files on the web, it is recommended to rely on -the MIDI header information and not use any of the options other than -the formatting options. -.PP -The program will extract the time signature information from the MIDI file -if it is present. Otherwise it will assume 4/4 or you could specify it with -\-m. option. -.PP -If the tune has an anacrusis, you can use either the \-ga or \-xa option to estimate the its length. Alternatively, you can specify its value using the \-a -option. The anacrusis is specified in half unit lengths, where the unit -length is defined by the L: field. For example if L: 1/8, then a -quarter note would be indicated by the value 4, (4 1/16 units). -.SS OPTIONS -.TP -.B -a \fIacbeats\fP -where acbeats specifies the anacrusis in half unit lengths. -.TP -.B -xa -extract the anacrusis from file by finding the first strong note -.TP -.B -ga -guess the anacrusis by minimizing the number of ties across bars -.TP -.B -m \fItime signature\fP -time signature -.TP -.B -b \fIbars\fP -number of bars wanted in output -.TP -.B -Q \fItempo\fP -tempo in quarter\-notes per minute -.TP -.B -u \fipulses\fP -Allows you to specify directly the number of midi pulses per -abc time unit. -.TP -.B -ppu \fiparts per abc unit length\fP -Normally, the smallest note unit that midi2abc can extract -is half the L: unit length.This is called the quantum unit. -Thus for L: 1/8, midi2abc can extract 1/16 notes but not 1/32 notes. -You can change this by specifying \-ppu 4 for example. The number of parts -should be a power of 2. -.TP -.B -aul \fidenominator of abc unit length\fP -Normally midi2abc chooses a unit length of 1/8 or 1/16 -depending upon the time signature. For time signatures -smaller than 3/4 the L: 1/16 is used and for larger time -signatures L: 1/8 is used. You can specify the unit length -to be used using this parameter. Thus \-aul 32 will cause -midi2abc to use a unit length of 1/32 nd note. -.TP -.B -gu -Tells midi2abc to estimate the number of midi pulses per abc -time unit from the note duration or spacing in the MIDI file. -.TP -.B -gk -Tells midi2abc to guess the key signature by minimizing -the number of accidentals even if the key signature is -already specified in the MIDI file. By default the key -signature is the one specified in the MIDI file. -If it is not specified, then the program guesses the -key signature by minimizing accidentals. -.TP -.B -k \fIkey\fP -key signature: \-6 to 6 sharps. -.TP -.B -c \fIchannel\fP -select only this midi channel. -.TP -.B -f \fIinfile\fP -input file in midi format -.TP -.B -o \fIoutput file\fP -specifies the output abc file name. -.TP -.B -s -do not discard very short notes. -.TP -.B -sr \fIquantum units\fP -do not notate a short rest smaller than the specified size after a note. If the -size (in quantum units) is zero, nothing is done. For larger values, the rest -is absorbed into the preceding note. In other words, the preceding note -is lengthened to include that rest. -.TP -.B -sum -print a short summary of the input midi file. -.TP -.B -nb -do not look for broken rhythms -.TP -.B -nt -do not look for triplets -.TP -.B -obpl -Print only one bar per line instead of 4. For complex music this -improves the readability and avoids some problems with some abc -to postscript converters. This option is deprecated. -.TP -.B -nogr -(No note grouping.) Inserts a space between all notes. It makes -a less pretty postscript file but it is easier to edit. -.TP -.B -bpl \finbars\fP -Print nbars of music on every line followed by a backslash. -.TP -.B -bps \finbars\fP -When nbars have been printed (including those lines joined by -a backslash continuation) go to a new line (with no backslash). -.TP -.B -splitvoices -This parameter handles polyphonic chords by -splitting an entire voice into multiple voices. (A polyphonic -chord is a chord composed of notes that do not start -or end at the same time.) Normally, midi2abc joins the -longer notes to the notes in the following chord using ties. -Here is an example: [Bd-]d [Bd-]d|. This should -be separated into two voices ideally Bz Bz and d2 d2. However, -the separation is not unique. Bz d2 and d2 Bz are also ok. -.TP -.B -midigram -When this option appears, all other options are ignored and -no abc file is produced. Instead a list of all notes in the -MIDI file are printed in a fixed format. Each line represents -a pair of MIDI note on/off event. The line contains the -on/off time of the note, its track number, channel number, -midi pitch and midi velocity. The last record indicates -the duration of the MIDI file in MIDI pulse units. The -output is designed to go into a graphical user interface -which will produce a graphical representation (piano roll). -.TP -.B -mftext -When this option appears, all other options are ignored and -no abc file is produced. Instead a list of all the MIDI -commands are printed. The output is designed to go into -a graphical user interface provided by runabc.tcl. -.TP -.B -mftextpulses -Same as -mftext except the time units is in midi pulses. -.TP -.B -title \fistring\fP -Replaces the default title field following T: with -the given string. -.TP -.B -origin \fistring\fP -Adds an O: field with the given string. -.TP -.B -stats -Extracts the characteristics of the given midi file. They include -ntrks - the number of tracks, ppqn - pulses per quarter note, -timesig - time signature, keysig - key signature, program - mapping -between channel number and midi program, npulses - length of the -midi file in pulses, tempocmd - number of times the tempo has -been specified, pitchbends - number of pitchbends, pitchbendin - -number of pitchbends in each of the channels, programcmd - number of -times the midi program has been revised, progs and progsact - the -programs used and the number of pulses these programs used, drums - -the drum numbers that were used, drumhits - the number of times -each of those drums were hit, pitches - the number of times the -11 pitch classes (C C# etc...) were activated and a few other -complex variables. These characteristics are used in other -applications such as midiexplorer. More details are available -in the file midi2abc-stats.txt included in the doc/ folder -of the abcmidi distribution package. - - -.SS FEATURES -* The key is chosen so as to minimize the number of accidentals. -Alternatively, the user can specify the key numerically (a positive number -is the number of sharps, a negative number is minus the number of flats). -.PP -* Note length can be set by specifying the total number of bars or the -tempo of the piece. Alternatively the note length can be read from the file. -However, by default it is deduced in a heuristic manner from the inter-note -distances. This means that you do not have to use the MIDI clock as a -metronome when playing in a tune from a keyboard. -.PP -* Barlines are automatically inserted. The user specifies the number of -measures in the anacrusis before the first barline and the time signature. -.PP -* The program can guess how the length of the anacrusis, -either by looking for the first strong note or minimizing the number of -notes split by a tie across a barline. -.PP -* Where a note extends beyond a bar break, it is split into two tied notes. -.PP -* The output has 4 bars per line. -.PP -* Enough accidental signs are put in the music to ensure that no pitch -errors occur if a barline is added or deleted. -.PP -* The program attempts to group notes sensibly in each bar. -.PP -* Triplets and broken rhythm (a>b) are supported. -.PP -* Chords are identified. -.PP -* Text information from the original MIDI file is included as comments. -.PP -* The \-c option can be used to select only 1 MIDI channel. Events on -other channels are ignored. -.SS LIMITATIONS -midi2abc does not ... -.PP -* Supply tune title, composer or any other field apart from X: , K:, Q:, M: -and L: - these must be added by hand afterwards, though they may have been -included in the text of the MIDI file. -.PP -* Support duplets, quadruplets, other esoteric features. -.PP -* Support mid-tune key or time signature changes. -.PP -* Deduce repeats. The output is just the notes in the input file. -.PP -* Recover an abc tune as supplied to abc2midi. However, if you want to -do this, "midi2abc \-xa \-f file.mid" comes close. -.SH "SEE ALSO" -abc2ps(1), abc2midi(1), abc2abc(1) -.SH AUTHOR -James Allwright -.SH SUPPORTED -Seymour Shlien -.SH VERSION -This man page describes midi2abc version 2.91 from March 09 2008. -.SH COPYRIGHT -Copyright 1999 James Allwright -.PP -midi2abc does not work correctly if lyrics are embedded in -the same track as the notes. If you are producing the MIDI -file using abc2midi, use the \-STFW option to ensure that the -lyrics are put in a separate track. -.PP -midi2abc is supplied "as is" without any warranty. It -is free software and can be used, copied, modified and -distributed without fee under the terms of the GNU General -Public License. - +.TH MIDI2ABC 1 "1 January 2017" +.SH NAME +\fBmidi2abc\fP \- program to convert MIDI format files to abc notation +.SH SYNOPSIS +midi2abc \-f \fIinfile\fP [\-xa] [\-ga] +[\-a \fIacbeats\fP] [\-m \fItime signature\fP] +[\-ppu \fiparts per unit\fP] [\-aul \fidenominator of unit length\fP] +[\-gu] [\-b \fIbars\fP] [\-Q \fItempo\fP] [\-u \fipulses\fP] +[\-k \fIkey\fP] [\-c \fIchannel\fP] [\-obpl] [\-bpl \fibars\fP] [\-bps \fPbars\fP] +[\-o \fIfilename\fP] [\-s] [\-sr \fiunits\fP] [\-sum] [\-nb] [\-nt] +[\-splitvoices] [\-midigram] [\-mftext] [-mftextpulses] [\-nogr] [\-title \fistring\fP] +[\-origin \fistring\fP] + + + +.SH DESCRIPTION +\fImidi2abc\fP takes a MIDI format file and converts it to something as close +as possible to abc text format. The user then has to add text fields not +present in the MIDI header and possibly tidy up the abc note output. +.PP +The output of midi2abc is printed to the screen. To save it to a file, use +the redirection operator, (e.g. \fImidi2abc \-f file.mid > file.abc\fP) or +specify the output file using the \-o option. +.PP +Use only one or none of the options \-u \-gu, \-b and \-Q. Midi2abc normally +converts the MIDI time units into quantum units normally corresponding to the +abc 1/16th note or 1/32nd note. If none of these is present, the +program will use the PPQN information in the MIDI header to compute the suitable +conversion factor. For most MIDI files on the web, it is recommended to rely on +the MIDI header information and not use any of the options other than +the formatting options. +.PP +The program will extract the time signature information from the MIDI file +if it is present. Otherwise it will assume 4/4 or you could specify it with +\-m. option. +.PP +If the tune has an anacrusis, you can use either the \-ga or \-xa option to estimate the its length. Alternatively, you can specify its value using the \-a +option. The anacrusis is specified in half unit lengths, where the unit +length is defined by the L: field. For example if L: 1/8, then a +quarter note would be indicated by the value 4, (4 1/16 units). +.SS OPTIONS +.TP +.B -a \fIacbeats\fP +where acbeats specifies the anacrusis in half unit lengths. +.TP +.B -xa +extract the anacrusis from file by finding the first strong note +.TP +.B -ga +guess the anacrusis by minimizing the number of ties across bars +.TP +.B -m \fItime signature\fP +time signature +.TP +.B -b \fIbars\fP +number of bars wanted in output +.TP +.B -Q \fItempo\fP +tempo in quarter\-notes per minute +.TP +.B -u \fipulses\fP +Allows you to specify directly the number of midi pulses per +abc time unit. +.TP +.B -ppu \fiparts per abc unit length\fP +Normally, the smallest note unit that midi2abc can extract +is half the L: unit length.This is called the quantum unit. +Thus for L: 1/8, midi2abc can extract 1/16 notes but not 1/32 notes. +You can change this by specifying \-ppu 4 for example. The number of parts +should be a power of 2. +.TP +.B -aul \fidenominator of abc unit length\fP +Normally midi2abc chooses a unit length of 1/8 or 1/16 +depending upon the time signature. For time signatures +smaller than 3/4 the L: 1/16 is used and for larger time +signatures L: 1/8 is used. You can specify the unit length +to be used using this parameter. Thus \-aul 32 will cause +midi2abc to use a unit length of 1/32 nd note. +.TP +.B -gu +Tells midi2abc to estimate the number of midi pulses per abc +time unit from the note duration or spacing in the MIDI file. +.TP +.B -gk +Tells midi2abc to guess the key signature by minimizing +the number of accidentals even if the key signature is +already specified in the MIDI file. By default the key +signature is the one specified in the MIDI file. +If it is not specified, then the program guesses the +key signature by minimizing accidentals. +.TP +.B -k \fIkey\fP +key signature: \-6 to 6 sharps. +.TP +.B -c \fIchannel\fP +select only this midi channel. +.TP +.B -f \fIinfile\fP +input file in midi format +.TP +.B -o \fIoutput file\fP +specifies the output abc file name. +.TP +.B -s +do not discard very short notes. +.TP +.B -sr \fIquantum units\fP +do not notate a short rest smaller than the specified size after a note. If the +size (in quantum units) is zero, nothing is done. For larger values, the rest +is absorbed into the preceding note. In other words, the preceding note +is lengthened to include that rest. +.TP +.B -sum +print a short summary of the input midi file. +.TP +.B -nb +do not look for broken rhythms +.TP +.B -nt +do not look for triplets +.TP +.B -obpl +Print only one bar per line instead of 4. For complex music this +improves the readability and avoids some problems with some abc +to postscript converters. This option is deprecated. +.TP +.B -nogr +(No note grouping.) Inserts a space between all notes. It makes +a less pretty postscript file but it is easier to edit. +.TP +.B -bpl \finbars\fP +Print nbars of music on every line followed by a backslash. +.TP +.B -bps \finbars\fP +When nbars have been printed (including those lines joined by +a backslash continuation) go to a new line (with no backslash). +.TP +.B -splitvoices +This parameter handles polyphonic chords by +splitting an entire voice into multiple voices. (A polyphonic +chord is a chord composed of notes that do not start +or end at the same time.) Normally, midi2abc joins the +longer notes to the notes in the following chord using ties. +Here is an example: [Bd-]d [Bd-]d|. This should +be separated into two voices ideally Bz Bz and d2 d2. However, +the separation is not unique. Bz d2 and d2 Bz are also ok. +.TP +.B -midigram +When this option appears, all other options are ignored and +no abc file is produced. Instead a list of all notes in the +MIDI file are printed in a fixed format. Each line represents +a pair of MIDI note on/off event. The line contains the +on/off time of the note, its track number, channel number, +midi pitch and midi velocity. The last record indicates +the duration of the MIDI file in MIDI pulse units. The +output is designed to go into a graphical user interface +which will produce a graphical representation (piano roll). +.TP +.B -mftext +When this option appears, all other options are ignored and +no abc file is produced. Instead a list of all the MIDI +commands are printed. The output is designed to go into +a graphical user interface provided by runabc.tcl. +.TP +.B -mftextpulses +Same as -mftext except the time units is in midi pulses. +.TP +.B -title \fistring\fP +Replaces the default title field following T: with +the given string. +.TP +.B -origin \fistring\fP +Adds an O: field with the given string. +.TP +.B -stats +Extracts the characteristics of the given midi file. They include +ntrks - the number of tracks, ppqn - pulses per quarter note, +timesig - time signature, keysig - key signature, program - mapping +between channel number and midi program, npulses - length of the +midi file in pulses, tempocmd - number of times the tempo has +been specified, pitchbends - number of pitchbends, pitchbendin - +number of pitchbends in each of the channels, programcmd - number of +times the midi program has been revised, progs and progsact - the +programs used and the number of pulses these programs used, drums - +the drum numbers that were used, drumhits - the number of times +each of those drums were hit, pitches - the number of times the +11 pitch classes (C C# etc...) were activated and a few other +complex variables. These characteristics are used in other +applications such as midiexplorer. More details are available +in the file midi2abc-stats.txt included in the doc/ folder +of the abcmidi distribution package. + + +.SS FEATURES +* The key is chosen so as to minimize the number of accidentals. +Alternatively, the user can specify the key numerically (a positive number +is the number of sharps, a negative number is minus the number of flats). +.PP +* Note length can be set by specifying the total number of bars or the +tempo of the piece. Alternatively the note length can be read from the file. +However, by default it is deduced in a heuristic manner from the inter-note +distances. This means that you do not have to use the MIDI clock as a +metronome when playing in a tune from a keyboard. +.PP +* Barlines are automatically inserted. The user specifies the number of +measures in the anacrusis before the first barline and the time signature. +.PP +* The program can guess how the length of the anacrusis, +either by looking for the first strong note or minimizing the number of +notes split by a tie across a barline. +.PP +* Where a note extends beyond a bar break, it is split into two tied notes. +.PP +* The output has 4 bars per line. +.PP +* Enough accidental signs are put in the music to ensure that no pitch +errors occur if a barline is added or deleted. +.PP +* The program attempts to group notes sensibly in each bar. +.PP +* Triplets and broken rhythm (a>b) are supported. +.PP +* Chords are identified. +.PP +* Text information from the original MIDI file is included as comments. +.PP +* The \-c option can be used to select only 1 MIDI channel. Events on +other channels are ignored. +.SS LIMITATIONS +midi2abc does not ... +.PP +* Supply tune title, composer or any other field apart from X: , K:, Q:, M: +and L: - these must be added by hand afterwards, though they may have been +included in the text of the MIDI file. +.PP +* Support duplets, quadruplets, other esoteric features. +.PP +* Support mid-tune key or time signature changes. +.PP +* Deduce repeats. The output is just the notes in the input file. +.PP +* Recover an abc tune as supplied to abc2midi. However, if you want to +do this, "midi2abc \-xa \-f file.mid" comes close. +.SH "SEE ALSO" +abc2ps(1), abc2midi(1), abc2abc(1) +.SH AUTHOR +James Allwright +.SH SUPPORTED +Seymour Shlien +.SH VERSION +This man page describes midi2abc version 2.91 from March 09 2008. +.SH COPYRIGHT +Copyright 1999 James Allwright +.PP +midi2abc does not work correctly if lyrics are embedded in +the same track as the notes. If you are producing the MIDI +file using abc2midi, use the \-STFW option to ensure that the +lyrics are put in a separate track. +.PP +midi2abc is supplied "as is" without any warranty. It +is free software and can be used, copied, modified and +distributed without fee under the terms of the GNU General +Public License. + diff -Nru abcmidi-20200706/doc/programming/abc2midi.txt abcmidi-20200728/doc/programming/abc2midi.txt --- abcmidi-20200706/doc/programming/abc2midi.txt 2008-05-30 23:22:22.000000000 +0000 +++ abcmidi-20200728/doc/programming/abc2midi.txt 2020-07-27 15:52:43.000000000 +0000 @@ -1,420 +1,420 @@ -Some Notes on the abc2midi Code -------------------------------- - written by Seymour Shlien - - -Abc2midi.txt - last updated 29 November 1999. - - -This file provides an algorithmic description of the program -abc2midi which converts an abc file into a midi file. - -The sources of abc2midi now comprising of 6700 lines of C code are -contained in the following five files. - -parseabc.c is the front end which scans the abc file and invokes - the appropriate event handler for each element it encounters - (eg. bar lines, notes, chords etc.) It happens to be the - front end for other programs such as abc2ps, yaps and - abc2abc. -store.c contains all the event handlers which are invoked from - parseabc. It converts the abc file into an internal - representation described later in this file. -genmidi.c converts this internal representation in an output midi - file. -queues.c are some utilities needed by genmidi. -midifile.c is a library of public domain routines to read and write - midifiles. - -In order to handle the multiple voices, parts, repeats and accompaniments -which occur in abc files, the program performs multiple passes. The -midi file stores the different voices, accompaniment and lyrics in -individual tracks, so it is necessary to separates these parts prior -to writing it to the midi file. If there are repeats in the music, -something which appears only once in the abc file may be invoked twice -when creating the output midi file. - -Parseabc.c ----------- - -The parser has been written so that it should be possible to write -your own utility to handle the interpreted abc and link it with -the parser code. This is very similar to the way that the midifilelib -utilities work. The parser is designed so that it can write out -almost exactly what it reads in. This means that in some cases -the level of parsing is fairly primitive and it may be necessary to make -use of routines in store.c which perform further processing on an item. - -In the first phase of parsing, the abc file is read and when, say, a note -is encountered, the routine event_note() is called. The code for event_note -is in the file store.c. Encountering an X in the abc generally causes a -routine called event_X() to be called. An internal representation of the tune -is built up and when the end of an abc tune is reached, a little bit of -processing is done on the internal representation before the routine -mywritetrack() is called to actually write out the MIDI file. - -The main program to abc2midi is contained in this file. Since this -main program is used to start other programs such as yaps, it is -fairly simple. It calls event_init defined in store.c which obtains -all the user parameters (eg. input filename) and then executes the -top level procedure, parsefile. - -The procedure parsefile (filename), opens the specified file (or stdin) -and processes it one character at a time. The characters fill a line -buffer which is passed to the procedure parseline whenever a linebreak -character (eg. linefeed) is encountered. By doing it in this fashion, -abc2midi is able to eliminate the extra carriage return which occurs -in DOS files. - -The procedure parseline first checks for blank lines and comments. A -blank line signals the end of the tune and the event_blankline is called -to initiate the next stage in processing. If it is neither a comment or -blank line, the procedure must decide whether the line is a a field line -(eg."X:2") or part of a music body (eg. "|:C>DGz|..."). This is decided -using the following rule. If the first letter begins with one of the field -letter commands and is immediately followed by a ":", then it is treated -as a field line and parsefield is called. Otherwise if a K: field line -was already encountered (variable inbody set to 1), then it is treated -as a line of music. If neither case is satisfied, then the line is -intepreted as plain text. - -The procedure parsefield identifies the particular field line type -and invokes the appropriate support function or event handler. If the -field command occurs after the first K: line, then only certain field -lines are legal. (For example it is illegal to place a T: (title) line -after the K: (key signature) line.) - -The procedure parsemusic is the most complex procedure in the file and -recognizes the musical notes, chords, guitar chord names, bar lines, -repeats, slurs, ties, graces etc. Appropriate event handlers and -support functions are called to complete the tasks of parsing the -information. The parsenote function, for example, must check for -decorations, accidentals, octave shifts as well as identifying the -note and its determining its duration. - -Unlike the other software modules, parseabc.c contains few global -variables. The global variables linenum, inhead, inbody, parsing, -slur, parserinchord indicate which lexical component of the abc -tune is being processed. - - - - -store.c -------- - -This is the most complex module consisting of almost 3000 lines -of source code. This module contains all of the event handlers which -may be invoked in the file parseabc.c. The main purpose of these -handlers are to build up an internal representation of the abc file -in the global memory arrays. This is a complete representation -which allows the regeneration of the abc file (eg. abc2abc) or -the creation of a midi file. The internal representation is used -by the genmidi.c software to create the midi file. - - -Global variables - -The internal representation is stored in global variables defined -at the beginning of the file. A description of the most important -variables is given here. - -The music is stored in the four arrays, feature, pitch, num, denom. -These arrays contains a list of the lexical components in the order -that they have been encountered or created. - -The array "feature" identifies the type of component which can be one -of the following enum features. - -SINGLE_BAR, DOUBLE_BAR, BAR_REP, REP_BAR, REP1, REP2, BAR1, -REP_BAR2, DOUBLE_REP, THICK_THIN, THIN_THICK, PART, TEMPO, -TIME, KEY, REST, TUPLE, NOTE, NONOTE, OLDTIE, TEXT, SLUR_ON, -SLUR_OFF, TIE, TITLE, CHANNEL, TRANSPOSE, RTRANSPOSE, GRACEON, -GRACEOFF, SETGRACE, SETC, GCHORD, GCHORDON, GCHORDOFF, VOICE, -CHORDON, CHORDOFF, SLUR_TIE, TNOTE, LT, GT, DYNAMIC, LINENUM, -MUSICLINE, MUSICSTOP, WORDLINE, WORDSTOP - -The array pitch[] mainly stores the pitch of a musical note, -represented in the same manner as in a midi file. Thus middle -C has a value of 60. The array has other uses, for example -in a LINENUM feature it stores the line number relative to -the X: reference command where the lexical feature has been -detected. The LINENUM feature is useful when reporting warnings -or errors in the input file. Duration of the notes are represented -as a fraction, where the standard unit is defined by the L: -command. If feature[n] is a NOTE, then num[n] and denom[n] -contain the numerator and denominator of this fraction. - -Here is an example of the contents of these arrays for the -following small file in the same order that they are stored in -these arrays. - -X: 1 -T: sample -M: 2/4 -L: 1/8 -K: G -|: C>D[EF]G |C4:| - - -Feature Pitch Num Denom -LINENUM 2 0 0 -TITLE 0 0 0 -LINENUM 3 0 0 -LINENUM 4 0 0 -LINENUM 5 0 0 -DOUBLE_BAR 0 0 0 -LINENUM 6 0 0 -MUSICLINE 0 0 0 -BAR_REP 0 0 0 -NOTE 60 2 3 -NOTE 62 1 3 -CHORDON 0 0 0 -NOTE 64 1 2 -NOTE 66 1 2 -CHORDOFF 0 1 2 -NOTE 67 1 2 -SINGLE_BAR 0 0 0 -NOTE 60 2 1 -REP_BAR 0 0 0 -MUSIC_STOP 0 0 0 -LINENUM 7 0 0 - - -In order to support multivoice abc files in the form -V:1 -| ...| ...|.... -V:2 -| ...| ...|.... -% -V:1 -| ...| ...|.... -V:2 -| ...| ...|.... -etc. - -abc2midi maintains a voicecontext structure for each voice. -This allows each voice to define its own key signature, default -note length using internal field commands. There is a head -voicecontext which is used when no voice field commands are -defined in the abc file. The v[] array maintains a set of -all voices active in the abc file and voicecount keeps track -of the number of voices. - -Similarly, abcmidi maintains a list of all the part stored -in the feature[], pitch[], num[] and denom[] arrays. - -All text items (eg. title and some other fields) are stored -in char atext[][] arrays, so that they can be included in -the midi output file. The textual information is repeated -in each track of the output midi file. - -Following the conventions in the midi file, the program uses -the quarter note as the standard unit of time instead of the -whole note. In contrast, the standard length in the abc file -is based on the whole note. For example in L:1/8, the fraction -refers to a whole note. In order to convert time units to -quarter notes, the numerator of the time specifications -for note lengths is multiplied by 4 when it is added to -the feature list. - -Table of contents of procedures in store.c - -getarg(option, argc, argv) - gets run time arguments. -newvoice(n) - creates a new voice context. -getvoicecontext() - finds or makes a voice context. -clearvoicecontexts() - frees up the memory of the voice context - -event_init() - called by main program -event_text(s) - called when a line of text is encountered. -event_reserved(p) - handles reserved character codes H-Z. -event_tex(s) - called whenever a TeX command is encountered. -event_linebreak() - called whenever a newline character is encountered. -event_startmusicline() - called for each new music line. -event_endmusicline() - called at the end of each music line. -event_eof() - end of abc file encountered. -event_fatal_error(s) - reports fatal error. -event_error(s) - reports an error. -event_warning(s) - reports a potential problem. -event_comment(s) - called whenever a comment is encountered. -event_specific(package, s) - recognizes %%package s. -event_startinline() - beginning of an in-music field command. -event_closeinline() - finishes an in-music field command. -event_field(k,f) - for R:, T: and any other unprocessed field commands. -event_words(p) - processes a w: field command. - -char_out(list,out,ch) - for building up a parts list in a P: command. -read_spec() - converts P:A(AB)3 to P:AABABAB etc. -event_part(s) - handles a P: field. - -char_break() - reports error for improper voice change. -event_voice(n,s) - processes a V: field. -event_length(n) - recognizes L: field -event_blankline - starts finishfile() procedure. -event_refno(n) - recognizes X: -event_tempo(n, a, b, rel) - recognizes Q: -event_timesig(n, m) - recognizes M: -event_key(sharps, s, minor, modmap, modmul) - recognizes K: -event_graceon() - start of grace notes, "{" encountered. -event_graceoff() - end of grace notes, "}" encountered. -event_rep1() - handles first repeat indicated by [1. -event_rep2() - handles second repeat indicated by [2. -event_slur(t) -called when s encountered in abc. -event_sluron(t) called when "(" is encountered in abc. -event_sluroff(t) called when ")" is encountered in abc. -slurtotie() - converts slur into tied notes. -event_tie() - handles tie indication "-". -event_rest(n,m) - processes rest indication Z or z. -event_bar(type) - processes various types of bar lines. -event_space() - space character encountered. Ignored here. -event_linend(ch,n) - handles line continuation at end of line (eg. \). -event_broken(type, mult) - handles >, <, >> etc. in abc. -event_tuple(n,q,r) - handles triplets and general tuplets. -event_chord() - called whenever + is encountered. -marknotestart() - remembers last few notes in voice context. -marknoteend() - this is used to process broken rhythms (eg. >). -marknote() - called for single note (as opposed to chord). -event_chordon() - called whenever [ is encountered. -event_chordoff() - called whenever ] is encountered. -splitstring(s,sep,handler) - splits string with separator sep. -event_instuction(s) - handles !...! event. -getchordnumber(s) - searches known chords for chord s. -addchordname(s, len, notes) - adds chord name to known chord list. -event_gchord(s) - handles guitar chords. -event_handle_gchord(s) - handler for guitar chords. -event_handle_instruction(s) - handles dynamic indications (eg. !ppp!). -event_finger(p) - handles 1,2,...5 in guitar chord field (does nothing). -hornp(num,denom) - modifies rhythm to hornpipe. -event_note(roll, staccato, updown, accidental, mult, note, octave, n, m) -doroll(note,octave,n,m,pitch) - applies roll to note. -dotrill(note,octave,n,m,pitch) - applies trill to note. -pitchof(note,accidental,mult,octave) -finds MIDI pitch value -setmap(sf,map,mult) - converts key signature to accidental map. -altermap(v,modmap,modmul) - modifies accidental map. -copymap(v) - sets up working accidental map at beginning of bar. - -addfeature(f,p,n,d) - places feature in internal tables. -autoextend(maxnotes) - increase memory limits for feature arrays. -textextend(maxstrings, stringarray) - resize array pointers to strings. -myputc(c) - workaround for problems with PCC compiler. -tiefix() - connect up tied notes. -dotie(j,xinchord) - called in preprocessing stage to handle ties. -addfrac(xnum,xdenom,a,b) - add a/b to the number of units in bar. -applybroken(place, type, n) - adjust length of broken notes. -brokenadjust() -adjust length of broken notes. -applygrace() - assign lengths to grace notes. -dograce() - assign lengths to grace notes. -lenmul(n, a, b) - multiply num(n),denom(n) by a/b. -zerobar() - start a new count of beats in the bar. -delendrep() - remove bogus repeat. -placeendrep(j) - patch up missing repeat. -placestartrep(j) - patch up missing repeat. -fixreps() - find and correct missing repeats in music. -startfile() - initialization performed after an event_refno. -tempounits(t_num, t_denom) - interprets Q: field. -setbeat() - sets default gchord command for time signature. -headerprocess() - called after the first K: field in tune. -finishfile() - starts next stage of processing when end of tune -is encountered. - -All the functions in this file respond to event calls from parseabc. -Once the internal representation of the abc file is completed, the -procedure finishfile is called to perform some clean up and create -the midi file. An internal representation of the midi file is -first created and then it is written onto the designated output file. -As finishfile provides the link to the next module, genmidi.c, here -is a brief description of what it does. - -proc finishfile performs several passes through the internal -representation to clean up the graces (dograce), the tied notes -(tiefix) and any unbalanced repeats. It then calls writetrack(i) -for each midi track to create the internal midi representation -and finally the midi representation is recorded in the output -file. - - - -genmidi.c ---------- -The procedure finishfile described above, creates each midi track -by calling the function writetrack which is defined here. To create -a track from the internal representation, the program must find all -the parts and put them in order with all the repeats. In addition, if -it contains karaoke text, this too must be placed in a separate track. -Any chordal accompaniment is generated from the guitar chord indications -and placed in another track. For multivoice and multipart music, a voice -may be missing in a particular part. If the voice is missing, the -procedure fillvoice ensures that all voices remain properly aligned when -the voice reappears in another part. - -Here is a simple road map to the important procedures included in this -file. - -dodeferred is here used to handle any dynamic indications (eg. !ppp!) -which may be contained in the file. The %%MIDI messages are stored -in a atext string which is pointed to by the contents of the pitch[] -array. - -checkbar is called each time a bar line is encountered and reports -a warning if the wrong number of beats occur. - -Transitions between parts are handled by the procedure partbreak. - -There are a number of procedures for handling karoake text -- -karaokestarttrack(), findwline(startline), getword(place,w), -write_syllable(place) and checksyllables(). - -For the first track, the meter, tempo and key signature are recorded -using the functions set_meter(n,m), write_meter(n,m), write_keysig(sf,mi). - -Chordal accompaniment is produced by the procedure dogchords(i). - - - -queues.c --------- - -For each midi note, it is necessary to send a note-on and a note-off -instruction. When polyphonic music is played on the same track, keeping -track of the time to issue a note-off instruction may get complicated. -The procedures in this file are used to maintain a linked list for the -notes to be turned off. The notes are put into the list in the order -that they are encountered but the order in which to issue note-off -commands is maintained by the links. As many as 50 notes playing -simultaneously can be handled by the list. - - - - - -Addendum --------- - -The following section contains clarifications on various components -of abc2midi. - -29 June 2003 - -Treatment of octave shifts. - -The key signature field command K: has provision for shifting -a note using either transpose= or octave= subcommands. Both -of these functions operate quite differently and deserve some -description especially for multivoiced tunes. - -The octave shift, is performed in event_note in store.c, using -the cached value v.octaveshift which is stored in the global -voicecontext structure, v. There is a structure for each voice -in the abc file. Whenever a new V: command is encountered, -event_voice (store.c) is invoked, which swaps the appropriate -voices structure into the global v array using the function -getvoicecontext(n). If getvoicecontext cannot find a cached -structure for that voice, then a new voice structure is created -and added to the linked list of voice structures. The v.octaveshift -variable is updated by event_octave which is called by event_key -(store.c) which is called by parsekey in parseabc.c -(Comment: it is not too clear how an octave switch is -handled in the middle of a repeat section. i.e. does the old -value get restored when repeating.) - -(Description of transpose shift is in CHANGES July 1 2003.) +Some Notes on the abc2midi Code +------------------------------- + written by Seymour Shlien + + +Abc2midi.txt - last updated 29 November 1999. + + +This file provides an algorithmic description of the program +abc2midi which converts an abc file into a midi file. + +The sources of abc2midi now comprising of 6700 lines of C code are +contained in the following five files. + +parseabc.c is the front end which scans the abc file and invokes + the appropriate event handler for each element it encounters + (eg. bar lines, notes, chords etc.) It happens to be the + front end for other programs such as abc2ps, yaps and + abc2abc. +store.c contains all the event handlers which are invoked from + parseabc. It converts the abc file into an internal + representation described later in this file. +genmidi.c converts this internal representation in an output midi + file. +queues.c are some utilities needed by genmidi. +midifile.c is a library of public domain routines to read and write + midifiles. + +In order to handle the multiple voices, parts, repeats and accompaniments +which occur in abc files, the program performs multiple passes. The +midi file stores the different voices, accompaniment and lyrics in +individual tracks, so it is necessary to separates these parts prior +to writing it to the midi file. If there are repeats in the music, +something which appears only once in the abc file may be invoked twice +when creating the output midi file. + +Parseabc.c +---------- + +The parser has been written so that it should be possible to write +your own utility to handle the interpreted abc and link it with +the parser code. This is very similar to the way that the midifilelib +utilities work. The parser is designed so that it can write out +almost exactly what it reads in. This means that in some cases +the level of parsing is fairly primitive and it may be necessary to make +use of routines in store.c which perform further processing on an item. + +In the first phase of parsing, the abc file is read and when, say, a note +is encountered, the routine event_note() is called. The code for event_note +is in the file store.c. Encountering an X in the abc generally causes a +routine called event_X() to be called. An internal representation of the tune +is built up and when the end of an abc tune is reached, a little bit of +processing is done on the internal representation before the routine +mywritetrack() is called to actually write out the MIDI file. + +The main program to abc2midi is contained in this file. Since this +main program is used to start other programs such as yaps, it is +fairly simple. It calls event_init defined in store.c which obtains +all the user parameters (eg. input filename) and then executes the +top level procedure, parsefile. + +The procedure parsefile (filename), opens the specified file (or stdin) +and processes it one character at a time. The characters fill a line +buffer which is passed to the procedure parseline whenever a linebreak +character (eg. linefeed) is encountered. By doing it in this fashion, +abc2midi is able to eliminate the extra carriage return which occurs +in DOS files. + +The procedure parseline first checks for blank lines and comments. A +blank line signals the end of the tune and the event_blankline is called +to initiate the next stage in processing. If it is neither a comment or +blank line, the procedure must decide whether the line is a a field line +(eg."X:2") or part of a music body (eg. "|:C>DGz|..."). This is decided +using the following rule. If the first letter begins with one of the field +letter commands and is immediately followed by a ":", then it is treated +as a field line and parsefield is called. Otherwise if a K: field line +was already encountered (variable inbody set to 1), then it is treated +as a line of music. If neither case is satisfied, then the line is +intepreted as plain text. + +The procedure parsefield identifies the particular field line type +and invokes the appropriate support function or event handler. If the +field command occurs after the first K: line, then only certain field +lines are legal. (For example it is illegal to place a T: (title) line +after the K: (key signature) line.) + +The procedure parsemusic is the most complex procedure in the file and +recognizes the musical notes, chords, guitar chord names, bar lines, +repeats, slurs, ties, graces etc. Appropriate event handlers and +support functions are called to complete the tasks of parsing the +information. The parsenote function, for example, must check for +decorations, accidentals, octave shifts as well as identifying the +note and its determining its duration. + +Unlike the other software modules, parseabc.c contains few global +variables. The global variables linenum, inhead, inbody, parsing, +slur, parserinchord indicate which lexical component of the abc +tune is being processed. + + + + +store.c +------- + +This is the most complex module consisting of almost 3000 lines +of source code. This module contains all of the event handlers which +may be invoked in the file parseabc.c. The main purpose of these +handlers are to build up an internal representation of the abc file +in the global memory arrays. This is a complete representation +which allows the regeneration of the abc file (eg. abc2abc) or +the creation of a midi file. The internal representation is used +by the genmidi.c software to create the midi file. + + +Global variables + +The internal representation is stored in global variables defined +at the beginning of the file. A description of the most important +variables is given here. + +The music is stored in the four arrays, feature, pitch, num, denom. +These arrays contains a list of the lexical components in the order +that they have been encountered or created. + +The array "feature" identifies the type of component which can be one +of the following enum features. + +SINGLE_BAR, DOUBLE_BAR, BAR_REP, REP_BAR, REP1, REP2, BAR1, +REP_BAR2, DOUBLE_REP, THICK_THIN, THIN_THICK, PART, TEMPO, +TIME, KEY, REST, TUPLE, NOTE, NONOTE, OLDTIE, TEXT, SLUR_ON, +SLUR_OFF, TIE, TITLE, CHANNEL, TRANSPOSE, RTRANSPOSE, GRACEON, +GRACEOFF, SETGRACE, SETC, GCHORD, GCHORDON, GCHORDOFF, VOICE, +CHORDON, CHORDOFF, SLUR_TIE, TNOTE, LT, GT, DYNAMIC, LINENUM, +MUSICLINE, MUSICSTOP, WORDLINE, WORDSTOP + +The array pitch[] mainly stores the pitch of a musical note, +represented in the same manner as in a midi file. Thus middle +C has a value of 60. The array has other uses, for example +in a LINENUM feature it stores the line number relative to +the X: reference command where the lexical feature has been +detected. The LINENUM feature is useful when reporting warnings +or errors in the input file. Duration of the notes are represented +as a fraction, where the standard unit is defined by the L: +command. If feature[n] is a NOTE, then num[n] and denom[n] +contain the numerator and denominator of this fraction. + +Here is an example of the contents of these arrays for the +following small file in the same order that they are stored in +these arrays. + +X: 1 +T: sample +M: 2/4 +L: 1/8 +K: G +|: C>D[EF]G |C4:| + + +Feature Pitch Num Denom +LINENUM 2 0 0 +TITLE 0 0 0 +LINENUM 3 0 0 +LINENUM 4 0 0 +LINENUM 5 0 0 +DOUBLE_BAR 0 0 0 +LINENUM 6 0 0 +MUSICLINE 0 0 0 +BAR_REP 0 0 0 +NOTE 60 2 3 +NOTE 62 1 3 +CHORDON 0 0 0 +NOTE 64 1 2 +NOTE 66 1 2 +CHORDOFF 0 1 2 +NOTE 67 1 2 +SINGLE_BAR 0 0 0 +NOTE 60 2 1 +REP_BAR 0 0 0 +MUSIC_STOP 0 0 0 +LINENUM 7 0 0 + + +In order to support multivoice abc files in the form +V:1 +| ...| ...|.... +V:2 +| ...| ...|.... +% +V:1 +| ...| ...|.... +V:2 +| ...| ...|.... +etc. + +abc2midi maintains a voicecontext structure for each voice. +This allows each voice to define its own key signature, default +note length using internal field commands. There is a head +voicecontext which is used when no voice field commands are +defined in the abc file. The v[] array maintains a set of +all voices active in the abc file and voicecount keeps track +of the number of voices. + +Similarly, abcmidi maintains a list of all the part stored +in the feature[], pitch[], num[] and denom[] arrays. + +All text items (eg. title and some other fields) are stored +in char atext[][] arrays, so that they can be included in +the midi output file. The textual information is repeated +in each track of the output midi file. + +Following the conventions in the midi file, the program uses +the quarter note as the standard unit of time instead of the +whole note. In contrast, the standard length in the abc file +is based on the whole note. For example in L:1/8, the fraction +refers to a whole note. In order to convert time units to +quarter notes, the numerator of the time specifications +for note lengths is multiplied by 4 when it is added to +the feature list. + +Table of contents of procedures in store.c + +getarg(option, argc, argv) - gets run time arguments. +newvoice(n) - creates a new voice context. +getvoicecontext() - finds or makes a voice context. +clearvoicecontexts() - frees up the memory of the voice context + +event_init() - called by main program +event_text(s) - called when a line of text is encountered. +event_reserved(p) - handles reserved character codes H-Z. +event_tex(s) - called whenever a TeX command is encountered. +event_linebreak() - called whenever a newline character is encountered. +event_startmusicline() - called for each new music line. +event_endmusicline() - called at the end of each music line. +event_eof() - end of abc file encountered. +event_fatal_error(s) - reports fatal error. +event_error(s) - reports an error. +event_warning(s) - reports a potential problem. +event_comment(s) - called whenever a comment is encountered. +event_specific(package, s) - recognizes %%package s. +event_startinline() - beginning of an in-music field command. +event_closeinline() - finishes an in-music field command. +event_field(k,f) - for R:, T: and any other unprocessed field commands. +event_words(p) - processes a w: field command. + +char_out(list,out,ch) - for building up a parts list in a P: command. +read_spec() - converts P:A(AB)3 to P:AABABAB etc. +event_part(s) - handles a P: field. + +char_break() - reports error for improper voice change. +event_voice(n,s) - processes a V: field. +event_length(n) - recognizes L: field +event_blankline - starts finishfile() procedure. +event_refno(n) - recognizes X: +event_tempo(n, a, b, rel) - recognizes Q: +event_timesig(n, m) - recognizes M: +event_key(sharps, s, minor, modmap, modmul) - recognizes K: +event_graceon() - start of grace notes, "{" encountered. +event_graceoff() - end of grace notes, "}" encountered. +event_rep1() - handles first repeat indicated by [1. +event_rep2() - handles second repeat indicated by [2. +event_slur(t) -called when s encountered in abc. +event_sluron(t) called when "(" is encountered in abc. +event_sluroff(t) called when ")" is encountered in abc. +slurtotie() - converts slur into tied notes. +event_tie() - handles tie indication "-". +event_rest(n,m) - processes rest indication Z or z. +event_bar(type) - processes various types of bar lines. +event_space() - space character encountered. Ignored here. +event_linend(ch,n) - handles line continuation at end of line (eg. \). +event_broken(type, mult) - handles >, <, >> etc. in abc. +event_tuple(n,q,r) - handles triplets and general tuplets. +event_chord() - called whenever + is encountered. +marknotestart() - remembers last few notes in voice context. +marknoteend() - this is used to process broken rhythms (eg. >). +marknote() - called for single note (as opposed to chord). +event_chordon() - called whenever [ is encountered. +event_chordoff() - called whenever ] is encountered. +splitstring(s,sep,handler) - splits string with separator sep. +event_instuction(s) - handles !...! event. +getchordnumber(s) - searches known chords for chord s. +addchordname(s, len, notes) - adds chord name to known chord list. +event_gchord(s) - handles guitar chords. +event_handle_gchord(s) - handler for guitar chords. +event_handle_instruction(s) - handles dynamic indications (eg. !ppp!). +event_finger(p) - handles 1,2,...5 in guitar chord field (does nothing). +hornp(num,denom) - modifies rhythm to hornpipe. +event_note(roll, staccato, updown, accidental, mult, note, octave, n, m) +doroll(note,octave,n,m,pitch) - applies roll to note. +dotrill(note,octave,n,m,pitch) - applies trill to note. +pitchof(note,accidental,mult,octave) -finds MIDI pitch value +setmap(sf,map,mult) - converts key signature to accidental map. +altermap(v,modmap,modmul) - modifies accidental map. +copymap(v) - sets up working accidental map at beginning of bar. + +addfeature(f,p,n,d) - places feature in internal tables. +autoextend(maxnotes) - increase memory limits for feature arrays. +textextend(maxstrings, stringarray) - resize array pointers to strings. +myputc(c) - workaround for problems with PCC compiler. +tiefix() - connect up tied notes. +dotie(j,xinchord) - called in preprocessing stage to handle ties. +addfrac(xnum,xdenom,a,b) - add a/b to the number of units in bar. +applybroken(place, type, n) - adjust length of broken notes. +brokenadjust() -adjust length of broken notes. +applygrace() - assign lengths to grace notes. +dograce() - assign lengths to grace notes. +lenmul(n, a, b) - multiply num(n),denom(n) by a/b. +zerobar() - start a new count of beats in the bar. +delendrep() - remove bogus repeat. +placeendrep(j) - patch up missing repeat. +placestartrep(j) - patch up missing repeat. +fixreps() - find and correct missing repeats in music. +startfile() - initialization performed after an event_refno. +tempounits(t_num, t_denom) - interprets Q: field. +setbeat() - sets default gchord command for time signature. +headerprocess() - called after the first K: field in tune. +finishfile() - starts next stage of processing when end of tune +is encountered. + +All the functions in this file respond to event calls from parseabc. +Once the internal representation of the abc file is completed, the +procedure finishfile is called to perform some clean up and create +the midi file. An internal representation of the midi file is +first created and then it is written onto the designated output file. +As finishfile provides the link to the next module, genmidi.c, here +is a brief description of what it does. + +proc finishfile performs several passes through the internal +representation to clean up the graces (dograce), the tied notes +(tiefix) and any unbalanced repeats. It then calls writetrack(i) +for each midi track to create the internal midi representation +and finally the midi representation is recorded in the output +file. + + + +genmidi.c +--------- +The procedure finishfile described above, creates each midi track +by calling the function writetrack which is defined here. To create +a track from the internal representation, the program must find all +the parts and put them in order with all the repeats. In addition, if +it contains karaoke text, this too must be placed in a separate track. +Any chordal accompaniment is generated from the guitar chord indications +and placed in another track. For multivoice and multipart music, a voice +may be missing in a particular part. If the voice is missing, the +procedure fillvoice ensures that all voices remain properly aligned when +the voice reappears in another part. + +Here is a simple road map to the important procedures included in this +file. + +dodeferred is here used to handle any dynamic indications (eg. !ppp!) +which may be contained in the file. The %%MIDI messages are stored +in a atext string which is pointed to by the contents of the pitch[] +array. + +checkbar is called each time a bar line is encountered and reports +a warning if the wrong number of beats occur. + +Transitions between parts are handled by the procedure partbreak. + +There are a number of procedures for handling karoake text -- +karaokestarttrack(), findwline(startline), getword(place,w), +write_syllable(place) and checksyllables(). + +For the first track, the meter, tempo and key signature are recorded +using the functions set_meter(n,m), write_meter(n,m), write_keysig(sf,mi). + +Chordal accompaniment is produced by the procedure dogchords(i). + + + +queues.c +-------- + +For each midi note, it is necessary to send a note-on and a note-off +instruction. When polyphonic music is played on the same track, keeping +track of the time to issue a note-off instruction may get complicated. +The procedures in this file are used to maintain a linked list for the +notes to be turned off. The notes are put into the list in the order +that they are encountered but the order in which to issue note-off +commands is maintained by the links. As many as 50 notes playing +simultaneously can be handled by the list. + + + + + +Addendum +-------- + +The following section contains clarifications on various components +of abc2midi. + +29 June 2003 + +Treatment of octave shifts. + +The key signature field command K: has provision for shifting +a note using either transpose= or octave= subcommands. Both +of these functions operate quite differently and deserve some +description especially for multivoiced tunes. + +The octave shift, is performed in event_note in store.c, using +the cached value v.octaveshift which is stored in the global +voicecontext structure, v. There is a structure for each voice +in the abc file. Whenever a new V: command is encountered, +event_voice (store.c) is invoked, which swaps the appropriate +voices structure into the global v array using the function +getvoicecontext(n). If getvoicecontext cannot find a cached +structure for that voice, then a new voice structure is created +and added to the linked list of voice structures. The v.octaveshift +variable is updated by event_octave which is called by event_key +(store.c) which is called by parsekey in parseabc.c +(Comment: it is not too clear how an octave switch is +handled in the middle of a repeat section. i.e. does the old +value get restored when repeating.) + +(Description of transpose shift is in CHANGES July 1 2003.) diff -Nru abcmidi-20200706/doc/programming/coding.txt abcmidi-20200728/doc/programming/coding.txt --- abcmidi-20200706/doc/programming/coding.txt 2016-03-15 20:02:59.000000000 +0000 +++ abcmidi-20200728/doc/programming/coding.txt 2020-07-27 15:52:43.000000000 +0000 @@ -1,426 +1,426 @@ -Notes on the code ------------------ - -These notes are for anyone who wants to re-compile, re-write or re-use -bits of the code. Additional information is available by downloading -the file abcextra.zip. This includes : - -* man pages for abc2midi and midi2abc. -* A detailed description of the inner workings of abc2midi, written by - Seymour Shlien. - -Compilation ------------ - -The file midifile.c and the header file midifile.h are slightly changed from -the midifilelib distribution. To see the program dependencies, examine the -Makefile. - -The code is written in K&R style C and should be fairly easy to compile -on any system with a C compiler and a make utility. Makefiles are -provided for gcc/unix, DJGPP/DOS and PCC/DOS. There are also some notes -on using the GUI front-end to Pacific C/DOS. You may get warning -messages if your compiler prefers ANSI C style function prototypes. - -Choose the most suitable makefile; unix.mak, djgpp.mak or pcc.mak and -rename it as 'makefile'. If you are not using any of the above compilers, -you may have to edit the makefile so that it uses compilation flags -suitable for your compiler. - -To compile the code, type - -make all - -If the complete compilation does not work, you can try compiling the -individual programs separately : - -make midi2abc (or make midi2abc.exe) -make abc2midi (or make abc2midi.exe) -make abc2abc (or make abc2abc.exe) -make mftext (or make mftext.exe) -make yaps (or make yaps.exe) - -Note that the make utility on some systems (e.g. GNU make) may require the -Makefile to be a unix text file and not a DOS text file. The difference -is that unix uses newline to mark the end of each line while DOS uses -carriage return and newline. - -Note, if you have difficulty compiling the package because you do not have -snprintf see the note in doc/CHANGES dated January 08 2005 (and also -December 17 2004). - - ---------------------------------------------------------------------- -Calling abc2midi or midi2abc from a GUI. ----------------------------------------- - -The programs should now have an exit value of 0 for normal termination -and 1 for an error state. However, abc2midi will still exit with 0 if -it finds non-fatal errors in the code, so the user should always have -the option of looking at the program's text output (I don't want to -get blamed when useful diagnostic output turns into the ubiquitous -'OK' click-button). - ----------------------------------------------------------------------- -Man pages ---------- -Files: abc2midi.1 and midi2abc.1 - -Christoph Dalitz has written some man pages for abc2midi and -midi2abc. These should be installed in the sub-directory /man1 -of the directory given by the MANPATH environment variable. -(The man command is usually only found on Unix variants). - -The source distribution has been re-organized to contain only the -source and a few text files. If you want these man pages, you need -to download the file abcextra.zip. - ---------------------------------------------------------------------- -Code Layout and Indentation style ---------------------------------- -If you want to add your own code and make it fit in with the existing -coding style, you can use GNU indent. The following is a DOS batch file -to invoke indent with the appropriate options. - -indent -bad -bap -br -ce -cli0 -npcs -di1 -nbc -i2 -ts0 -psl -lp -ipo %1 -rem -rem options for GNU indent to achieve my personal style -rem -rem -bad blank line after declaration block -rem -bap blank line after procedure body -rem -br brace on same line after if, struct and enum -rem -ce cuddle else -rem -cli0 case at same identation as switch -rem -npcs no space between procedure name and following open bracket -rem -di1 one space between variable type and variable name -rem -nbc comma-separated variables on the same line -rem -i2 indent 2 spaces -rem -ts0 no tabs -rem -npsl function type on same line as function name -rem -lp continuations matched to left parenthesis -rem -ip0 no indention of variables in K&R function headers -rem -ncs no space after cast - ---------------------------------------------------------------------- - -Extensions to the abc Format ----------------------------- - -1. The parser recognizes -%%package -as some package-specific command and calls event_specific with the -package name and the string that follows it. - -2. The abc standard defines notation for 4 octaves : - -C, - B, -C - B -c - b -c' - b' - -The parser recognizes each additional comma as meaning "going down -an extra octave", giving - -C,, - B,, -C,,, - B,,, -and so on. - -Likewise, each additional prime symbols s interpreted as "go up an extra -octave" : - -c'' - b'' -c''' - b''' -and so on. - ----------------------------------------------------------------------- - -abc2midi --------- -abc2midi consists of the following C source files: - -parseabc.c - parses the input text and calls a routine for - every parsed element encountered. -parser2.c - performs some additional parsing that may not be - required. -store.c - builds an internal representation of the abc tune. -genmidi.c - uses the internal representation to generate - the MIDI file, using calls to MIDI-specific routines - in midifile.c -queues.c - library of routines for handling 'queues', a data - structure used internally by genmidi.c -midifile.c - Thompson and Czeisperger's public domain library of - MIDI file manipulation routines. - -In the first phase of parsing, the abc file is read and when, say, a note -is encountered, the routine event_note() is called. The code for event_note -is in the file store.c. Encountering an X in the abc generally causes a -routine called event_X() to be called. abc2midi builds up an internal -representation of the tune. At the end of the abc tune, a little bit of -processing is done on the internal representation before the routine -writetrack() is called to actually write out the MIDI file. If there are -repeats in the music, something that appears only once in the abc may be -invoked twice by writetrack(). - -The internal representation uses the arrays feature[], pitch[], num[], -and denom[]. feature[] holds a description of an object while the other -arrays hold data relating to the object. The list of possible features -can be found in the file abc.h . The main features are NOTE, a note of -specified duration, REST, a pause of specified duration and TNOTE, a -note of specified duration with no interval between when it starts and -when the next item starts. This provides a simple way of representing -chords. Ties, broken rhythm signs and grace note brackets are all -deal with before writetrack() is called. - -To add your own special features, you could define a new feature type. -However, an easier option is to use the %%MIDI format. If the parser -encounters "%%MIDI command", where command is not recognized by the -routine event_specific(), the string following %%MIDI is stored away -and passed to the routine dodeferred() by writetrack() when the MIDI -file is being written. ----------------------------------------------------------------------- -abc2abc -------- -abc2abc shares the parser with abc2midi, but instead of storing the -abc code in an internal format, it is written almost straight out -again. The components of abc2abc are: - -parseabc.c - parser -toabc.c - generates output abc. -midifile.c - public domain MIDI library. - ----------------------------------------------------------------------- -YAPS ----- - -YAPS is written mainly using ANSI C style function headers and -compiled and tested using DJGPP (DOS/Windows port of gcc). - -The code is composed of the following files: - -parseabc.c - reads through the abc text file. When it recognizes an abc -"unit" it calls a routine such as event_note() or event_key(). - -yapstree.c - creates a data structure to represent a tune. -Generally, I have tried to use as small a number of passes through the -data structure as possible. This means that things like applying a -broken rhythm symbol > is done as the notes are read in, rather than -using a second pass. This results in a lot of variables being needed in -'struct voice' to keep track of what is going on as we read in the -current symbols. Different sets of these variables are used on different -passes through the data. - -drawtune.c - responsible for creating the PostScript file. When a whole -tune has been read in, this calculates the size of each individual -element, works out spacing within each line, decides how to place the beam -for a group of beamed notes, then finally generates the PostScript for -the tune. - -position.c - called by drawtune.c. This set of routines is responsible -for spacing each line correctly. Where the input abc is multi-voice, -elements played at the same time but in different voices are aligned. - -pslib.c - a single routine to print out the entire library of PostScript -functions used by yaps. - -debug.c - routines to print to screen the contents of a tune data -structure. - -abc.h - header file defining abc element types. - -structs.h - header file defining the data structures used by the program. -The voice structure holds a lot of cuurent context information, used by -the program as it does a pass through the voice. - -sizes.h - header file containing macros to define sizes in points for all -the graphic elements. - -Dynamic Memory Management -------------------------- -abc2midi uses a system of re-sizable arrays in order to handle arbitrary -size input. This scheme was a natural way to adapt the original fixed size -arrays, and is not as flexible as it might be. Yaps instead uses linked -lists. There is a tune data structure containing a linked list of voices -and the notes and other elements are stored in a linked list in each tune. -Some music elements contain their own linked lists; for example a note -may have a linked list of lyric syllables. Adding a new data item to an -element (e.g. a note) involves the following : - -1. find 'struct note' in structs.h and add the element. -2. Initialize it in newnote(). -3. If it is a pointer to a new data structure, make sure the new data - structure is de-allocated in freevoice(). - ----------------------------------------------------------------------- -The abc parser --------------- -The parser (parseabc.c) has been written in such a way that it forms -an independent unit which must be linked with routines to handle -parsed units. This is very similar to the way that the -midifilelib utilities work. - -The abc is parsed line by line. Each line may be - -* A comment -* A package-specific command -* A field (which may have a trailing comment) -* A blank line -* A TeX command - -Having detected one of these, the parser calls an appropriate -routine. If it is none of these, then within the tune body it is - -* A line of music (which may have a trailing comment). - -Which is parsed and individual elements recognized. Outside the tune -body it is - -* A line of arbitrary text. - -and an appropriate routine is called. - -Routines called by the parser ------------------------------ -These may be a bit out of date - look in the file parseabc.c for the actual -interfaces. - -event_init(argc, argv, filename) -int argc; -char* argv[]; -char** filename; -- first routine called by the parser. Expects filename to be the name of the - abc file to parse on return. - -event_text(s) -char *s; -- called whenever a line of text is encountered. - -event_tex(s) -char *s; -- called whenever a TeX command is encountered. - -event_linebreak() -- called whenever a newline character is encountered. - -event_blankline() -- called whenever a blank line is encountered. - -event_eof() -- called when the end of file is reached. - -event_error(s) -char *s; -- called whenever an error condition is detected. Errors are - generally not fatal within the parser. - -event_warning(s) -char *s; -- called whenever a condition which is likely to be an error is - detected. - -event_comment(s) -char *s; -- called whenever a comment is encountered. - -The following are calls are invoked by fields in the abc : - -event_specific(package, s) -char *package, *s; -- recognizes %%package s - -event_length(n) -int n; -- recognizes L: - -event_refno(n) -int n; -- recognizes X: - -event_tempo(n, a, b, rel) -int n, a, b; -int relative; -- recognizes Q: - -event_timesig(n, m) -int n, m; -- recognizes M: - -event_key(sharps, s, minor) -int sharps; -char *s; -int minor; -- recognizes K: - -event_part(s) -char* s; -- recognizes P: - -When any other field is encountered in the abc : - -event_field(k, f) -char k; -char *f; - -If a line of music is encountered, the elements of that line each -trigger a call to one of the following events, provided that parsing -of music lines has been enabled : - -event_graceon() - -event_graceoff() - -event_rep1() - -event_rep2() - -event_slur(t) -int t; - -event_tie() - -event_rest(n,m) -int n, m; - -event_bar(type) -int type; - -event_space() - -event_lineend(ch, n) -char ch; -int n; - -event_broken(type, mult) -int type, n; - -event_tuple(p, q, r) -int p, q, r; -- general tuple: q and r are zero if not present - -event_chord() -- called whenever + is encountered. - -event_chordon() -- called whenever [ is encountered. - -event_chordoff() -- called whenever ] is encountered. - -event_gchord(s) -char* s; - -event_reserved(p) -char p; - -event_note(roll, staccato, updown, accidental, mult, note, octave, n, m) -int roll, staccato, mult; -char updown, accidental, note; -int octave, n, m; - -In addition, there are 2 other routines : - -int getline() - returns the line currently being parsed - -parseron() - enable parsing of music lines. - -parseroff() - disable parsing of music lines. - +Notes on the code +----------------- + +These notes are for anyone who wants to re-compile, re-write or re-use +bits of the code. Additional information is available by downloading +the file abcextra.zip. This includes : + +* man pages for abc2midi and midi2abc. +* A detailed description of the inner workings of abc2midi, written by + Seymour Shlien. + +Compilation +----------- + +The file midifile.c and the header file midifile.h are slightly changed from +the midifilelib distribution. To see the program dependencies, examine the +Makefile. + +The code is written in K&R style C and should be fairly easy to compile +on any system with a C compiler and a make utility. Makefiles are +provided for gcc/unix, DJGPP/DOS and PCC/DOS. There are also some notes +on using the GUI front-end to Pacific C/DOS. You may get warning +messages if your compiler prefers ANSI C style function prototypes. + +Choose the most suitable makefile; unix.mak, djgpp.mak or pcc.mak and +rename it as 'makefile'. If you are not using any of the above compilers, +you may have to edit the makefile so that it uses compilation flags +suitable for your compiler. + +To compile the code, type + +make all + +If the complete compilation does not work, you can try compiling the +individual programs separately : + +make midi2abc (or make midi2abc.exe) +make abc2midi (or make abc2midi.exe) +make abc2abc (or make abc2abc.exe) +make mftext (or make mftext.exe) +make yaps (or make yaps.exe) + +Note that the make utility on some systems (e.g. GNU make) may require the +Makefile to be a unix text file and not a DOS text file. The difference +is that unix uses newline to mark the end of each line while DOS uses +carriage return and newline. + +Note, if you have difficulty compiling the package because you do not have +snprintf see the note in doc/CHANGES dated January 08 2005 (and also +December 17 2004). + + +--------------------------------------------------------------------- +Calling abc2midi or midi2abc from a GUI. +---------------------------------------- + +The programs should now have an exit value of 0 for normal termination +and 1 for an error state. However, abc2midi will still exit with 0 if +it finds non-fatal errors in the code, so the user should always have +the option of looking at the program's text output (I don't want to +get blamed when useful diagnostic output turns into the ubiquitous +'OK' click-button). + +---------------------------------------------------------------------- +Man pages +--------- +Files: abc2midi.1 and midi2abc.1 + +Christoph Dalitz has written some man pages for abc2midi and +midi2abc. These should be installed in the sub-directory /man1 +of the directory given by the MANPATH environment variable. +(The man command is usually only found on Unix variants). + +The source distribution has been re-organized to contain only the +source and a few text files. If you want these man pages, you need +to download the file abcextra.zip. + +--------------------------------------------------------------------- +Code Layout and Indentation style +--------------------------------- +If you want to add your own code and make it fit in with the existing +coding style, you can use GNU indent. The following is a DOS batch file +to invoke indent with the appropriate options. + +indent -bad -bap -br -ce -cli0 -npcs -di1 -nbc -i2 -ts0 -psl -lp -ipo %1 +rem +rem options for GNU indent to achieve my personal style +rem +rem -bad blank line after declaration block +rem -bap blank line after procedure body +rem -br brace on same line after if, struct and enum +rem -ce cuddle else +rem -cli0 case at same identation as switch +rem -npcs no space between procedure name and following open bracket +rem -di1 one space between variable type and variable name +rem -nbc comma-separated variables on the same line +rem -i2 indent 2 spaces +rem -ts0 no tabs +rem -npsl function type on same line as function name +rem -lp continuations matched to left parenthesis +rem -ip0 no indention of variables in K&R function headers +rem -ncs no space after cast + +--------------------------------------------------------------------- + +Extensions to the abc Format +---------------------------- + +1. The parser recognizes +%%package +as some package-specific command and calls event_specific with the +package name and the string that follows it. + +2. The abc standard defines notation for 4 octaves : + +C, - B, +C - B +c - b +c' - b' + +The parser recognizes each additional comma as meaning "going down +an extra octave", giving + +C,, - B,, +C,,, - B,,, +and so on. + +Likewise, each additional prime symbols s interpreted as "go up an extra +octave" : + +c'' - b'' +c''' - b''' +and so on. + +---------------------------------------------------------------------- + +abc2midi +-------- +abc2midi consists of the following C source files: + +parseabc.c - parses the input text and calls a routine for + every parsed element encountered. +parser2.c - performs some additional parsing that may not be + required. +store.c - builds an internal representation of the abc tune. +genmidi.c - uses the internal representation to generate + the MIDI file, using calls to MIDI-specific routines + in midifile.c +queues.c - library of routines for handling 'queues', a data + structure used internally by genmidi.c +midifile.c - Thompson and Czeisperger's public domain library of + MIDI file manipulation routines. + +In the first phase of parsing, the abc file is read and when, say, a note +is encountered, the routine event_note() is called. The code for event_note +is in the file store.c. Encountering an X in the abc generally causes a +routine called event_X() to be called. abc2midi builds up an internal +representation of the tune. At the end of the abc tune, a little bit of +processing is done on the internal representation before the routine +writetrack() is called to actually write out the MIDI file. If there are +repeats in the music, something that appears only once in the abc may be +invoked twice by writetrack(). + +The internal representation uses the arrays feature[], pitch[], num[], +and denom[]. feature[] holds a description of an object while the other +arrays hold data relating to the object. The list of possible features +can be found in the file abc.h . The main features are NOTE, a note of +specified duration, REST, a pause of specified duration and TNOTE, a +note of specified duration with no interval between when it starts and +when the next item starts. This provides a simple way of representing +chords. Ties, broken rhythm signs and grace note brackets are all +deal with before writetrack() is called. + +To add your own special features, you could define a new feature type. +However, an easier option is to use the %%MIDI format. If the parser +encounters "%%MIDI command", where command is not recognized by the +routine event_specific(), the string following %%MIDI is stored away +and passed to the routine dodeferred() by writetrack() when the MIDI +file is being written. +---------------------------------------------------------------------- +abc2abc +------- +abc2abc shares the parser with abc2midi, but instead of storing the +abc code in an internal format, it is written almost straight out +again. The components of abc2abc are: + +parseabc.c - parser +toabc.c - generates output abc. +midifile.c - public domain MIDI library. + +---------------------------------------------------------------------- +YAPS +---- + +YAPS is written mainly using ANSI C style function headers and +compiled and tested using DJGPP (DOS/Windows port of gcc). + +The code is composed of the following files: + +parseabc.c - reads through the abc text file. When it recognizes an abc +"unit" it calls a routine such as event_note() or event_key(). + +yapstree.c - creates a data structure to represent a tune. +Generally, I have tried to use as small a number of passes through the +data structure as possible. This means that things like applying a +broken rhythm symbol > is done as the notes are read in, rather than +using a second pass. This results in a lot of variables being needed in +'struct voice' to keep track of what is going on as we read in the +current symbols. Different sets of these variables are used on different +passes through the data. + +drawtune.c - responsible for creating the PostScript file. When a whole +tune has been read in, this calculates the size of each individual +element, works out spacing within each line, decides how to place the beam +for a group of beamed notes, then finally generates the PostScript for +the tune. + +position.c - called by drawtune.c. This set of routines is responsible +for spacing each line correctly. Where the input abc is multi-voice, +elements played at the same time but in different voices are aligned. + +pslib.c - a single routine to print out the entire library of PostScript +functions used by yaps. + +debug.c - routines to print to screen the contents of a tune data +structure. + +abc.h - header file defining abc element types. + +structs.h - header file defining the data structures used by the program. +The voice structure holds a lot of cuurent context information, used by +the program as it does a pass through the voice. + +sizes.h - header file containing macros to define sizes in points for all +the graphic elements. + +Dynamic Memory Management +------------------------- +abc2midi uses a system of re-sizable arrays in order to handle arbitrary +size input. This scheme was a natural way to adapt the original fixed size +arrays, and is not as flexible as it might be. Yaps instead uses linked +lists. There is a tune data structure containing a linked list of voices +and the notes and other elements are stored in a linked list in each tune. +Some music elements contain their own linked lists; for example a note +may have a linked list of lyric syllables. Adding a new data item to an +element (e.g. a note) involves the following : + +1. find 'struct note' in structs.h and add the element. +2. Initialize it in newnote(). +3. If it is a pointer to a new data structure, make sure the new data + structure is de-allocated in freevoice(). + +---------------------------------------------------------------------- +The abc parser +-------------- +The parser (parseabc.c) has been written in such a way that it forms +an independent unit which must be linked with routines to handle +parsed units. This is very similar to the way that the +midifilelib utilities work. + +The abc is parsed line by line. Each line may be + +* A comment +* A package-specific command +* A field (which may have a trailing comment) +* A blank line +* A TeX command + +Having detected one of these, the parser calls an appropriate +routine. If it is none of these, then within the tune body it is + +* A line of music (which may have a trailing comment). + +Which is parsed and individual elements recognized. Outside the tune +body it is + +* A line of arbitrary text. + +and an appropriate routine is called. + +Routines called by the parser +----------------------------- +These may be a bit out of date - look in the file parseabc.c for the actual +interfaces. + +event_init(argc, argv, filename) +int argc; +char* argv[]; +char** filename; +- first routine called by the parser. Expects filename to be the name of the + abc file to parse on return. + +event_text(s) +char *s; +- called whenever a line of text is encountered. + +event_tex(s) +char *s; +- called whenever a TeX command is encountered. + +event_linebreak() +- called whenever a newline character is encountered. + +event_blankline() +- called whenever a blank line is encountered. + +event_eof() +- called when the end of file is reached. + +event_error(s) +char *s; +- called whenever an error condition is detected. Errors are + generally not fatal within the parser. + +event_warning(s) +char *s; +- called whenever a condition which is likely to be an error is + detected. + +event_comment(s) +char *s; +- called whenever a comment is encountered. + +The following are calls are invoked by fields in the abc : + +event_specific(package, s) +char *package, *s; +- recognizes %%package s + +event_length(n) +int n; +- recognizes L: + +event_refno(n) +int n; +- recognizes X: + +event_tempo(n, a, b, rel) +int n, a, b; +int relative; +- recognizes Q: + +event_timesig(n, m) +int n, m; +- recognizes M: + +event_key(sharps, s, minor) +int sharps; +char *s; +int minor; +- recognizes K: + +event_part(s) +char* s; +- recognizes P: + +When any other field is encountered in the abc : + +event_field(k, f) +char k; +char *f; + +If a line of music is encountered, the elements of that line each +trigger a call to one of the following events, provided that parsing +of music lines has been enabled : + +event_graceon() + +event_graceoff() + +event_rep1() + +event_rep2() + +event_slur(t) +int t; + +event_tie() + +event_rest(n,m) +int n, m; + +event_bar(type) +int type; + +event_space() + +event_lineend(ch, n) +char ch; +int n; + +event_broken(type, mult) +int type, n; + +event_tuple(p, q, r) +int p, q, r; +- general tuple: q and r are zero if not present + +event_chord() +- called whenever + is encountered. + +event_chordon() +- called whenever [ is encountered. + +event_chordoff() +- called whenever ] is encountered. + +event_gchord(s) +char* s; + +event_reserved(p) +char p; + +event_note(roll, staccato, updown, accidental, mult, note, octave, n, m) +int roll, staccato, mult; +char updown, accidental, note; +int octave, n, m; + +In addition, there are 2 other routines : + +int getline() - returns the line currently being parsed + +parseron() - enable parsing of music lines. + +parseroff() - disable parsing of music lines. + diff -Nru abcmidi-20200706/doc/programming/midi2abc.txt abcmidi-20200728/doc/programming/midi2abc.txt --- abcmidi-20200706/doc/programming/midi2abc.txt 2019-06-27 13:38:55.000000000 +0000 +++ abcmidi-20200728/doc/programming/midi2abc.txt 2020-07-27 15:52:43.000000000 +0000 @@ -1,227 +1,227 @@ -Notes on the midi2abc code --------------------------- -written by Seymour Shlien - - -midi2abc.txt - last updated December 5 1999 - - -Introduction - -Midi2abc is a program for converting a midi files into an abc file. - -A midi file merely consists of a list of commands to turn on -and off a set of voices at specific times. The time units are -expressed in pulses where the number of pulses per second can -be deduced from the information in the midi file. Pitch is -specified in semitone units ranging from 0 to 127 where middle C -is assigned a value of 60. - -There are two types of midi in general use. In type 0, all the -voices are interleaved in time in one track. Each voice goes -to a specific channel which is mapped to a particular musical -instrument or program. There is a limit of 16 voices since only -4 bits are assigned to indicate the channel number. In type 2, -the voices are placed in separate tracks. This format allows -handling more than 16 voices. - -In order to create an abc file, midi2abc must determine the -length of a musical unit (for example an eighth note) in terms -of midi pulses, determine the key signature, and, finally map the -midi commands into musical notes. - -Though the midi file specifies the number of pulses per quarter note -(PPQN) in the Mthd header chunk, this is not necessarily an -accurate indication. It is primarily used together with the tempo -indication to determine the number of pulses per second. Unless -the midi file was created by a computer program, the actual length -of a quarter note in midi pulses can depart from the nominal indication. -Midi2abc tries to determine the length using its own heuristic -algorithm. - -The sources of the program are contained in two files: midi2abc.c and -midifile.c. This document does not try to describe completely how -this program works, but is more of a general road map to the software. - - -Algorithmic Description - -The conversion is done in multiple passes. In the first pass, the -midi file is read and an internal representation of the file is -created and stored in global dynamic memory. In each subsequent pass, -other information is added to the internal representation and finally -it is written to the output abc file. - -After parsing the input run parameters and performing various -initializations, the main program calls the procedure mfread -which is found in the midifile.c module. Mfread parses the different -midi components of the file and calls the appropriate functions -midi2abc to process these components. These functions begin with -the txt_ prefix, some of which are listed here. - -txt_header midi header chunk -txt_trackstart start of midi track -txt_trackend end of midi track -txt_noteon note on channel command -txt_noteoff note off channel command -txt_program midi program definition or change -txt_metatext midi textual information -txt_keysig midi key signature indication -txt_tempo midi tempo indication - -Many other functions such as txt_pressure and txt_parameter -corresponding to other midi commands, do nothing here. - -These functions build up an internal representation of the midi file -in a global structure referenced through the track[64] structure. -Up to 64 midi tracks can be stored in the internal representation. -Each track structure (atrack structure) contains single and double -linked lists of notes or text strings which are described below. -In addition there are other linked lists (referenced by playinghead -and chordhead), used for keeping track of notes currently playing -and musical chords. - -Each note is represented by an anote structure which stores various -parameters of the note. The anote structure is allocated dynamically -as the midi file is read. The first four entries of the structure -are filled in while the structures are being created by mfread. -These are entries are listed below. - -anote.pitch pitch in midi units -anote.chan midi channel number -anote.vel midi velocity (corresponding to loudness) -anote.time time in midi pulses. - -The other entries in the anote structure are determined in later passes. -The anote structures are linked together into a a listx structure which -is included in the atrack structure. The head and tail of the list are -contained in this structure to facilitate the construction of this list. -In addition there is tlistx structure for storing all the textual -information (for example words in a Karaoke midi file) which may be found -in the track. - -There is a doubly linked list structure of anotes (dlistx, -*playinghead) which is used as a work space for matching the note off -command with the corresponding note on command. This is needed in order -to determine the length of time the note was turned on (called anote.tplay). - -The internal representation is mainly constructed by the important -functions txt_noteon and txt_noteoff called by mfread. These functions -in turn call the functions addnote and notestop. The midi file contains -separate commands for turning a note on or off so that in order to -determine the length of time that a note has been on, it is necessary to -match a note-off command with the corresponding note-on command. -Every time a note is turned on, it is also added to the playhead (tail) -structure. The procedure notestop finds the corresponding note-on -command in the playhead structure, removes it, and records the -duration of the note which was turned on. - -At the end of the first pass, the number of tracks is counted and each -track is processed by the function postprocess which computes the entry -anote.dtnext for each anote structure. This entry contains the time -interval between the current and the next note in the linked list of -notes. - -The abc time unit length is either 1/8 or 1/16 depending on the time -signature. A time signature of 4/4 is assumed unless it is specified -by the run time parameters (-m or -xm). (If -xm is specified, then the -program uses the time signature given by the midi meta command if it -exists in the file.) - -The next step involves the quantization of the midi time units -expressed in pulse counts into abc time units. It is first necessary -to estimate the length of an abc time unit in terms of midi time -units. This either is estimated using a heuristic algorithm, -guesslength, or is determined from the run time parameters (-Q or -b). - -The function guesslength makes an initial guess by dividing the -total number of pulse counts in the track by the total number -of notes. It then tries 100 different lengths in the neighbourhood -of this initial guess and chooses the one which leads to the smallest -quantization error. The quantization error is determined by the function -quantizer which keeps track of the total deviation of the time -a note starts (in pulse counts) from the expected time the note -starts assuming the standard musical intervals. This deviation -can either drift positively or negatively with time. The total -error is determined by summing the absolute values of these -deviations for each bar. - -Once the unit length has been determined, all the notes in all -tracks are quantized by the function quantizer. This function -assigns values to the anote entries, anote.xnum, anote.playnum -and anote.denom. - -anote.xnum interval between the current note and following note - also called the gap. -anote.playnum note duration. -anote.denom always has the value of 2. - -A musical part does not necessarily begin at the start of a bar line, -but may have some leading notes. This is called anacrusis. -There are two methods to estimate the anacrusis. The function findana -searches for the first upbeat by examining the velocities (loudness) of -the notes. The function guessana, chooses the anacrusis which minimizes -the number of tied notes across a bar line which is determined by the -function testtrack. - -At this point the key signature of the tune must be determined. -The procedure findkey computes the frequency distribution of all the -notes in the piece and stores it in the local array n[]. The key -signature is determined by transposing the distribution by each -of the 12 keys and counting the number of sharp or flat notes. The -key signature is determined from the key which leads to the minimum -number of black keys on the piano. The mode of the scale (major, -minor, Dorian, etc.) is determined by looking at the final note of -the piece. - -Once the key signature is determined, the assumed flats or sharps -are determined by the procedure setupkey. The program is now ready -for its final pass where the musical parts (or voices) are printed -in the output abc file. - -The procedure printtrack processes the internal representation -of each midi track producing a separate voice for each track. -In order to handle chords which may be present in an individual -track, printtrack maintains a structure referenced by chordhead -by calling the support functions addchord(), advancechord(), -removechord() and printchord(). These functions handle single -notes as well as chords. Another function, dospecial(), handles -the triplets and broken rhythms (eg. dotted notes followed by -half sized note or vice versa) whenever they are detected. The -printchord() function is supported by the functions printfraction() -and printpitch(). - -After the track is printed, the memory allocated to the structures -for the internal representation of the tracks is freed. - -The option -splitvoice was introduced to handle polyphonic chords. -Without this option polyphonic chords appear in the abc file -like this. - -[DF-A-][FA-]Az| - -this will be represented by three voices - -V: split1A -D2 z6| -V: split1B -F4 z4| -V: split1C -A6 z2| - -This option is implemented by the function printtrack_split_voice(). -The function label_split_voices() is called to assign all the notes -in the track to their split voice. This assignment of each note is -stored in note->splitnum. This is a complex function since it needs -to try to match the onset and end of each note in the chord. If -it is unable to do this for a particular note, then the note is -assigned to a different split. At the end, nsplits were created. -The notes in each split are printed in a separate voice by the -function printtrack_split(). The function printtrack_split(splitnumber) -was derived from printtrack(); however, it only processes the -notes belonging to a particular splitnumber. - -It was necessary to determine and store the offset of the first -note in each split using the function set_first_gaps() prior -to calling printtrack_split(). - +Notes on the midi2abc code +-------------------------- +written by Seymour Shlien + + +midi2abc.txt - last updated December 5 1999 + + +Introduction + +Midi2abc is a program for converting a midi files into an abc file. + +A midi file merely consists of a list of commands to turn on +and off a set of voices at specific times. The time units are +expressed in pulses where the number of pulses per second can +be deduced from the information in the midi file. Pitch is +specified in semitone units ranging from 0 to 127 where middle C +is assigned a value of 60. + +There are two types of midi in general use. In type 0, all the +voices are interleaved in time in one track. Each voice goes +to a specific channel which is mapped to a particular musical +instrument or program. There is a limit of 16 voices since only +4 bits are assigned to indicate the channel number. In type 2, +the voices are placed in separate tracks. This format allows +handling more than 16 voices. + +In order to create an abc file, midi2abc must determine the +length of a musical unit (for example an eighth note) in terms +of midi pulses, determine the key signature, and, finally map the +midi commands into musical notes. + +Though the midi file specifies the number of pulses per quarter note +(PPQN) in the Mthd header chunk, this is not necessarily an +accurate indication. It is primarily used together with the tempo +indication to determine the number of pulses per second. Unless +the midi file was created by a computer program, the actual length +of a quarter note in midi pulses can depart from the nominal indication. +Midi2abc tries to determine the length using its own heuristic +algorithm. + +The sources of the program are contained in two files: midi2abc.c and +midifile.c. This document does not try to describe completely how +this program works, but is more of a general road map to the software. + + +Algorithmic Description + +The conversion is done in multiple passes. In the first pass, the +midi file is read and an internal representation of the file is +created and stored in global dynamic memory. In each subsequent pass, +other information is added to the internal representation and finally +it is written to the output abc file. + +After parsing the input run parameters and performing various +initializations, the main program calls the procedure mfread +which is found in the midifile.c module. Mfread parses the different +midi components of the file and calls the appropriate functions +midi2abc to process these components. These functions begin with +the txt_ prefix, some of which are listed here. + +txt_header midi header chunk +txt_trackstart start of midi track +txt_trackend end of midi track +txt_noteon note on channel command +txt_noteoff note off channel command +txt_program midi program definition or change +txt_metatext midi textual information +txt_keysig midi key signature indication +txt_tempo midi tempo indication + +Many other functions such as txt_pressure and txt_parameter +corresponding to other midi commands, do nothing here. + +These functions build up an internal representation of the midi file +in a global structure referenced through the track[64] structure. +Up to 64 midi tracks can be stored in the internal representation. +Each track structure (atrack structure) contains single and double +linked lists of notes or text strings which are described below. +In addition there are other linked lists (referenced by playinghead +and chordhead), used for keeping track of notes currently playing +and musical chords. + +Each note is represented by an anote structure which stores various +parameters of the note. The anote structure is allocated dynamically +as the midi file is read. The first four entries of the structure +are filled in while the structures are being created by mfread. +These are entries are listed below. + +anote.pitch pitch in midi units +anote.chan midi channel number +anote.vel midi velocity (corresponding to loudness) +anote.time time in midi pulses. + +The other entries in the anote structure are determined in later passes. +The anote structures are linked together into a a listx structure which +is included in the atrack structure. The head and tail of the list are +contained in this structure to facilitate the construction of this list. +In addition there is tlistx structure for storing all the textual +information (for example words in a Karaoke midi file) which may be found +in the track. + +There is a doubly linked list structure of anotes (dlistx, +*playinghead) which is used as a work space for matching the note off +command with the corresponding note on command. This is needed in order +to determine the length of time the note was turned on (called anote.tplay). + +The internal representation is mainly constructed by the important +functions txt_noteon and txt_noteoff called by mfread. These functions +in turn call the functions addnote and notestop. The midi file contains +separate commands for turning a note on or off so that in order to +determine the length of time that a note has been on, it is necessary to +match a note-off command with the corresponding note-on command. +Every time a note is turned on, it is also added to the playhead (tail) +structure. The procedure notestop finds the corresponding note-on +command in the playhead structure, removes it, and records the +duration of the note which was turned on. + +At the end of the first pass, the number of tracks is counted and each +track is processed by the function postprocess which computes the entry +anote.dtnext for each anote structure. This entry contains the time +interval between the current and the next note in the linked list of +notes. + +The abc time unit length is either 1/8 or 1/16 depending on the time +signature. A time signature of 4/4 is assumed unless it is specified +by the run time parameters (-m or -xm). (If -xm is specified, then the +program uses the time signature given by the midi meta command if it +exists in the file.) + +The next step involves the quantization of the midi time units +expressed in pulse counts into abc time units. It is first necessary +to estimate the length of an abc time unit in terms of midi time +units. This either is estimated using a heuristic algorithm, +guesslength, or is determined from the run time parameters (-Q or -b). + +The function guesslength makes an initial guess by dividing the +total number of pulse counts in the track by the total number +of notes. It then tries 100 different lengths in the neighbourhood +of this initial guess and chooses the one which leads to the smallest +quantization error. The quantization error is determined by the function +quantizer which keeps track of the total deviation of the time +a note starts (in pulse counts) from the expected time the note +starts assuming the standard musical intervals. This deviation +can either drift positively or negatively with time. The total +error is determined by summing the absolute values of these +deviations for each bar. + +Once the unit length has been determined, all the notes in all +tracks are quantized by the function quantizer. This function +assigns values to the anote entries, anote.xnum, anote.playnum +and anote.denom. + +anote.xnum interval between the current note and following note + also called the gap. +anote.playnum note duration. +anote.denom always has the value of 2. + +A musical part does not necessarily begin at the start of a bar line, +but may have some leading notes. This is called anacrusis. +There are two methods to estimate the anacrusis. The function findana +searches for the first upbeat by examining the velocities (loudness) of +the notes. The function guessana, chooses the anacrusis which minimizes +the number of tied notes across a bar line which is determined by the +function testtrack. + +At this point the key signature of the tune must be determined. +The procedure findkey computes the frequency distribution of all the +notes in the piece and stores it in the local array n[]. The key +signature is determined by transposing the distribution by each +of the 12 keys and counting the number of sharp or flat notes. The +key signature is determined from the key which leads to the minimum +number of black keys on the piano. The mode of the scale (major, +minor, Dorian, etc.) is determined by looking at the final note of +the piece. + +Once the key signature is determined, the assumed flats or sharps +are determined by the procedure setupkey. The program is now ready +for its final pass where the musical parts (or voices) are printed +in the output abc file. + +The procedure printtrack processes the internal representation +of each midi track producing a separate voice for each track. +In order to handle chords which may be present in an individual +track, printtrack maintains a structure referenced by chordhead +by calling the support functions addchord(), advancechord(), +removechord() and printchord(). These functions handle single +notes as well as chords. Another function, dospecial(), handles +the triplets and broken rhythms (eg. dotted notes followed by +half sized note or vice versa) whenever they are detected. The +printchord() function is supported by the functions printfraction() +and printpitch(). + +After the track is printed, the memory allocated to the structures +for the internal representation of the tracks is freed. + +The option -splitvoice was introduced to handle polyphonic chords. +Without this option polyphonic chords appear in the abc file +like this. + +[DF-A-][FA-]Az| + +this will be represented by three voices + +V: split1A +D2 z6| +V: split1B +F4 z4| +V: split1C +A6 z2| + +This option is implemented by the function printtrack_split_voice(). +The function label_split_voices() is called to assign all the notes +in the track to their split voice. This assignment of each note is +stored in note->splitnum. This is a complex function since it needs +to try to match the onset and end of each note in the chord. If +it is unable to do this for a particular note, then the note is +assigned to a different split. At the end, nsplits were created. +The notes in each split are printed in a separate voice by the +function printtrack_split(). The function printtrack_split(splitnumber) +was derived from printtrack(); however, it only processes the +notes belonging to a particular splitnumber. + +It was necessary to determine and store the offset of the first +note in each split using the function set_first_gaps() prior +to calling printtrack_split(). + diff -Nru abcmidi-20200706/doc/readme.txt abcmidi-20200728/doc/readme.txt --- abcmidi-20200706/doc/readme.txt 2020-07-05 13:00:57.000000000 +0000 +++ abcmidi-20200728/doc/readme.txt 2020-07-28 13:55:23.000000000 +0000 @@ -1,9 +1,9 @@ abcMIDI : abc <-> MIDI conversion utilities midi2abc version 3.46 June 22 2020 -abc2midi version 4.38 July 05 2020 +abc2midi version 4.40 July 28 2020 abc2abc version 2.08 June 04 2020 -yaps version 1.77 June 04 2020 +yaps version 1.78 June 14 2020 abcmatch version 1.73 June 04 2020 midicopy version 1.36 June 04 2019 diff -Nru abcmidi-20200706/doc/yapshelp.txt abcmidi-20200728/doc/yapshelp.txt --- abcmidi-20200706/doc/yapshelp.txt 2008-05-30 23:22:22.000000000 +0000 +++ abcmidi-20200728/doc/yapshelp.txt 2020-07-27 15:52:43.000000000 +0000 @@ -1,211 +1,211 @@ - -YAPS - an abc to PostScript Converter - -YAPS is Yet Another abc to PostScript converter. - -The original converter was Michael Methfessel's abc2ps program. Another -converter is abcm2ps, based on the abc2ps code. YAPS uses the library -of PostScript routines from abc2ps, but the parser and other C code does -not come from abc2ps. - -What is PostScript ? --------------------- -This program is intended for converting an abc tune to high quality -printed output. If yaps created an image file in, say, GIF -or PNG format it would have to decide how many pixels wide and how -many pixels high the final image would be. When the image was printed -on a printer capable of much higher resolution, you would be able to -see that the image was built up of lower resolution pixels. PostScript -gets round this limitation by storing the image as a description of -all the objects that will appear on the page. These descriptions are -converted to pixels at the last moment, when the resolution of the -printer is known. This way, you can make full use of the resolution -available. - -If you are lucky, you may have a PostScript printer, in which case -you can send the output of yaps directly to the printer. If not, -there are PostScript interpreter programs available, which will -turn a PostScript image into other image formats. A popular freely -available PostScript interpreter is called GhostScript. Another -freely available program, GSView, provides a graphical interface -to GhostScript and lets you view images on-screen. - -For a full description of the PostScript language, see "PostScript -Language Reference (third edition)" by Adobe Systems Inc. - ----------------------------------------------------------------- -Executing yaps with no filename will give the yaps version and the -following message: - -Usage: yaps [] - possible options are - - -d : debug - display data structure - -e : draw tunes with reference numbers in list - list is comma-separated and may contain ranges - but no spaces e.g. 1,3,7-20 - -E : generate Encapsulated PostScript - -l : landscape mode - -M XXXxYYY : set margin sizes in points - 28.3 points = 1cm, 72 points = 1 inch - -N : add page numbering - -k [NN] : number every bar or every NN th bar - -o : specify output file - -P ss : paper size; 0 is A4, 1 is US Letter - or XXXxYYY to set size in points - -s XX : scaling factor (default is 0.7) - -V : separate voices in multi-voice tune - -x : print tune number in X: field -Takes an abc music file and converts it to PostScript. -If no output filename is given, then by default it is -the input filename but with extension .ps . - -YAPS features -------------- - -1. Uses the abc2midi parsing code, so hopefully compatibility with -abc2midi will be good. - -2. Measures the width of lyric text for lyric typesetting. - -3. Uses dynamically extensible data structures in most places, so -you should not be restricted by compiled-in limits. - -4. Multiple voices drawn with notes played at the same time aligned. - -5. Supports special characters using ISO latin 1 font. Special -characters are created with a TeX-like code e.g. \'E or a 3 digit octal -code e.g. \315 . - -6. Supports the following clefs : baritone, tenor, alto, mezzo, soprano, -treble, bass. Recommended use is - - I:clef=bass - -To make it easier to enter tunes in clefs othan than treble clef, -yaps supports I:octave=-1 to indicate than a C in the tune represents -the note one octave below the pitch defined in the abc standard. These -may be combined in one I: statement e.g. - -I:clef=bass octave=-2 - -You can also use clefs that are one, two or three octaves higher or -lower than normal using e.g. treble-8, treble+15, treble-22. The clef is -drawn with a small 8, 15 or 22 above or below the clef symbol. The clef= -and octave= commands may also go in the K: field e.g. - -K:G clef=bass-8 octave=-3 - -Note that there is an incompatibility between the behaviour of yaps and -the behaviour of abc2ps 1.3.3. abc2ps 1.3.3 does not support the -I:octave=N command, but selecting certain clefs causes it to automatically -transpose by several octaves. You can produce abc that works for both by -following the clef change with an I:octave=N command to do the transpose -that abc2ps does automatically. - -7. Produces boxed part labels. - -8. Supports the segno symbol with !segno! and coda with !coda! . Other -musical instructions such as !fine! and !D.C.! come out as text. - -9. Supports the U: field for abbreviating symbols to single characters. -e.g. - -U:S = !segno! - -allows S to be used to produce the segno symbol. Currently this only -allows new symbols to be defined and does not allow the existing -pre-defined symbols M,L,R,H and T to be altered. - -10. Supports the following abc2ps extensions to abc : - - %%newpage - start a new page, - %%vskip N - adds vertical space of N points. If N is followed by - 'cm' or 'in' the units are taken as centimetres or inches - instead of points e.g. 4cm. - %%text - print text - %%centre (or %%center for Americans) - print centred text. - - If %%text or %%centre appear in the header, the text appears above the - tune. - - %%staffsep SIZE - set vertical blank space between 2 consecutive music - staves. - %%titleleft N - select title placed to the left or centred. N = 1 places - the title on the left while N = 0 centres it. - %%titlecaps - title is displayed in upper case (capital) letters. - %%textfont NAME SIZE - select font called NAME and point size SIZE for - text produced by %%text or %%centre. If only NAME is given, the font - stays the same size. Likewise, if '-' is given as the NAME, only the - font size changes. - - %%titlefont NAME SIZE - select font for title. - %%subtitlefont NAME SIZE - select font for titles after the first title. - %%composerfont NAME SIZE - select font for words in C: and O: fields and - part specifier (P: in header). - %%wordsfont NAME SIZE - select font for words in W: fields. - %%partsfont NAME SIZE - select font for boxed parts and !instruction! . - %%vocalfont NAME SIZE - select font for words in w: fields. - %%gchordfont NAME SIZE - select font for guitar chords in the music. - (It is advisable not to change the font name for the last two, since - the program calculates the width of strings using default fonts) - - %%titlespace, %%subtitlespace, %%textspace, %%composerspace, %%wordsspace, - %%partsspace, %%vocalspace and %%gchordspace determine the amount of - space left above the relevant type of text. Each of these should be - followed by a size in points or value in centrimetres or inches. - - e.g. %%composerfont 3 - %%titlefont 2cm - -11. Supports placing of accompaniment chords either above or below the -stave. - - %%chordsabove - places accompaniment chords above the stave (default). - %%chordsbelow - places accompaniment chords below the stave. - -12. Supports optional text enclosed in quotes before and after the -tempo specification in the Q: field. This extension comes from abc2ps. - -13. Allows highlighting notes by switching output from black to -red and vice-versa using the !red! and !black! instructions. - -Known bugs and Limitations --------------------------- -1. Overfull lines of music are not split over multiple lines. - -WARNING - the -s option is now used for scaling. Selecting separate -printing of each voice (which previously used -s) is now done with -V. - -YAPS is still under development and missing a number of features. However, -it should produce acceptable output most of the time. You may want to try -abc2ps or abcm2ps if you find that YAPS will not do what you want. - -Viewing PostScript Files from DOS ---------------------------------- -Here is how I arranged things so that I can view PostScript from DOS : - -1. Install Ghostscript 5.10 (compiled to run under DOS). This is not -totally straightforward; you have to install the fonts separately and -then set the environment variable GS_LIB to a path containing the -GhostScript initialization files and the fonts. -2. Install Pictview, a free image viewer. -3. Create a small batch file ps.bat containing the following line: - -c:\gs5.10\gs386 -r120 -sOutputFile=out.tif -sDEVICE=tiffg3 -sPAPERSIZE=a4 -dNOPAUSE -dBATCH %1 - -When you run this, it creates a TIFF file out.tif containing all -pages in compressed format. - -The following commands will show you the dots for an abc file: - -yaps demo.abc -ps demo.ps -pictview out.tif - - -Copyright (c) James Allwright 1999 -(parts of the code Copyright Michael Methfessel). -This code may be freely re-distributed under the terms of the -GNU public license. You are encouraged to modify and redistribute it, -provided this copyright notice is retained. - + +YAPS - an abc to PostScript Converter + +YAPS is Yet Another abc to PostScript converter. + +The original converter was Michael Methfessel's abc2ps program. Another +converter is abcm2ps, based on the abc2ps code. YAPS uses the library +of PostScript routines from abc2ps, but the parser and other C code does +not come from abc2ps. + +What is PostScript ? +-------------------- +This program is intended for converting an abc tune to high quality +printed output. If yaps created an image file in, say, GIF +or PNG format it would have to decide how many pixels wide and how +many pixels high the final image would be. When the image was printed +on a printer capable of much higher resolution, you would be able to +see that the image was built up of lower resolution pixels. PostScript +gets round this limitation by storing the image as a description of +all the objects that will appear on the page. These descriptions are +converted to pixels at the last moment, when the resolution of the +printer is known. This way, you can make full use of the resolution +available. + +If you are lucky, you may have a PostScript printer, in which case +you can send the output of yaps directly to the printer. If not, +there are PostScript interpreter programs available, which will +turn a PostScript image into other image formats. A popular freely +available PostScript interpreter is called GhostScript. Another +freely available program, GSView, provides a graphical interface +to GhostScript and lets you view images on-screen. + +For a full description of the PostScript language, see "PostScript +Language Reference (third edition)" by Adobe Systems Inc. + +---------------------------------------------------------------- +Executing yaps with no filename will give the yaps version and the +following message: + +Usage: yaps [] + possible options are - + -d : debug - display data structure + -e : draw tunes with reference numbers in list + list is comma-separated and may contain ranges + but no spaces e.g. 1,3,7-20 + -E : generate Encapsulated PostScript + -l : landscape mode + -M XXXxYYY : set margin sizes in points + 28.3 points = 1cm, 72 points = 1 inch + -N : add page numbering + -k [NN] : number every bar or every NN th bar + -o : specify output file + -P ss : paper size; 0 is A4, 1 is US Letter + or XXXxYYY to set size in points + -s XX : scaling factor (default is 0.7) + -V : separate voices in multi-voice tune + -x : print tune number in X: field +Takes an abc music file and converts it to PostScript. +If no output filename is given, then by default it is +the input filename but with extension .ps . + +YAPS features +------------- + +1. Uses the abc2midi parsing code, so hopefully compatibility with +abc2midi will be good. + +2. Measures the width of lyric text for lyric typesetting. + +3. Uses dynamically extensible data structures in most places, so +you should not be restricted by compiled-in limits. + +4. Multiple voices drawn with notes played at the same time aligned. + +5. Supports special characters using ISO latin 1 font. Special +characters are created with a TeX-like code e.g. \'E or a 3 digit octal +code e.g. \315 . + +6. Supports the following clefs : baritone, tenor, alto, mezzo, soprano, +treble, bass. Recommended use is + + I:clef=bass + +To make it easier to enter tunes in clefs othan than treble clef, +yaps supports I:octave=-1 to indicate than a C in the tune represents +the note one octave below the pitch defined in the abc standard. These +may be combined in one I: statement e.g. + +I:clef=bass octave=-2 + +You can also use clefs that are one, two or three octaves higher or +lower than normal using e.g. treble-8, treble+15, treble-22. The clef is +drawn with a small 8, 15 or 22 above or below the clef symbol. The clef= +and octave= commands may also go in the K: field e.g. + +K:G clef=bass-8 octave=-3 + +Note that there is an incompatibility between the behaviour of yaps and +the behaviour of abc2ps 1.3.3. abc2ps 1.3.3 does not support the +I:octave=N command, but selecting certain clefs causes it to automatically +transpose by several octaves. You can produce abc that works for both by +following the clef change with an I:octave=N command to do the transpose +that abc2ps does automatically. + +7. Produces boxed part labels. + +8. Supports the segno symbol with !segno! and coda with !coda! . Other +musical instructions such as !fine! and !D.C.! come out as text. + +9. Supports the U: field for abbreviating symbols to single characters. +e.g. + +U:S = !segno! + +allows S to be used to produce the segno symbol. Currently this only +allows new symbols to be defined and does not allow the existing +pre-defined symbols M,L,R,H and T to be altered. + +10. Supports the following abc2ps extensions to abc : + + %%newpage - start a new page, + %%vskip N - adds vertical space of N points. If N is followed by + 'cm' or 'in' the units are taken as centimetres or inches + instead of points e.g. 4cm. + %%text - print text + %%centre (or %%center for Americans) - print centred text. + + If %%text or %%centre appear in the header, the text appears above the + tune. + + %%staffsep SIZE - set vertical blank space between 2 consecutive music + staves. + %%titleleft N - select title placed to the left or centred. N = 1 places + the title on the left while N = 0 centres it. + %%titlecaps - title is displayed in upper case (capital) letters. + %%textfont NAME SIZE - select font called NAME and point size SIZE for + text produced by %%text or %%centre. If only NAME is given, the font + stays the same size. Likewise, if '-' is given as the NAME, only the + font size changes. + + %%titlefont NAME SIZE - select font for title. + %%subtitlefont NAME SIZE - select font for titles after the first title. + %%composerfont NAME SIZE - select font for words in C: and O: fields and + part specifier (P: in header). + %%wordsfont NAME SIZE - select font for words in W: fields. + %%partsfont NAME SIZE - select font for boxed parts and !instruction! . + %%vocalfont NAME SIZE - select font for words in w: fields. + %%gchordfont NAME SIZE - select font for guitar chords in the music. + (It is advisable not to change the font name for the last two, since + the program calculates the width of strings using default fonts) + + %%titlespace, %%subtitlespace, %%textspace, %%composerspace, %%wordsspace, + %%partsspace, %%vocalspace and %%gchordspace determine the amount of + space left above the relevant type of text. Each of these should be + followed by a size in points or value in centrimetres or inches. + + e.g. %%composerfont 3 + %%titlefont 2cm + +11. Supports placing of accompaniment chords either above or below the +stave. + + %%chordsabove - places accompaniment chords above the stave (default). + %%chordsbelow - places accompaniment chords below the stave. + +12. Supports optional text enclosed in quotes before and after the +tempo specification in the Q: field. This extension comes from abc2ps. + +13. Allows highlighting notes by switching output from black to +red and vice-versa using the !red! and !black! instructions. + +Known bugs and Limitations +-------------------------- +1. Overfull lines of music are not split over multiple lines. + +WARNING - the -s option is now used for scaling. Selecting separate +printing of each voice (which previously used -s) is now done with -V. + +YAPS is still under development and missing a number of features. However, +it should produce acceptable output most of the time. You may want to try +abc2ps or abcm2ps if you find that YAPS will not do what you want. + +Viewing PostScript Files from DOS +--------------------------------- +Here is how I arranged things so that I can view PostScript from DOS : + +1. Install Ghostscript 5.10 (compiled to run under DOS). This is not +totally straightforward; you have to install the fonts separately and +then set the environment variable GS_LIB to a path containing the +GhostScript initialization files and the fonts. +2. Install Pictview, a free image viewer. +3. Create a small batch file ps.bat containing the following line: + +c:\gs5.10\gs386 -r120 -sOutputFile=out.tif -sDEVICE=tiffg3 -sPAPERSIZE=a4 -dNOPAUSE -dBATCH %1 + +When you run this, it creates a TIFF file out.tif containing all +pages in compressed format. + +The following commands will show you the dots for an abc file: + +yaps demo.abc +ps demo.ps +pictview out.tif + + +Copyright (c) James Allwright 1999 +(parts of the code Copyright Michael Methfessel). +This code may be freely re-distributed under the terms of the +GNU public license. You are encouraged to modify and redistribute it, +provided this copyright notice is retained. + diff -Nru abcmidi-20200706/drawtune.c abcmidi-20200728/drawtune.c --- abcmidi-20200706/drawtune.c 2020-06-06 23:06:13.000000000 +0000 +++ abcmidi-20200728/drawtune.c 2020-07-27 15:52:44.000000000 +0000 @@ -1,3619 +1,3619 @@ -/* - * 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 - * - */ - -/* drawtune.c */ -/* This file contains routines for generating final PostScript Output */ -/* There are 2 stages to this process. First the symbol positions are */ -/* calculated, then the PostScript symbols are generated. */ - -#ifdef _MSC_VER -#define ANSILIBS 1 -#endif - -#include -#ifdef ANSILIBS -#include -#include -#include -#endif - -#include "abc.h" -#include "structs.h" -#include "sizes.h" -#include "drawtune.h" - -/* external functions and variables */ -extern struct tune thetune; -extern int debugging; -extern int pagenumbering; -extern int barnums, nnbars; -extern char outputname[256]; -extern char outputroot[256]; -extern int make_open(); -extern void printlib(); -extern int count_dots(int *base, int *base_exp, int n, int m); - -extern void monospace(struct tune* t); -extern void spacevoices(struct tune* t); - -/* provided by debug.c */ -extern void showtune(struct tune *t); - -struct key* newkey(char* name, int sharps, char accidental[], int mult[]); -struct aclef* newclef(enum cleftype t, int octave); -char* addstring(char *s); -extern int lineno; -extern int separate_voices; -extern int print_xref; -extern int landscape; - -static void pagebottom(); -static void setfont(int size, int num); - -FILE* f = NULL; -struct feature* beamset[64]; -struct feature* gracebeamset[32]; -int beamctr, gracebeamctr;; -int rootstem; -int fontsize, fontnum; -int donemeter; -int inchord; -int chordcount; -static int ingrace; /* [SDG] 2020-06-03 */ -struct feature* chordhead; - -double scale, descend, totlen, oldplace; -int pagecount = 1; -int pagelen, pagewidth, xmargin, ymargin; -double scaledlen, scaledwidth; -int staffsep; -int eps_out; -int titleleft = 0; -int titlecaps = 0; -int gchords_above = 1; -int redcolor; /* [SS] 2013-11-04*/ - -enum placetype {left, right, centre}; -struct font textfont; -struct font titlefont; -struct font subtitlefont; -struct font wordsfont; -struct font composerfont; -struct font vocalfont; -struct font gchordfont; -struct font partsfont; - -/* arrays giving character widths for characters 32-255 */ - -/* font for lyrics : 13 point Times-Bold */ -double timesbold_width[224] = { -3.249, 4.328, 7.214, 6.499, 6.499, 12.999, 10.828, 4.328, -4.328, 4.328, 6.499, 7.409, 3.249, 7.409, 3.249, 3.613, -6.499, 6.499, 6.499, 6.499, 6.499, 6.499, 6.499, 6.499, -6.499, 6.499, 4.328, 4.328, 7.409, 7.409, 7.409, 6.499, -12.089, 9.385, 8.67, 9.385, 9.385, 8.67, 7.942, 10.113, -10.113, 5.056, 6.499, 10.113, 8.67, 12.271, 9.385, 10.113, -7.942, 10.113, 9.385, 7.227, 8.67, 9.385, 9.385, 12.999, -9.385, 9.385, 8.67, 4.328, 3.613, 4.328, 7.552, 6.499, -4.328, 6.499, 7.227, 5.771, 7.227, 5.771, 4.328, 6.499, -7.227, 3.613, 4.328, 7.227, 3.613, 10.828, 7.227, 6.499, -7.227, 7.227, 5.771, 5.056, 4.328, 7.227, 6.499, 9.385, -6.499, 6.499, 5.771, 5.121, 2.859, 5.121, 6.759, 3.249, -3.249, 3.249, 3.249, 3.249, 3.249, 3.249, 3.249, 3.249, -3.249, 3.249, 3.249, 3.249, 3.249, 3.249, 3.249, 3.249, -3.613, 4.328, 4.328, 4.328, 4.328, 4.328, 4.328, 4.328, -4.328, 3.249, 4.328, 4.328, 3.249, 4.328, 4.328, 4.328, -3.249, 4.328, 6.499, 6.499, 6.499, 6.499, 2.859, 6.499, -4.328, 9.71, 3.899, 6.499, 7.409, 4.328, 9.71, 4.328, -5.199, 7.409, 3.899, 3.899, 4.328, 7.227, 7.019, 3.249, -4.328, 3.899, 4.289, 6.499, 9.749, 9.749, 9.749, 6.499, -9.385, 9.385, 9.385, 9.385, 9.385, 9.385, 12.999, 9.385, -8.67, 8.67, 8.67, 8.67, 5.056, 5.056, 5.056, 5.056, -9.385, 9.385, 10.113, 10.113, 10.113, 10.113, 10.113, 7.409, -10.113, 9.385, 9.385, 9.385, 9.385, 9.385, 7.942, 7.227, -6.499, 6.499, 6.499, 6.499, 6.499, 6.499, 9.385, 5.771, -5.771, 5.771, 5.771, 5.771, 3.613, 3.613, 3.613, 3.613, -6.499, 7.227, 6.499, 6.499, 6.499, 6.499, 6.499, 7.409, -6.499, 7.227, 7.227, 7.227, 7.227, 6.499, 7.227, 6.499, -}; - -/* font for chord names : 12 point Helvetica */ -double helvetica_width[224] = { -3.335, 3.335, 4.259, 6.671, 6.671, 10.667, 8.003, 2.651, -3.995, 3.995, 4.667, 7.007, 3.335, 3.995, 3.335, 3.335, -6.671, 6.671, 6.671, 6.671, 6.671, 6.671, 6.671, 6.671, -6.671, 6.671, 3.335, 3.335, 7.007, 7.007, 7.007, 6.671, -12.179, 8.003, 8.003, 8.663, 8.663, 8.003, 7.331, 9.335, -8.663, 3.335, 5.999, 8.003, 6.671, 9.995, 8.663, 9.335, -8.003, 9.335, 8.663, 8.003, 7.331, 8.663, 8.003, 11.327, -8.003, 8.003, 7.331, 3.335, 3.335, 3.335, 5.627, 6.671, -2.663, 6.671, 6.671, 5.999, 6.671, 6.671, 3.335, 6.671, -6.671, 2.663, 2.663, 5.999, 2.663, 9.995, 6.671, 6.671, -6.671, 6.671, 3.995, 5.999, 3.335, 6.671, 5.999, 8.663, -5.999, 5.999, 5.999, 4.007, 3.119, 4.007, 7.007, 3.335, -3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, -3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, -3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, -3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, -3.335, 3.995, 6.671, 6.671, 2.003, 6.671, 6.671, 6.671, -6.671, 2.291, 3.995, 6.671, 3.995, 3.995, 5.999, 5.999, -3.335, 6.671, 6.671, 6.671, 3.335, 3.335, 6.443, 4.199, -2.663, 3.995, 3.995, 6.671, 11.999, 11.999, 3.335, 7.331, -3.335, 3.995, 3.995, 3.995, 3.995, 3.995, 3.995, 3.995, -3.995, 3.335, 3.995, 3.995, 3.335, 3.995, 3.995, 3.995, -11.999, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, -3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, -3.335, 11.999, 3.335, 4.439, 3.335, 3.335, 3.335, 3.335, -6.671, 9.335, 11.999, 4.379, 3.335, 3.335, 3.335, 3.335, -3.335, 10.667, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, -2.663, 7.331, 11.327, 7.331, 3.335, 3.335, 3.335, 3.335, -}; - -static int ISOencoding(char ch1, char ch2) -/* converts the 2 characters following '\' into an ISO Latin 1 Font */ -/* character code in the range 128-255 */ -{ - int code; - - code = 0; - switch (ch1) { - case '`': - switch(ch2) { - case 'A': code = 0300; break; - case 'E': code = 0310; break; - case 'I': code = 0314; break; - case 'O': code = 0322; break; - case 'U': code = 0331; break; - case 'a': code = 0340; break; - case 'e': code = 0350; break; - case 'i': code = 0354; break; - case 'o': code = 0362; break; - case 'u': code = 0371; break; - default: - break; - }; - break; - case '\'': - switch(ch2) { - case 'A': code = 0301; break; - case 'E': code = 0311; break; - case 'I': code = 0315; break; - case 'U': code = 0332; break; - case 'Y': code = 0335; break; - case 'a': code = 0341; break; - case 'e': code = 0351; break; - case 'i': code = 0355; break; - case 'o': code = 0363; break; - case 'u': code = 0372; break; - case 'y': code = 0375; break; - default: - break; - }; - break; - case '^': - switch(ch2) { - case 'A': code = 0302; break; - case 'E': code = 0312; break; - case 'I': code = 0316; break; - case 'O': code = 0324; break; - case 'U': code = 0333; break; - case 'a': code = 0342; break; - case 'e': code = 0352; break; - case 'i': code = 0356; break; - case 'o': code = 0364; break; - case 'u': code = 0373; break; - default: - break; - }; - break; - case '"': - switch(ch2) { - case 'A': code = 0304; break; - case 'E': code = 0313; break; - case 'I': code = 0317; break; - case 'O': code = 0326; break; - case 'U': code = 0334; break; - case 'a': code = 0344; break; - case 'e': code = 0353; break; - case 'i': code = 0357; break; - case 'o': code = 0366; break; - case 'u': code = 0374; break; - case 'y': code = 0377; break; - default: - break; - }; - break; - case '~': - switch(ch2) { - case 'A': code = 0303; break; - case 'N': code = 0321; break; - case 'O': code = 0325; break; - case 'a': code = 0343; break; - case 'n': code = 0361; break; - case 'o': code = 0365; break; - default: - break; - }; - break; - case 'c': - switch(ch2) { - case 'C': code = 0307; break; - case 'c': code = 0347; break; - default: - break; - }; - break; - case 'o': - switch(ch2) { - case 'A': code = 0305; break; - case 'a': code = 0345; break; - default: - break; - }; - break; - case 'A': - if (ch2=='A') { - code = 0305; - }; - if (ch2=='E') { - code = 0305; - }; - break; - case 'a': - if (ch2=='a') { - code = 0345; - }; - if (ch2=='e') { - code = 0346; - }; - break; - case 's': - if (ch2=='s') { - code = 0337; - }; - break; - default: - break; - }; - return(code); -} - -static int getISO(char s[], int j, int* code) -/* convert input into 8-bit character code */ -/* s[] is input string, j is place in string */ -/* returns new place in string and writes 8-bit value to code */ -{ - int i; - int count; - - i =j; - if (s[i]=='\\') { - /* look for 2 character code */ - *code = ISOencoding(s[i+1], s[i+2]); - if (*code != 0) { - i = i+3; - } else { - /* look for octal code */ - i = i+1; - *code = 0; - count = 0; - while ((s[i]>='0')&&(s[i]<='7')&&(count<3)) { - *code = ((*code)*8+(int)s[i]-'0')%256; - count = count+1; - i = i+1; - }; - if (*code == 0) { - *code = '\\'; - }; - }; - } else { - *code = (int)s[i] & 0xFF; - i = i + 1; - }; - return(i); -} - -static void ISOdecode(char s[], char out[]) -/* convert coded characters to straight 8-bit ascii */ -{ - int i, j, len; - int code; - - len = strlen(s); - i = 0; - j = 0; - while (i 31) && (ch < 128)) { - fprintf(f, "%c", s[i]); - } else { - fprintf(f, "\\%03o", 0xFF & (int)s[i]); - }; - i = i+1; - break; - }; - }; -} - -static double stringwidth(char* str, double ptsize, int fontno) -/* calculate width of string */ -{ - int i, len, ch; - double width; - int code; - double baseptsize; - double* charwidth; - - if (fontno == 3) { - charwidth = &helvetica_width[0]; - baseptsize = 12.0; - } else { - charwidth = ×bold_width[0]; - baseptsize = 13.0; - }; - width = 0.0; - len = strlen(str); - i = 0; - while (i < len) { - i = getISO(str, i, &code); - ch = code - 32; - if ((ch >= 0) && (ch < 224)) { - width = width + charwidth[ch]; - } else { - width = width + charwidth[0]; /* approximate with space */ - }; - }; - return(width*ptsize/baseptsize); -} - -void setscaling(char* s) -/* scaling value from string following -s in arglist */ -{ - double f; - - f = -1.0; - sscanf(s, "%lf", &f); - if (f < 0.001) { - f = TUNE_SCALING; - }; - scale = f; -} - -void setmargins(s) -char* s; -/* set margin values from string following -M in arglist */ -{ - int i, j; - - xmargin = XMARGIN; - ymargin = YMARGIN; - if (s != NULL) { - i = -1; - j = -1; - sscanf(s, "%dx%d", &i, &j); - if ((i != -1) && (j != -1)) { - xmargin = i; - ymargin = j; - }; - }; -} - -void setpagesize(s) -char* s; -/* set page size from string following -P in arglist */ -{ - int i, j; - - pagelen = A4_PAGELEN - 2 * ymargin; - pagewidth = A4_PAGEWIDTH - 2 * xmargin; - if (s != NULL) { - i = 0; - j = 0; - sscanf(s, "%dx%d", &i, &j); - if (i == 0) { - pagelen = A4_PAGELEN - 2 * ymargin; - pagewidth = A4_PAGEWIDTH - 2 * xmargin; - }; - if (i == 1) { - pagelen = US_LETTER_PAGELEN - 2 * ymargin; - pagewidth = US_LETTER_PAGEWIDTH - 2 * xmargin; - }; - if ((i > 100) && (i > 2*xmargin) && (j > 100) && (j > 2*ymargin)) { - pagewidth = i - 2 * xmargin; - pagelen = j - 2 * ymargin; - }; - }; - if (landscape) { - i = xmargin; - xmargin = ymargin; - ymargin = i; - i = pagelen; - pagelen = pagewidth; - pagewidth = i; - }; - scaledlen = ((double)(pagelen))/scale; - scaledwidth = ((double)(pagewidth))/scale; -} - -static void set_space(afont, s) -/* set vertical space to appear above a line in the given font */ -/* s is a string specifying space in points (default), centimetres */ -/* or inches */ -struct font* afont; -char* s; -{ - int count; - double x; - char units[40]; - - count = sscanf(s, "%lf%s", &x, units); - if (count > 0) { - if ((count >= 2) && (strncmp(units, "cm", 2) == 0)) { - x = x*28.3; - }; - if ((count >= 2) && (strncmp(units, "in", 2) == 0)) { - x = x*72.0 ; - }; - afont->space = (int)x; - }; -} - -static void init_font(afont, ptsize, spce, defnum, specialnum) -/* initialize font structure with given values */ -struct font* afont; -int ptsize, spce, defnum, specialnum; -{ - afont->pointsize = ptsize; - afont->space = spce; - afont->default_num = defnum; - afont->special_num = specialnum; - afont->name = NULL; - afont->defined = 0; -} - -static void startpage() -/* Encapsulated PostScript for a page header */ -{ - fprintf(f, "%%%%Page: %d %d\n", pagecount, pagecount); - fprintf(f, "%%%%BeginPageSetup\n"); - fprintf(f, "gsave\n"); - if (landscape) { - fprintf(f, "90 rotate %d %d T\n", xmargin, -ymargin); - } else { - fprintf(f, "%d %d T\n", xmargin, ymargin+pagelen); - }; - fprintf(f, "0.8 setlinewidth 0 setlinecap\n"); - fprintf(f, "%.3f %.3f scale\n", scale, scale); - fprintf(f, "%%%%EndPageSetup\n\n"); - fontnum = 0; - fontsize = 0; - totlen = 0.0; - oldplace = 0.0; - descend = 0.0; -} - -static void closepage() -/* Encapsulated PostScript for page end */ -{ - - if (pagenumbering) { - setfont(12, 3); - pagebottom(); - fprintf(f, "(%d) %.1f 0 M cshow\n", pagecount, scaledwidth/2.0); - }; - fprintf(f, "%%%%PageTrailer\n"); - fprintf(f, "grestore\n"); - fprintf(f, "showpage\n\n"); - pagecount = pagecount + 1; -} - -void newpage() -/* create PostScript for a new page of paper */ -/* everything will now be printed on this fresh sheet */ -/* ignore the command if the sheet is completely blank */ -{ - if (totlen > 0.0) { - closepage(); - startpage(); - }; -} - -static void pagebottom() -/* move to the bottom of the page */ -{ - fprintf(f, "0 %.1f T\n", -(scaledlen - totlen)); - totlen = scaledlen; - descend = 0.0; -} - -static void newblock(double height, double descender) -/* The page is modelled as a series of vertical bands */ -/* This routine finds room for a fresh band below the current one */ -/* It also does a translation to set a new y=0 line */ -/* Subsequent calls may draw up to height units above or */ -/* descender units below the line y=0 */ -{ - if (totlen+(descend+height+descender) > scaledlen - (double)(pagenumbering*12)) { - newpage(); - }; - fprintf(f, "0 %.1f T\n", - descend - height); - totlen = totlen + (descend + height); - descend = descender; -} - -static void staveline() -/* draw 5 lines of a stave */ -{ - fprintf(f, "%.1f staff\n", scaledwidth); -} - -static void printclef(struct aclef* t, double x, double yup, double ydown) -/* draw a clef of the specified type */ -{ - switch (t->type) { - case treble: - fprintf(f, "%.1f tclef\n", x); - break; - case bass: - fprintf(f, "%.1f bclef\n", x); - break; - case alto: - fprintf(f, "%.1f cclef\n", x); - break; - case baritone: - fprintf(f, "0 %d T %.1f cclef 0 %d T\n", 4*TONE_HT, x, -4*TONE_HT); - break; - case tenor: - fprintf(f, "0 %d T %.1f cclef 0 %d T\n", 2*TONE_HT, x, -2*TONE_HT); - break; - case mezzo: - fprintf(f, "0 %d T %.1f cclef 0 %d T\n", -2*TONE_HT, x, 2*TONE_HT); - break; - case soprano: - fprintf(f, "0 %d T %.1f cclef 0 %d T\n", -4*TONE_HT, x, 4*TONE_HT); - break; - default: - break; - }; - if (t->octave > 0) { - fprintf(f, "%.1f %.1f (%d) bnum\n", x, yup - CLEFNUM_HT + 3, t->octave); - }; - if (t->octave < 0) { - fprintf(f, "%.1f %.1f (%d) bnum\n", x, -ydown, - t->octave); - }; -} - -void set_keysig(struct key* k, struct key* newval) -/* copy across key signature */ -{ - int i; - - for (i=0; i<7; i++) { - k->map[i] = newval->map[i]; - k->mult[i] = newval->mult[i]; - }; - k->sharps = newval->sharps; -} - -static double size_keysig(char oldmap[], char newmap[]) -/* compute width taken up by drawing the key signature */ -{ - int i, n; - - n = 0; - for (i=0; i<7; i++) { - if (((newmap[i] == '=')&&(oldmap[i] != '=')) || - (newmap[i] == '^') || (newmap[i] == '_')) { - n = n + 1; - }; - }; - return((double)n * 5.0); -} - -static double size_timesig(struct fract* meter) -/* compute width of the time signature */ -{ - double len1, len2; - char temp[20]; - - sprintf(temp, "%d", meter->num); - len1 = stringwidth(temp, 16.0, 2); - sprintf(temp, "%d", meter->denom); - len2 = stringwidth(temp, 16.0, 2); - if (len1 > len2) { - return(len1); - } else { - return(len2); - }; -} - -static void draw_keysig(char oldmap[], char newmap[], int newmult[], - double x, struct aclef* clef) -/* draw key specified key signature at position x on line */ -/* arrays oldmap[], newmap[], newmult[] are indexed with a=0 to g=7 */ -/* sharp_pos[] and flat_pos[] give order of notes */ -/* e.g. sharp_pos[0] = 8 means 1st sharp drawn is conventionally f */ -/* which is in position 8 on the stave (bottom line is E = 0) */ -{ - double xpos; - static int sharp_pos[7] = { 8, 5, 9, 6, 3, 7, 4 }; - static int flat_pos[7] = { 4, 7, 3, 6, 2, 5, 1}; - int i; - int offset, pos; - int note; - - switch (clef->type) { - case treble: - offset = 0; - break; - case soprano: - offset = 2; - break; - case mezzo: - offset = 4; - break; - case alto: - offset = 6; - break; - case tenor: - offset = 8; - break; - case baritone: - offset = 10; - break; - case bass: - offset = 12; - break; - case noclef: - break; - }; - xpos = x; - /* draw naturals to cancel out old accidentals */ - for (i=0; i<7; i++) { - note = (sharp_pos[i] + 4) % 7; - if ((newmap[note] == '=')&&(oldmap[note] != '=')) { - pos = (sharp_pos[i] + offset - 3) % 7 + 3; - fprintf(f, " %.1f %d nt0", xpos, pos*TONE_HT); - xpos = xpos + 5; - }; - }; - /* draw sharps */ - for (i=0; i<7; i++) { - note = (sharp_pos[i] + 4) % 7; - if (newmap[note] == '^') { - pos = (sharp_pos[i] + offset - 3) % 7 + 3; - if (newmult[note] == 2) { - fprintf(f, " %.1f %d dsh0", xpos, pos*TONE_HT); - } else { - fprintf(f, " %.1f %d sh0", xpos, pos*TONE_HT); - }; - xpos = xpos + 5; - }; - }; - /* draw flats */ - for (i=0; i<7; i++) { - note = (flat_pos[i] + 4) % 7; - if (newmap[note] == '_') { - pos = (flat_pos[i] + offset - 1)%7 + 1; - if (newmult[note] == 2) { - fprintf(f, " %.1f %d dft0", xpos, pos*TONE_HT); - } else { - fprintf(f, " %.1f %d ft0", xpos, pos*TONE_HT); - }; - xpos = xpos + 5; - }; - }; - fprintf(f, "\n"); -} - -static void draw_meter(struct fract* meter, double x) -/* draw meter (time signature) at specified x value */ -{ - fprintf(f, "%.1f (%d) (%d) tsig\n", x, meter->num, meter->denom); -} - -static double maxstrwidth(struct llist* strings, double ptsize, int fontno) -/* return the width of longest string in the list */ -{ - double width, max; - char* str; - - max = 0.0; - str = firstitem(strings); - while (str != NULL) { - width = stringwidth(str, ptsize, fontno); - if (width > max) { - max = width; - }; - str = nextitem(strings); - }; - return(max); -} - -static void sizerest(struct rest* r, struct feature* ft) -/* compute width of rest element */ -{ - double width; - - if (r->multibar > 0) { - ft->xleft = 0.0; - ft->xright = 40.0; - } else { - switch (r->base_exp) { - case 1: - case 0: - ft->xleft = 2.5; - ft->xright = 2.5; - break; - case -1: - ft->xleft = 2.5; - ft->xright = 2.5; - break; - case -2: - ft->xleft = 3.0; - ft->xright = 4.0; - break; - case -3: - ft->xleft = 4.0; - ft->xright = 3.0; - break; - case -4: - ft->xleft = 5.0; - ft->xright = 3.0; - break; - case -5: - ft->xleft = 6.0; - ft->xright = 5.0; - break; - case -6: - default: - ft->xleft = 7.0; - ft->xright = 5.0; - break; - }; - if (r->dots > 0) { - ft->xright = ft->xright + (r->dots* (float) DOT_SPACE); - }; - }; - if (r->gchords != NULL) { - width = maxstrwidth(r->gchords, (double)(gchordfont.pointsize), - gchordfont.default_num); - if (width > ft->xleft + ft->xright) { - ft->xright = (float) width - ft->xleft; - }; - }; -} - -static int acc_upsize(char ch) -/* returns height above note of accent */ -{ - int size; - - switch (ch) { - case '=': - size = NAT_UP; - break; - case '^': - size = SH_UP; - break; - case '_': - size = FLT_UP; - break; - default: - size = 0; - break; - }; - return(size); -} - -static int acc_downsize(char ch) -/* returns depth below note of accent */ -{ - int size; - - switch (ch) { - case '=': - size = NAT_DOWN; - break; - case '^': - size = SH_DOWN; - break; - case '_': - size = FLT_DOWN; - break; - default: - size = 0; - break; - }; - return(size); -} - -static void setstemlen(struct note* n, int ingrace) -/* work out how long stem needs to be for an isolated note */ -/* if the note is in a beamed set, the length may be altered later */ -{ - if (n->base_exp >= 0) { - n->stemlength = 0.0; - } else { - if (ingrace) { - if (n->beaming != single) { - n->stemlength = GRACE_STEMLEN; /* default stem length */ - /* value will be recalculated later by setbeams() */ - } else { - switch(n->base_exp) { - case -6: - n->stemlength = (float) GRACE_STEMLEN + 2* (float) TAIL_SEP* (float) 0.7; - break; - case -5: - n->stemlength = (float) GRACE_STEMLEN + (float) TAIL_SEP* (float) 0.7; - break; - default: - n->stemlength = (float) GRACE_STEMLEN; /* default stem length */ - break; - }; - }; - } else { - if (n->beaming != single) { - n->stemlength = STEMLEN; /* default stem length */ - /* value will be recalculated later by setbeams() */ - } else { - switch(n->base_exp) { - case -6: - n->stemlength = (float) STEMLEN + 2* (float) TAIL_SEP; - break; - case -5: - n->stemlength = (float) STEMLEN + (float) TAIL_SEP; - break; - default: - n->stemlength = (float) STEMLEN; /* default stem length */ - break; - }; - }; - }; - }; -} - -static void sizenote(struct note* n, struct feature* f, int ingrace) -/* compute width and height of note element */ -{ - double width; - char* decorators; - int i; - - f->ydown = (float) TONE_HT*(n->y - 2); - f->yup = (float) TONE_HT*(n->y + 2); - if (ingrace) { - f->xleft = (float) GRACE_HALF_HEAD; - f->xright = (float) GRACE_HALF_HEAD; - } else { - if (n->base_exp >= 1) { - f->xleft = HALF_BREVE; - f->xright = HALF_BREVE; - } else { - f->xleft = HALF_HEAD; - f->xright = HALF_HEAD; - }; - if (n->fliphead) { - if (n->stemup) { - f->xright = (float) f->xright + (float) HALF_HEAD * 2; - } else { - f->xleft = f->xleft + (float) HALF_HEAD * 2; - }; - }; - }; - if (n->dots > 0) { - f->xright = f->xright + (n->dots* (float) DOT_SPACE); - }; - if ((n->stemup) && (n->base_exp <= -3) && (n->beaming==single)) { - if (f->xright < (HALF_HEAD + TAILWIDTH)) { - f->xright = HALF_HEAD + TAILWIDTH; - }; - }; - if (n->accidental != ' ') { - f->xleft = (float) HALF_HEAD + (float) ACC_OFFSET + (n->acc_offset-1) * (float) ACC_OFFSET2; - if (n->stemup) { - f->ydown = (float) TONE_HT*(n->y) - (float)(acc_downsize(n->accidental)); - } else { - f->yup = (float) ((float) TONE_HT*(n->y) + (double)(acc_upsize(n->accidental))); - }; - }; - if (n->stemlength > 0.0) { - if (n->stemup) { - f->yup = TONE_HT*(n->y) + n->stemlength; - } else { - f->ydown = TONE_HT*(n->y) - n->stemlength; - }; - }; - if (n->accents != NULL) { - decorators = n->accents; - for (i=0; i<(int) strlen(decorators); i++) { - switch(decorators[i]) { - case 'H': - case '~': - case 'u': - case 'v': - case 'T': - f->yup = f->yup + BIG_DEC_HT; - break; - case 'R': - if (n->stemup) { - f->ydown = f->ydown + BIG_DEC_HT; - } else { - f->yup = f->yup + BIG_DEC_HT; - }; - break; - case 'M': - case '.': - if (n->stemup) { - f->ydown = f->ydown + SMALL_DEC_HT; - } else { - f->yup = f->yup + SMALL_DEC_HT; - }; - break; - default: - /* this should never happen */ - event_error("Un-recognized accent"); - break; - }; - }; - }; - if (n->syllables != NULL) { - width = maxstrwidth(n->syllables, (double)(vocalfont.pointsize), - vocalfont.default_num); - if (width > f->xleft + f->xright) { - f->xright = (float) width - f->xleft; - }; - }; - if (n->gchords != NULL) { - width = maxstrwidth(n->gchords, (double)(gchordfont.pointsize), - gchordfont.default_num); - if (width > f->xleft + f->xright) { - f->xright = (float) width - f->xleft; - }; - }; -} - -static void spacechord(struct feature* chordplace) -/* prevents collision of note heads and accidentals in a chord */ -/* sets fliphead and acc_offset for all notes in chord */ -{ - struct feature* place; - struct note* anote; - int thisy, lasty, lastflip; - int stemdir =0; /* [SDG] 2020-06-03 */ - int doneflip; - int ygap[10]; - int accplace; - int i; - - /* first flip heads */ - place = chordplace; - lasty = 200; - lastflip = 1; - doneflip = 0; - while ((place != NULL) && (place->type != CHORDOFF)) { - if ((place->type == CHORDNOTE) || (place->type == NOTE)) { - anote = place->item; - thisy = anote->y; - if ((lasty - thisy <= 1) && (lastflip != 1)) { - anote->fliphead = 1; - stemdir = anote->stemup; - lastflip = 1; - doneflip = 1; - } else { - lastflip = 0; - }; - lasty = thisy; - }; - place = place->next; - }; - /* now deal with accidentals */ - for (i=0; i<10; i++) { - ygap[i] = 200; - }; - if ((doneflip) && (stemdir == 0)) { - /* block off space where note is */ - ygap[0] = -200; - }; - place = chordplace; - while ((place != NULL) && (place->type != CHORDOFF)) { - if ((place->type == CHORDNOTE) || (place->type == NOTE)) { - anote = place->item; - thisy = anote->y; - if (anote->accidental != ' ') { - /* find space for this accidental */ - accplace = 0; - while ((accplace <10) && (ygap[accplace] < thisy)) { - accplace = accplace + 1; - }; - anote->acc_offset = accplace + 1; - ygap[accplace] = thisy - 6; - }; - }; - place = place->next; - }; -} - -static void sizechord(struct chord* ch, int ingrace) -/* decide on stem direction and set default stem length for a chord */ -{ - if ((ch->ytop + ch->ybot < 8)||(ingrace)) { - ch->stemup = 1; - } else { - ch->stemup = 0; - }; - ch->stemlength = STEMLEN; -} - -static void setxy(double* x, double* y, struct note* n, struct feature* ft, - double offset, double half_head) -/* compute x,y co-ordinates above note for drawing a beam */ -{ - if (n->stemup) { - *x = ft->x + half_head; - *y = (double)(TONE_HT*n->y) + n->stemlength - offset; - } else { - *x = ft->x - half_head; - *y = (double)(TONE_HT*n->y) - n->stemlength + offset; - }; -} - -static void drawtuple(struct feature* beamset[], int beamctr, int tupleno) -/* label a beam which is a tuplet of notes (e.g. triplet) */ -{ - double x0, x1, y0, y1; - struct note* n; - int stemup; - - x0 = beamset[0]->x; - n = beamset[0]->item; - stemup = n->stemup; - if (stemup) { - y0 = (double)(TONE_HT*n->y) + n->stemlength; - } else { - y0 = (double)(TONE_HT*n->y) - n->stemlength; - }; - x1 = beamset[beamctr-1]->x; - n = beamset[beamctr-1]->item; - if (stemup) { - y1 = (double)(TONE_HT*n->y) + n->stemlength; - } else { - y1 = (double)(TONE_HT*n->y) - n->stemlength; - }; - if (stemup) { - fprintf(f, " %.1f %.1f (%d) bnum", (x0+x1)/2, (y0+y1)/2 + TUPLE_UP, tupleno); - } else { - fprintf(f, " %.1f %.1f (%d) bnum", (x0+x1)/2, (y0+y1)/2 + TUPLE_DOWN, - tupleno); - }; -} - -static void drawhtuple(double xstart, double xend, int num, double y) -/* draw a tuple using half-brackets */ -/* used for tuples which do not correspond to a beam */ -{ - double xmid; - - xmid = (xstart + xend)/2; - fprintf(f, " %.1f %.1f %.1f %.1f hbr", xstart, y, xmid-6.0, y); - fprintf(f, " %.1f %.1f %.1f %.1f hbr", xend, y, xmid+6.0, y); - fprintf(f, " %.1f %.1f (%d) bnum\n", xmid, y-4.0, num); -} - -static void drawbeam(struct feature* beamset[], int beamctr, int dograce) -/* draw a beam spanning the notes in array beamset[] */ -{ - int i, d; - int donenotes; - int start, stop; - double x0, x1, y0, y1, offset; - struct note* n; - int stemup, beamdir; - double half_head; - - if (beamctr==0) { - event_error("Internal error: beam with 0 notes"); - showtune(&thetune); - return; - /* exit(0); [SS] 2017-11-17 */ - }; - if (beamset[0]->type != NOTE) { - event_error("Internal error: beam does not start with NOTE"); - exit(0); - }; - fprintf(f, "\n"); - if (redcolor) fprintf(f,"1.0 0.0 0.0 setrgbcolor\n"); - n = beamset[0]->item; - stemup = n->stemup; - beamdir = 2*stemup - 1; - donenotes = 1; - d = -3; - offset = 0.0; - if (dograce) { - half_head = GRACE_HALF_HEAD; - } else { - half_head = HALF_HEAD; - }; - while (donenotes) { - donenotes = 0; - start = -1; - for (i=0; iitem; - if (n->base_exp <= d) { - if (start == -1) { - start = i; - setxy(&x0, &y0, n, beamset[i], offset, half_head); - }; - stop = i; - setxy(&x1, &y1, n, beamset[i], offset, half_head); - }; - if ((n->beaming == endbeam) || (n->base_exp > d)) { - if (start != -1) { - /* draw unit */ - if (start == stop) { - if (start != 0) { - /* half line in front of note */ - n = beamset[start-1]->item; - setxy(&x0, &y0, n, beamset[start-1], offset, half_head); - x0 = x0 + (x1-x0)/2; - y0 = y0 + (y1-y0)/2; - } else { - /* half line behind note */ - n = beamset[start+1]->item; - setxy(&x1, &y1, n, beamset[start+1], offset, half_head); - x1 = x1 + (x0-x1)/2; - y1 = y1 + (y0-y1)/2; - }; - if (dograce) { - fprintf(f, "%.1f %.1f %.1f %.1f gbm2\n", x0, y0, x1, y1); - } else { - fprintf(f, "%.1f %.1f %.1f %.1f %.1f bm\n", x0, y0, x1, y1, - (double)(beamdir * TAIL_WIDTH)); - }; - } else { - if (dograce) { - fprintf(f, "%.1f %.1f %.1f %.1f gbm2\n", x0, y0, x1, y1); - } else { - fprintf(f, "%.1f %.1f %.1f %.1f %.1f bm\n", x0, y0, x1, y1, - (double)(beamdir * TAIL_WIDTH)); - }; - }; - donenotes = 1; - start = -1; - }; - }; - }; - d = d - 1; - offset = offset + TAIL_SEP; - }; - if (redcolor) fprintf(f,"0 setgray\n"); -} - -static void sizevoice(struct voice* v, struct tune* t) -/* compute width and height values for all elements in voice */ -{ - struct feature* ft; - struct note* anote; - struct key* akey; - struct aclef* theclef; - char* astring; - struct fract* afract; - struct rest* arest; - struct note* lastnote; - struct chord* thischord; - struct feature* chordplace; - struct tuple* thistuple; - enum tail_type chordbeaming; - int intuple, tuplecount; - struct feature* tuplefeature; - - ingrace = 0; - inchord = 0; - intuple = 0; - tuplefeature = NULL; - chordhead = NULL; - thischord = NULL; - chordplace = NULL; - v->clef->type = t->clef.type; - v->clef->octave = t->clef.octave; - if (v->keysig == NULL) { - event_error("Voice has no key signature"); - }; - ft = v->first; - lastnote = NULL; - while (ft != NULL) { - ft->xleft = 0.0; - ft->xright = 0.0; - ft->ydown = 0.0; - ft->yup = 0.0; - switch (ft->type) { - case SINGLE_BAR: - ft->xleft = (float) 0.8; - ft->xright = 0.0; - break; - case DOUBLE_BAR: - ft->xleft = 3.0; - ft->xright = 0.0; - break; - case BAR_REP: - ft->xleft = 1.0; - ft->xright = 10.0; - break; - case REP_BAR: - ft->xleft = 1.0; - ft->xright = 10.0; - break; - case REP1: - break; - case REP2: - break; - case PLAY_ON_REP: - break; - case BAR1: - break; - case REP_BAR2: - ft->xleft = 6.0; - ft->xright = 5.0; - break; - case DOUBLE_REP: - ft->xleft = 10.0; - ft->xright = 10.0; - break; - case THICK_THIN: - ft->xleft = 0.0; - ft->xright = 6.0; - break; - case THIN_THICK: - ft->xleft = 6.0; - ft->xright = 0.0; - break; - case PART: - astring = ft->item; - case TEMPO: - break; - case TIME: - afract = ft->item; - if (afract == NULL) { - afract = &v->meter; - }; - ft->xleft = 0; - ft->xright = (float) size_timesig(afract); - break; - case KEY: - ft->xleft = 0; - akey = ft->item; - ft->xright = (float) size_keysig(v->keysig->map, akey->map); - set_keysig(v->keysig, akey); - break; - case REST: - arest = ft->item; - sizerest(arest, ft); - if (intuple) { - if (ft->yup > thistuple->height) { - thistuple->height = ft->yup; - }; - tuplecount = tuplecount - 1; - if (tuplecount <= 0) { - tuplefeature->yup = thistuple->height + HTUPLE_HT; - intuple = 0; - thistuple = NULL; - tuplefeature = NULL; - }; - }; - break; - case TUPLE: - thistuple = ft->item; - if (thistuple->beamed == 0) { - intuple = 1; - tuplecount = thistuple ->r; - thistuple->height = (double)(10*TONE_HT); - tuplefeature = ft; - }; - break; - case NOTE: - anote = ft->item; - setstemlen(anote, ingrace); - sizenote(anote, ft, ingrace); - if (inchord) { - if (chordcount == 0) { - chordhead = ft; - thischord->ytop = anote->y; - thischord->ybot = anote->y; - chordbeaming = anote->beaming; - } else { - if (anote->y > thischord->ytop) { - thischord->ytop = anote->y; - }; - if (anote->y < thischord->ybot) { - thischord->ybot = anote->y; - }; - if (ft->xleft > chordhead->xleft) { - chordhead->xleft = ft->xleft; - }; - if (ft->xright > chordhead->xright) { - chordhead->xright = ft->xright; - }; - if (ft->yup > chordhead->yup) { - chordhead->yup = ft->yup; - }; - if (ft->ydown > chordhead->ydown) { - chordhead->ydown = ft->ydown; - }; - ft->type = CHORDNOTE; - }; - chordcount = chordcount + 1; - }; - if (intuple) { - if (ft->yup > thistuple->height) { - thistuple->height = ft->yup; - }; - tuplecount = tuplecount - 1; - if (tuplecount <= 0) { - tuplefeature->yup = thistuple->height + HTUPLE_HT; - intuple = 0; - thistuple = NULL; - tuplefeature =NULL; - }; - }; - break; - case NONOTE: - break; - case OLDTIE: - break; - case TEXT: - break; - case SLUR_ON: - break; - case SLUR_OFF: - break; - case TIE: - break; - case CLOSE_TIE: - break; - case TITLE: - break; - case CHANNEL: - break; - case TRANSPOSE: - break; - case RTRANSPOSE: - break; - case GRACEON: - ingrace = 1; - break; - case GRACEOFF: - ingrace = 0; - break; - case SETGRACE: - break; - case SETC: - break; - case GCHORD: - break; - case GCHORDON: - break; - case GCHORDOFF: - break; - case VOICE: - break; - case CHORDON: - inchord = 1; - chordcount = 0; - thischord = ft->item; - chordplace = ft; - spacechord(chordplace); - break; - case CHORDOFF: - if (thischord != NULL) { - anote = chordhead->item; - thischord->beaming = chordbeaming; - if (thischord->beaming == single) { - sizechord(thischord, ingrace); - }; - }; - inchord = 0; - chordhead = NULL; - thischord = NULL; - chordplace = NULL; - break; - case SLUR_TIE: - break; - case TNOTE: - break; - case LT: - break; - case GT: - break; - case DYNAMIC: - break; - case LINENUM: - lineno = (int)(ft->item); - break; - case MUSICLINE: - break; - case MUSICSTOP: - break; - case WORDLINE: - break; - case WORDSTOP: - break; - case INSTRUCTION: - break; - case NOBEAM: - break; - case CLEF: - theclef = ft->item; - if (theclef == NULL) { - theclef = v->clef; - }; - ft->yup = (double)8*TONE_HT; - ft->ydown = 0.0; - switch (theclef->type) { - case treble: - ft->yup = (double)TREBLE_UP; - ft->ydown = (double)TREBLE_DOWN; - ft->xright = TREBLE_RIGHT; - ft->xleft = TREBLE_LEFT; - break; - case baritone: - ft->yup = (double)12*TONE_HT; - ft->xright = CCLEF_RIGHT; - ft->xleft = CCLEF_LEFT; - break; - case tenor: - ft->yup = (double)10*TONE_HT; - ft->xright = CCLEF_RIGHT; - ft->xleft = CCLEF_LEFT; - break; - case alto: - ft->xright = CCLEF_RIGHT; - ft->xleft = CCLEF_LEFT; - break; - case mezzo: - ft->ydown = (double)2*TONE_HT; - ft->xright = CCLEF_RIGHT; - ft->xleft = CCLEF_LEFT; - break; - case soprano: - ft->ydown = (double)4*TONE_HT; - ft->xright = CCLEF_RIGHT; - ft->xleft = CCLEF_LEFT; - break; - case bass: - ft->xright = BASS_RIGHT; - ft->xleft = BASS_LEFT; - break; - case noclef: - break; - }; - if (theclef->octave > 0) { - ft->yup = ft->yup + CLEFNUM_HT; - }; - if (theclef->octave < 0) { - ft->ydown = ft->ydown + CLEFNUM_HT; - }; - v->clef->type = theclef->type; - v->clef->octave = theclef->octave; - break; - case PRINTLINE: - break; - case NEWPAGE: - break; - case LEFT_TEXT: - break; - case CENTRE_TEXT: - break; - case VSKIP: - break; - case SPLITVOICE: - break; - default: - printf("unknown type %d\n", ft->type); - break; - }; - ft = ft->next; - }; -} - -static void sizetune(struct tune* t) -/* compute width and height values for all elements in the tune */ -{ - struct voice* v; - - v = firstitem(&t->voices); - while (v != NULL) { - sizevoice(v, t); - v = nextitem(&t->voices); - }; -} - -static void singlehead(double x, double y, int base, int base_exp, int dots) -/* draws a note head */ -{ - int i; - double dot_offset; - - fprintf(f, "%.1f %.1f ", x, y); - /* note head */ - dot_offset = HALF_HEAD; - switch(base_exp) { - case 1: - fprintf(f, "BHD"); - dot_offset = HALF_BREVE; - break; - case 0: - fprintf(f, "HD"); - break; - case -1: - fprintf(f, "Hd"); - break; - default: - if (base_exp > 1) { - fprintf(f, "BHD"); - dot_offset = HALF_BREVE; - event_warning("Note value too long to represent"); - } else { - fprintf(f, "hd"); - }; - break; - }; - if (dots == -1) { - event_warning("Note value cannot be represented"); - }; - for (i=1; i <= dots; i++) { - fprintf(f, " %.1f 3.0 dt", (double)(dot_offset+DOT_SPACE*i)); - }; -} - -static void drawhead(struct note* n, double x, struct feature* ft) -/* draw just the head of a note together with any decorations */ -/* and accidentals */ -{ - int i; - char* decorators; - double ytop, ybot; - double notex, accspace; - int offset; - - if (n->fliphead) { - if (n->stemup) { - notex = x + 2.0 * HALF_HEAD; - } else { - notex = x - 2.0 * HALF_HEAD; - } - } else { - notex = x; - }; - singlehead(notex, (double)(TONE_HT*n->y), n->base, n->base_exp, n->dots); - if (n->acc_offset == 0) { - accspace = ACC_OFFSET; - } else { - if (n->fliphead) { - /* compensate for flipped note head */ - if (n->stemup) { - offset = n->acc_offset; - } else { - offset = n->acc_offset - 2; - }; - } else { - offset = n->acc_offset - 1; - }; - accspace = ACC_OFFSET + ACC_OFFSET2 * offset; - }; - switch (n->accidental) { - case '=': - fprintf(f, " %.1f nt", accspace); - break; - case '^': - if (n->mult == 1) { - fprintf(f, " %.1f sh", accspace); - } else { - fprintf(f, " %.1f dsh", accspace); - }; - break; - case '_': - if (n->mult == 1) { - fprintf(f, " %.1f ft", accspace); - } else { - fprintf(f, " %.1f dft", accspace); - }; - break; - default: - break; - }; - i = 10; - while (n->y >= i) { - fprintf(f, " %d hl", i*TONE_HT); - i = i + 2; - }; - i = -2; - while (n->y <= i) { - fprintf(f, " %d hl", i*TONE_HT); - i = i - 2; - }; - if (n->accents != NULL) { - decorators = n->accents; - ytop = (n->y + 2) * TONE_HT; - ybot = (n->y - 2) * TONE_HT; - if (n->stemup) { - ytop = ytop + n->stemlength; - } else { - ybot = ybot - n->stemlength; - }; - for (i=0; i< (int) strlen(decorators); i++) { - switch (decorators[i]) { - case '.': - if (n->stemup) { - fprintf(f, " %.1f stc", ybot+STC_OFF); - ybot = ybot - SMALL_DEC_HT; - } else { - fprintf(f, " %.1f stc", ytop+STC_OFF); - ytop = ytop + SMALL_DEC_HT; - }; - break; - case 'R': - if (n->stemup) { - fprintf(f, " %.1f cpd", ybot+CPD_OFF); - ybot = ybot - SMALL_DEC_HT; - } else { - fprintf(f, " %.1f cpu", ytop+CPU_OFF); - ytop = ytop + SMALL_DEC_HT; - }; - break; - case 'M': - if (n->stemup) { - fprintf(f, " %.1f emb", ybot+EMB_OFF); - ybot = ybot - SMALL_DEC_HT; - } else { - fprintf(f, " %.1f emb", ytop+EMB_OFF); - ytop = ytop + SMALL_DEC_HT; - }; - break; - case 'H': - fprintf(f, " %.1f hld", ytop+HLD_OFF); - ytop = ytop + BIG_DEC_HT; - break; - case '~': - fprintf(f, " %.1f grm", ytop+GRM_OFF); - ytop = ytop + BIG_DEC_HT; - break; - case 'u': - fprintf(f, " %.1f upb", ytop+UPB_OFF); - ytop = ytop + BIG_DEC_HT; - break; - case 'v': - fprintf(f, " %.1f dnb", ytop+DNB_OFF); - ytop = ytop + BIG_DEC_HT; - break; - case 'T': - fprintf(f, " %.1f trl", ytop+TRL_OFF); - ytop = ytop + BIG_DEC_HT; - break; - default: - break; - }; - }; - }; - fprintf(f, "\n"); -} - -static void drawgracehead(struct note* n, double x, struct feature* ft, - enum tail_type tail) -/* draw the head of a grace note */ -{ - int i; - double y; - - y = (double)(TONE_HT*n->y); - switch (tail) { - case nostem: - /* note head only */ - fprintf(f, "%.1f %.1f gn", x, y); - break; - case single: - /* note head with stem and tail */ - fprintf(f, "%.1f %.1f %.1f gn1", x, y, n->stemlength); - break; - case midbeam: - case startbeam: - case endbeam: - /* note head with stem and tail */ - fprintf(f, "%.1f %.1f %.1f gnt", x, y, n->stemlength); - break; - }; - switch (n->accidental) { - case '=': - fprintf(f, " %.1f %.1f gnt0", x-ACC_OFFSET, y); - break; - case '^': - if (n->mult == 1) { - fprintf(f, " %.1f %.1f gsh0", x-ACC_OFFSET, y); - } else { - fprintf(f, " %.1f %.1f gds0h", x-ACC_OFFSET, y); - }; - break; - case '_': - if (n->mult == 1) { - fprintf(f, " %.1f %.1f gft0", x-ACC_OFFSET, y); - } else { - fprintf(f, " %.1f %.1f gdf0", x-ACC_OFFSET, y); - }; - break; - default: - break; - }; - i = 10; - while (n->y >= i) { - fprintf(f, " %.1f %.1f ghl", x, (double)i*TONE_HT); - i = i + 2; - }; - i = -2; - while (n->y <= i) { - fprintf(f, " %.1f %.1f ghl", x, (double)i*TONE_HT); - i = i - 2; - }; - fprintf(f, "\n"); -} - -static void handlegracebeam(struct note *n, struct feature* ft) -/* this is called to set up the variables needed to lay out */ -/* beams for grace notes */ -{ - switch (n->beaming) { - case single: - gracebeamctr = -1; - break; - case startbeam: - gracebeamset[0] = ft; - gracebeamctr = 1; - break; - case midbeam: - case endbeam: - if (gracebeamctr > 0) { - gracebeamset[gracebeamctr] = ft; - if (gracebeamctr < 32) { - gracebeamctr = gracebeamctr + 1; - } else { - event_error("Too many notes under one gracebeam"); - }; - } else { - event_error("Internal beaming error"); - exit(1); - }; - break; - case nostem: - break; - }; -} - -static void drawgracenote(struct note* n, double x, struct feature* ft, - struct chord* thischord) -/* draw an entire grace note */ -{ - handlegracebeam(n, ft); - drawgracehead(n, x, ft, n->beaming); - if (n->beaming == endbeam) { - drawbeam(gracebeamset, gracebeamctr, 1); - }; - fprintf(f, "\n"); -} - -static void singletail(int base, int base_exp, int stemup, double stemlength) -/* draw the tail of a note if it has one */ -{ - switch(base_exp) { - case 0: - case -1: - case -2: - break; - case -3: - if (stemup) { - fprintf(f, " %.1f f1u", stemlength); - } else { - fprintf(f, " %.1f f1d", stemlength); - }; - break; - case -4: - if (stemup) { - fprintf(f, " %.1f f2u", stemlength); - } else { - fprintf(f, " %.1f f2d", stemlength); - }; - break; - case -5: - if (stemup) { - fprintf(f, " %.1f f3u", stemlength); - } else { - fprintf(f, " %.1f f3d", stemlength); - }; - break; - case -6: - if (stemup) { - fprintf(f, " %.1f f4u", stemlength); - } else { - fprintf(f, " %.1f f4d", stemlength); - }; - break; - default: - if (base_exp < -6) { - event_warning("Note value too small to represent"); - if (stemup) { - fprintf(f, " %.1f f4u", stemlength); - } else { - fprintf(f, " %.1f f4d", stemlength); - }; - }; - break; - }; -} - -static void drawchordtail(struct chord* ch, double x) -/* draw the tail to a set of notes belonging to a single chord */ -{ - if (ch->base > 1) { - fprintf(f, "%.1f setx ", x); - if (ch->stemup) { - fprintf(f, "%.1f sety ", (double)(ch->ybot*TONE_HT)); - fprintf(f, " %.1f su ", ch->stemlength + - (double)((ch->ytop - ch->ybot)*TONE_HT)); - } else { - fprintf(f, "%.1f sety ", (double)(ch->ytop*TONE_HT)); - fprintf(f, " %.1f sd ", ch->stemlength + - (double)((ch->ytop - ch->ybot)*TONE_HT)); - }; - if (ch->beaming == single) { - singletail(ch->base, ch->base_exp, ch->stemup, ch->stemlength+ - (double)((ch->ytop - ch->ybot)*TONE_HT)); - }; - fprintf(f, "\n"); - }; -} - -static void showtext(struct llist* textitems, double x, double ystart, double ygap) -/* This routine deals with lyrics, guitar chords and instructions */ -/* print text item at specified point */ -/* if a string starts with ':' this means it is a special symbol */ -{ - double ygc; - char* gc; - - gc = firstitem(textitems); - ygc = ystart; - while (gc != NULL) { - if (*gc == ':') { - switch (*(gc+1)) { - case 'p': /* part label */ - fprintf(f, " %.1f %.1f %.1f (", x, ygc, ygap); - ISOfprintf(gc+2); - fprintf(f, ") boxshow "); - break; - case 's': - fprintf(f, " %.1f %.1f segno\n", x, ygc); - break; - case 'c': - fprintf(f, " %.1f %.1f coda\n", x, ygc); - break; - default: - break; - }; - } else { - fprintf(f, " %.1f %.1f (", x, ygc); - ISOfprintf(gc); - fprintf(f, ") gc "); - }; - gc = nextitem(textitems); - ygc = ygc - ygap; - }; -} - -static void define_font(struct font* thefont, char* s) -/* set up a font structure with user-defined font and pointsize */ -{ - char fontname[80]; - int fontsize, params; - - params = sscanf(s, "%s %d", fontname, &fontsize); - if (params >= 1) { - if (strcmp(fontname, "-") != 0) { - if (thefont->name != NULL) { - free(thefont->name); - }; - thefont->name = addstring(fontname); - thefont->defined = 0; - }; - if (params == 2) { - thefont->pointsize = fontsize; - }; - }; -} - -int read_boolean(s) -char *s; -/* set logical parameter from %%command */ -/* part of the handling for event_specific */ -{ - char p[10]; - int result; - - p[0] = '\0'; - sscanf(s, " %10s", p); - result = 1; - if ((strcmp(p, "0") == 0) || (strcmp(p, "no") == 0) || - (strcmp(p, "false") == 0)) { - result = 0; - }; - return(result); -} - -void font_command(p, s) -/* execute font-related %%commands */ -/* called from event_specific */ -char* p; -char*s; -{ - if (strcmp(p, "textfont") == 0) { - define_font(&textfont, s); - }; - if (strcmp(p, "titlefont") == 0) { - define_font(&titlefont, s); - }; - if (strcmp(p, "subtitlefont") == 0) { - define_font(&subtitlefont, s); - }; - if (strcmp(p, "wordsfont") == 0) { - define_font(&wordsfont, s); - }; - if (strcmp(p, "composerfont") == 0) { - define_font(&composerfont, s); - }; - if (strcmp(p, "vocalfont") == 0) { - define_font(&vocalfont, s); - }; - if (strcmp(p, "gchordfont") == 0) { - define_font(&gchordfont, s); - }; - if (strcmp(p, "partsfont") == 0) { - define_font(&partsfont, s); - }; - if (strcmp(p, "titlespace") == 0) { - set_space(&titlefont, s); - }; - if (strcmp(p, "subtitlespace") == 0) { - set_space(&subtitlefont, s); - }; - if (strcmp(p, "composerspace") == 0) { - set_space(&composerfont, s); - }; - if (strcmp(p, "vocalspace") == 0) { - set_space(&vocalfont, s); - }; - if (strcmp(p, "wordsspace") == 0) { - set_space(&wordsfont, s); - }; - if (strcmp(p, "gchordspace") == 0) { - set_space(&gchordfont, s); - }; - if (strcmp(p, "textspace") == 0) { - set_space(&textfont, s); - }; - if (strcmp(p, "partsspace") == 0) { - set_space(&textfont, s); - }; - if (strcmp(p, "staffsep") == 0) { - sscanf(s, "%d", &staffsep); - }; - if (strcmp(p, "titleleft") == 0) { - titleleft = read_boolean(s); - }; - if (strcmp(p, "titlecaps") == 0) { - titlecaps = read_boolean(s); - }; -} - -static void setfont(int size, int num) -/* define font point size and style */ -{ - if ((fontsize != size) || (fontnum != num)) { - fprintf(f, "%d.0 F%d\n", size, num); - fontsize = size; - fontnum = num; - }; -} - -static void setfontstruct(struct font* thefont) -/* set font according to font structure */ -{ - if (thefont->name == NULL) { - setfont(thefont->pointsize, thefont->default_num); - } else { - if (thefont->defined == 0) { - /* define font */ - fprintf(f, "/%s-ISO /%s setISOfont\n", thefont->name, thefont->name); - fprintf(f, "/F%d { /%s-ISO exch selectfont} def\n", - thefont->special_num, thefont->name); - thefont->defined = 1; - } - setfont(thefont->pointsize, thefont->special_num); - }; -} - -static void notetext(struct note* n, int* tupleno, double x, struct feature* ft, - struct vertspacing* spacing) -/* print text associated with note */ -{ - if (n->beaming == endbeam) { - drawbeam(beamset, beamctr, 0); - if (*tupleno != 0) { - drawtuple(beamset, beamctr, *tupleno); - *tupleno = 0; - }; - }; - fprintf(f, "\n"); - if (n->instructions != NULL && spacing != NULL) { - setfontstruct(&partsfont); - showtext(n->instructions, x - ft->xleft, spacing->yinstruct, - (double)(partsfont.pointsize+partsfont.space)); - }; - if (n->gchords != NULL && spacing != NULL) { - setfontstruct(&gchordfont); - showtext(n->gchords, x - ft->xleft, spacing->ygchord, - (double)(gchordfont.pointsize+gchordfont.space)); - }; - if (n->syllables != NULL && spacing != NULL) { - setfontstruct(&vocalfont); - showtext(n->syllables, x - ft->xleft, spacing->ywords, - (double)(vocalfont.pointsize + vocalfont.space)); - }; -} - -static void handlebeam(struct note* n, struct feature* ft) -/* set up some variables needed to lay out the beam for a set of */ -/* notes beamed together */ -{ - switch (n->beaming) { - case single: - beamctr = -1; - break; - case startbeam: - beamset[0] = ft; - beamctr = 1; - rootstem = n->stemup; - break; - case midbeam: - case endbeam: - if (beamctr > 0) { - beamset[beamctr] = ft; - if (beamctr < 64) { - beamctr = beamctr + 1; - } else { - event_error("Too many notes under one beam"); - }; - } else { - event_error("Internal beaming error"); - }; - n->stemup = rootstem; - break; - }; -} - -static void drawnote(struct note* n, double x, struct feature* ft, - struct chord* thischord, - int* tupleno, struct vertspacing* spacing) -/* draw a note */ -{ - handlebeam(n, ft); - if (redcolor) fprintf(f,"1.0 0.0 0.0 setrgbcolor\n"); - drawhead(n, x, ft); - if (thischord == NULL) { - if (n->base > 1) { - if (n->stemup) { - fprintf(f, " %.1f su", n->stemlength); - } else { - fprintf(f, " %.1f sd", n->stemlength); - }; - }; - }; - if (n->beaming == single) { - singletail(n->base, n->base_exp, n->stemup, n->stemlength); - }; - fprintf(f, "\n"); - if (redcolor) fprintf(f,"0 setgray\n"); - notetext(n, tupleno, x, ft, spacing); -} - -static void drawrest(struct rest* r, double x, struct vertspacing* spacing) -/* draw a rest of specified duration */ -{ - int i; - - if (redcolor) fprintf(f,"1.0 0.0 0.0 setrgbcolor\n"); - if (r->multibar > 0) { - fprintf(f, "(%d) %.1f %d mrest ", r->multibar, x, 4*TONE_HT); - } else { - reducef(&r->len); - switch (r->base_exp) { - case 1: - fprintf(f, "%.1f %d r0 ", x, 4*TONE_HT); - break; - case 0: - fprintf(f, "%.1f %d r1 ", x, 4*TONE_HT); - break; - case -1: - fprintf(f, "%.1f %d r2 ", x, 4*TONE_HT); - break; - case -2: - fprintf(f, "%.1f %d r4 ", x, 4*TONE_HT); - break; - case -3: - fprintf(f, "%.1f %d r8 ", x, 4*TONE_HT); - break; - case -4: - fprintf(f, "%.1f %d r16 ", x, 4*TONE_HT); - break; - case -5: - fprintf(f, "%.1f %d r32 ", x, 4*TONE_HT); - break; - case -6: - fprintf(f, "%.1f %d r64 ", x, 4*TONE_HT); - break; - default: - event_error("Cannot represent rest length"); - break; - }; - }; - for (i=1; i <= r->dots; i++) { - fprintf(f, " %.1f 3.0 dt", (double)(HALF_HEAD+DOT_SPACE*i)); - }; - fprintf(f, "\n"); - if (redcolor) fprintf(f,"0 setgray\n"); - if (r->instructions != NULL) { - setfontstruct(&partsfont); - showtext(r->instructions, x, spacing->yinstruct, - (double)(partsfont.pointsize+partsfont.space)); - }; - if (r->gchords != NULL) { - setfontstruct(&gchordfont); - showtext(r->gchords, x, spacing->ygchord, - (double)(gchordfont.pointsize+gchordfont.space)); - }; -} - -static void setbeams(struct feature* note[], struct chord* chording[], int m, - double stemlen) -/* choose the spatial postioning of beams and compute appropriate stem */ -/* lengths for all notes within the beam */ -{ - int i; - double min[64]; - double lift; - struct note* anote; - int stemup; - double x1, y1, x2, y2, xi, yi; - double max; - - /* printf("Doing setbeams m=%d\n", m); */ - anote = note[0]->item; - stemup = anote->stemup; - /* calculate minimum feasible stem lengths */ - /* bearing in mind space needed for the tails */ - for (i=0; iitem; - anote->stemup = stemup; - switch (anote->base) { - default: - case 8: - min[i] = TAIL_SEP; - break; - case 16: - min[i] = 2*TAIL_SEP; - break; - case 32: - min[i] = 3*TAIL_SEP; - break; - case 64: - min[i] = 4*TAIL_SEP; - break; - }; - if (anote->accidental == ' ') { - min[i] = min[i] + TONE_HT; - } else { - if (stemup) { - min[i] = min[i] + (double)acc_upsize(anote->accidental); - } else { - min[i] = min[i] + (double)acc_downsize(anote->accidental); - }; - }; - min[i] = min[i] + TONE_HT; /* require at least this much clearance */ - if (stemup) { - if (chording[i] == NULL) { - min[i] = TONE_HT*anote->y + min[i]; - /* avoid collision with previous note */ - if ((i > 0) && (min[i-1] - TONE_HT < min[i])) { - min[i-1] = min[i]; - }; - } else { - min[i] = TONE_HT*chording[i]->ytop + min[i]; - }; - } else { - if (chording[i] == NULL) { - min[i] = TONE_HT*anote->y - min[i]; - /* avoid collision with previous note */ - if (i > 0) { - anote = note[i-1]->item; - if (anote->y*TONE_HT < min[i]) { - min[i] = anote->y*TONE_HT; - }; - }; - } else { - min[i] = TONE_HT*chording[i]->ybot - min[i]; - }; - }; - }; - /* work out clearance from a straight line between 1st and last note */ - x1 = note[0]->x; - anote = note[0]->item; - y1 = anote->y*TONE_HT; - x2 = note[m-1]->x; - anote = note[m-1]->item; - y2 = anote->y*TONE_HT; - if (x1 == x2) { - printf("Internal error: x1 = x2 = %.1f\n", x1); - x2 = x2 + 1; - }; - lift = 0.0; - max = 0.0; - if (stemup) { - for (i=0; ix; - yi = y1 + (y2-y1)*(xi-x1)/(x2-x1); - if (min[i] - yi > lift) { - lift = min[i] - yi; - }; - if (min[i] - yi < max) { - max = min[i] - yi; - }; - }; - if (lift - max < stemlen) { - lift = stemlen - max; - }; - } else { - for (i=0; ix; - yi = y1 + (y2-y1)*(xi-x1)/(x2-x1); - if (min[i] - yi < lift) { - lift = min[i] - yi; - }; - if (min[i] - yi > max) { - max = min[i] - yi; - }; - }; - if (max - lift < stemlen) { - lift = max - stemlen; - }; - }; - /* raise the line just enough to clear notes */ - for (i=0; iitem; - xi = note[i]->x; - if (stemup) { - anote->stemlength = (float) (y1 + (y2-y1)*(xi-x1)/(x2-x1) + lift - - TONE_HT*anote->y); - } else { - anote->stemlength = - (float) ((y1 + (y2-y1)*(xi-x1)/(x2-x1) + lift - - TONE_HT*anote->y)); - }; - }; - /* now transfer results to chords */ - for (i=0; iitem; - chording[i]->stemup = anote->stemup; - if (chording[i]->stemup) { - chording[i]->stemlength = anote->stemlength; - } else { - chording[i]->stemlength = (float) (anote->stemlength - - (double)((chording[i]->ytop - chording[i]->ybot)*TONE_HT)); - }; - }; - }; -}; - -static void beamline(struct feature* ft) -/* compute positioning of beams for one stave line of music */ -{ - struct feature* p; - struct note* n; - struct feature* beamnote[64]; - struct chord* chording[64]; - struct chord* lastchord; - struct feature* gracebeamnote[64]; - struct chord* gracechording[64]; - struct chord* gracelastchord; - int i, j; - int ingrace; - - - i = j = 0; /* [SS] 2019-08-11 not set in conditional line 2426 when - beamline() is called from finalsizeline() - https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=890250 */ - /* [SDG] 2020-06-03 also set j */ - p = ft; - ingrace = 0; - lastchord = NULL; - gracelastchord = NULL; - while ((p != NULL) && (p->type != PRINTLINE)) { - switch (p->type) { - case NOTE: - if (ingrace) { - n = p->item; - if (n == NULL) { - event_error("Missing NOTE!!!!"); - exit(0); - }; - if (n->beaming == startbeam) { - j = 0; - }; - if (n->beaming != single) { - gracebeamnote[j] = p; - gracechording[j] = gracelastchord; - j = j + 1; - }; - if ((n->beaming == endbeam)||(j==10)) { - setbeams(gracebeamnote, gracechording, j, GRACE_STEMLEN); - j = 0; - }; - } else { - /* non-grace notes*/ - n = p->item; - if (n == NULL) { - event_error("Missing NOTE!!!!"); - exit(0); - }; - if (n->beaming == startbeam) { - i = 0; - }; - if (n->beaming != single) { - beamnote[i] = p; - chording[i] = lastchord; - i = i + 1; - }; - if ((n->beaming == endbeam)||(i==64)) { - setbeams(beamnote, chording, i, STEMLEN); - i = 0; - }; - }; - break; - case CHORDON: - if (ingrace) { - gracelastchord = p->item; - } else { - lastchord = p->item; - }; - break; - case CHORDOFF: - if (ingrace) { - gracelastchord = NULL; - } else { - lastchord = NULL; - }; - break; - case GRACEON: - ingrace = 1; - break; - case GRACEOFF: - ingrace = 0; - gracelastchord = NULL; - break; - default: - break; - }; - p = p->next; - }; -} - -static void measureline(struct feature* ft, double* height, double* descender, - double* yend, double* yinstruct, double* ygchord, double* ywords) -/* calculate vertical space (height/descender) requirement of current line */ -{ - struct feature* p; - struct note* n; - struct rest* r; - double max, min; - int gotgchords, gcount; - int gotinstructions, icount; - int gotwords, wcount; - int gotends; - char* gc; - - p = ft; - min = 0.0; - max = 8*TONE_HT; - gotgchords = 0; - gotwords = 0; - gotinstructions = 0; - gotends = 0; - while ((p != NULL) && (p->type != PRINTLINE)) { - switch (p->type) { - case NOTE: - n = p->item; - if (n == NULL) { - event_error("Missing NOTE!!!!"); - exit(0); - } else { - if (n->gchords != NULL) { - gcount = 0; - gc = firstitem(n->gchords); - while (gc != NULL) { - gcount = gcount + 1; - gc = nextitem(n->gchords); - }; - if (gcount > gotgchords) { - gotgchords = gcount; - }; - }; - if (n->instructions != NULL) { - icount = 0; - gc = firstitem(n->instructions); - while (gc != NULL) { - icount = icount + 1; - gc = nextitem(n->instructions); - }; - if (icount > gotinstructions) { - gotinstructions = icount; - }; - }; - if (n->syllables != NULL) { - wcount = 0; - gc = firstitem(n->syllables); - while (gc != NULL) { - wcount = wcount + 1; - gc = nextitem(n->syllables); - }; - if (wcount > gotwords) { - gotwords = wcount; - }; - }; - /* recalculate note size in case of new stemlen */ - sizenote(n, p, ingrace); - if (p->yup > max) { - max = p->yup; - }; - if (p->ydown < min) { - min = p->ydown; - }; - }; - break; - case REST: - r = p->item; - if (r == NULL) { - event_error("Missing REST!!!!"); - exit(0); - } else { - if (r->gchords != NULL) { - gcount = 0; - gc = firstitem(r->gchords); - while (gc != NULL) { - gcount = gcount + 1; - gc = nextitem(r->gchords); - }; - if (gcount > gotgchords) { - gotgchords = gcount; - }; - }; - if (r->instructions != NULL) { - icount = 0; - gc = firstitem(r->instructions); - while (gc != NULL) { - icount = icount + 1; - gc = nextitem(r->instructions); - }; - if (icount > gotinstructions) { - gotinstructions = icount; - }; - }; - }; - break; - case REP1: - case REP2: - case PLAY_ON_REP: - case BAR1: - case REP_BAR2: - gotends = 1; - break; - case TUPLE: - case CLEF: - if (p->yup > max) { - max = p->yup; - }; - if (-(p->ydown) < min) { - min = -(p->ydown); - }; - break; - case TEMPO: - if (gotinstructions == 0) { - gotinstructions = 1; - }; - break; - default: - break; - }; - p = p->next; - }; - if ((gotgchords > 0) && (gchords_above)) { - max = max + (gchordfont.pointsize + gchordfont.space)*gotgchords; - *ygchord = max - (gchordfont.pointsize + gchordfont.space); - }; - if (gotinstructions > 0) { - max = max + INSTRUCT_HT*gotinstructions; - *yinstruct = max - INSTRUCT_HT; - }; - if (gotends) { - max = max + END_HT; - *yend = max - END_HT; - }; - if ((gotgchords > 0) && (!gchords_above)) { - *ygchord = min - (gchordfont.pointsize + gchordfont.space); - min = min - (gchordfont.pointsize + gchordfont.space)*gotgchords; - }; - if (gotwords > 0) { - *ywords = min - (vocalfont.pointsize + vocalfont.space); - min = min - (vocalfont.pointsize + vocalfont.space)*gotwords; - }; - *height = staffsep + max; - *descender = -min; -} - -static void printtext(place, s, afont) -enum placetype place; -char* s; -struct font* afont; -/* print text a line of text in specified font at specified place */ -{ - newblock((double)(afont->pointsize + afont->space), 0.0); - setfontstruct(afont); - fprintf(f, "("); - ISOfprintf(s); - switch (place) { - case left: - fprintf(f, ") 0 0 M show\n"); - break; - case right: - fprintf(f, ") %.1f 0 M lshow\n", (double) scaledwidth); - break; - case centre: - fprintf(f, ") %.1f 0 M cshow\n", scaledwidth/2.0); - break; - }; -} - -void vskip(double gap) -/* skip over specified amount of vertical space */ -{ - newblock(gap, 0.0); -} - -void centretext(s) -char* s; -/* print text centred in the middle of the page */ -/* used by %%centre command */ -{ - printtext(centre, s, &textfont); -} - -void lefttext(s) -char* s; -/* print text up against the left hand margin */ -/* used by %%text command */ -{ - printtext(left, s, &textfont); -} - -static void draw_tempo(double x_init, double y_init, struct atempo* t) -/* print out tempo as specified in Q: field */ -/* this goes to the left of the tune's title */ -{ - int base, base_exp, dots; - double x, y; - char ticks[30]; - - if (t != NULL) { - x = x_init; - y = y_init; - /* fprintf(f, "gsave 0.7 0.7 scale\n"); */ - setfont(12, 3); - if (t->pre != NULL) { - fprintf(f, "%.1f %.1f M (", x, y); - ISOfprintf(t->pre); - fprintf(f, ") show\n"); - x = x + stringwidth(t->pre, 12, 3) + HALF_HEAD * 2; - }; - if (t->count != 0) { - dots = count_dots(&base, &base_exp, t->basenote.num, t->basenote.denom); - singlehead(x, y, base, base_exp, dots); - if (base >= 2) { - fprintf(f, " %.1f su", TEMPO_STEMLEN); - }; - singletail(base, base_exp, 1, TEMPO_STEMLEN); - x = x + 20.0; - sprintf(ticks, "= %d", t->count); - fprintf(f, " %.1f %.1f M (%s) show\n", x, y, ticks); - x = x + stringwidth(ticks, 12, 3); - }; - if (t->post != NULL) { - fprintf(f, "%.1f %.1f M ( ", x, y); - ISOfprintf(t->post); - fprintf(f, ") show\n"); - }; - /* fprintf(f, "grestore\n"); */ - }; -} - -static void voicedivider() -/* This draws a horizontal line. It is used to mark where one voice */ -/* ends and the next one starts */ -{ - newblock((double)staffsep, 0.0); - fprintf(f, " %.1f %.1f M %.1f %.1f L\n", - scaledwidth/4.0, -descend, - scaledwidth*3/4.0, -descend); - newblock((double)staffsep, 0.0); -} - -static void resetvoice(struct tune* t, struct voice * v) -/* sets up voice with initial attributes as defined in abc tune header */ -{ - v->place = v->first; - if (v->clef == NULL) { - v->clef = newclef(t->clef.type, t->clef.octave); - } else { - v->clef->type = t->clef.type; - v->clef->octave = t->clef.octave; - }; - if (v->keysig == NULL) { - v->keysig = newkey(t->keysig->name, t->keysig->sharps, - t->keysig->map, t->keysig->mult); - } else { - free(v->keysig->name); - v->keysig->name = addstring(t->keysig->name); - set_keysig(v->keysig, t->keysig); - /* v->keysig->sharps = t->keysig->sharps; */ - }; - v->meter.num = t->meter.num; - v->meter.denom = t->meter.denom; -} - -static void resettune(struct tune* t) -/* initializes all voices to their start state */ -{ - struct voice* v; - - v = firstitem(&t->voices); - while (v != NULL) { - resetvoice(t, v); - v = nextitem(&t->voices); - }; -} - -void setup_fonts() -{ - init_font(&textfont, TEXT_HT, 1, 1, 5); - init_font(&titlefont, TITLE1_HT, 0, 0, 6); - init_font(&subtitlefont, TITLE2_HT, 0, 0, 7); - init_font(&wordsfont, WORDS_HT, 1, 0, 8); - init_font(&composerfont, COMP_HT, 1, 1, 9); - init_font(&vocalfont, LYRIC_HT, 1, 2, 10); - init_font(&gchordfont, CHORDNAME_HT, 1, 3, 11); - init_font(&partsfont, INSTRUCT_HT, 1, 3, 12); - staffsep = VERT_GAP; -} - -void open_output_file(filename, boundingbox) -char* filename; -struct bbox* boundingbox; -/* open output file and write the abc2ps library to it */ -{ - f = fopen(filename, "w"); - if (f == NULL) { - printf("Could not open file!!\n"); - exit(0); - }; - printf("writing file %s\n", filename); - printlib(f, filename, boundingbox); - fontsize = 0; - fontnum = 0; - startpage(); -} - -void close_output_file() -/* complete last page and close file */ -{ - if (f != NULL) { - closepage(); - fclose(f); - f = NULL; - }; -} - -static int endrep(inend, end_string, x1, x2, yend) -int inend; -char* end_string; -double x1, x2, yend; -/* draws either 1st or 2nd ending marker */ -{ - if (inend != 0) { - fprintf(f, "%.1f %.1f %.1f (%s) endy1\n", yend, x1, x2, end_string); - }; - return(0); -} - -static void drawslurtie(struct slurtie* s) -/* draws slur or tie corresponding to the given structure */ -{ - double x0, y0, x1, y1; - struct feature* ft; - struct note* n; - struct rest* r; - - ft = s->begin; - if (ft == NULL) { - event_error("Slur or tie missing start note"); - return; - }; - x0 = ft->x; - if ((ft->type == NOTE) || (ft->type == CHORDNOTE)) { - n = ft->item; - y0 = (double)(n->y * TONE_HT); - } else { - if (ft->type == REST) { - r = ft->item; - y0 = (double)(4 * TONE_HT); - } else { - printf("Internal error: NOT A NOTE/REST (%d)in slur/tie\n" ,ft->type); - return; - }; - }; - if (s->crossline) { - /* draw to end of stave */ - x1 = scaledwidth + xmargin/(2.0*scale); - y1 = y0; - } else { - /* draw slur/tie to other element */ - ft = s->end; - if (ft == NULL) { - event_error("Slur or tie missing end note"); - return; - }; - x1 = ft->x; - if ((ft->type == NOTE) || (ft->type == CHORDNOTE)) { - n = ft->item; - y1 = (double)(n->y * TONE_HT); - } else { - if (ft->type == REST) { - r = ft->item; - y1 = (double)(4 * TONE_HT); - } else { - printf("Internal error: NOT A NOTE/REST (%d)in slur/tie\n" ,ft->type); - return; - }; - }; - }; - if (n->stemup) { - fprintf(f, " %.1f %.1f %.1f %.1f slurdown\n", x0, y0, x1, y1); - } else { - fprintf(f, " %.1f %.1f %.1f %.1f slurup\n", x0, y0, x1, y1); - }; -} - -static void close_slurtie(struct slurtie* s) -/* This deals with the special case of a slur or tie running beyond */ -/* one line of music and into the next. This draws the part on the */ -/* second line of music */ -{ - double x0, y0, x1, y1; - struct feature* ft; - struct note* n; - struct rest* r; - - if ((s == NULL) || (s->crossline == 0)) { - return; - } else { - /* draw slur/tie to other element */ - ft = s->end; - if (ft == NULL) { - event_error("Slur or tie missing end note"); - return; - }; - x1 = ft->x; - if ((ft->type == NOTE) || (ft->type == CHORDNOTE)) { - n = ft->item; - y1 = (double)(n->y * TONE_HT); - } else { - if (ft->type == REST) { - r = ft->item; - y1 = (double)(4 * TONE_HT); - } else { - printf("Internal error: NOT A NOTE/REST (%d)in slur/tie\n" ,ft->type); - return; - }; - }; - }; - x0 = TREBLE_LEFT + TREBLE_RIGHT; - y0 = y1; - if (n->stemup) { - fprintf(f, " %.1f %.1f %.1f %.1f slurdown\n", x0, y0, x1, y1); - } else { - fprintf(f, " %.1f %.1f %.1f %.1f slurup\n", x0, y0, x1, y1); - }; -} - -static void blockline(struct voice* v, struct vertspacing** spacing) -/* gets vertical spacing for current line */ -{ - struct feature* ft; - /* struct vertspacing* spacing; */ - - ft = v->place; - /* find end of line */ - while ((ft != NULL) && (ft->type != PRINTLINE)) { - ft = ft->next; - }; - if ((ft != NULL) && (ft->type == PRINTLINE)) { - *spacing = ft->item; - } else { - *spacing = NULL; - event_error("Expecting line of music - possible voices mis-match"); - }; -} - -static void printbarnumber(double x, int n) -{ -if (barnums <0 ) return; -if (x + 15.0 > scaledwidth) return; -fprintf(f, " %.1f %.1f (%d) bnum\n", x, 28.0, n); -} - -static void underbar(struct feature* ft) -/* This is never normally called, but is useful as a debugging routine */ -/* shows width of a graphical element */ -{ - fprintf(f, " %.1f -10 moveto %.1f -10 lineto stroke\n", ft->x - ft->xleft, - ft->x + ft->xright); -} - -static int printvoiceline(struct voice* v) -/* draws one line of music from specified voice */ -{ - struct feature* ft; - struct note* anote; - struct key* akey; - char* astring; - struct fract* afract; - struct rest* arest; - struct tuple* atuple; - double x; - int sharps; - struct chord* thischord; - int chordcount; - double xchord; - struct aclef* theclef; - int printedline; - double xend; - int inend; - char endstr[80]; - int ingrace; - struct vertspacing* spacing; - struct dynamic *psaction; - - /* skip over line number so we can check for end of tune */ - while ((v->place != NULL) && - ((v->place->type == LINENUM) || (v->place->type == NEWPAGE) || - (v->place->type == LEFT_TEXT) || (v->place->type == CENTRE_TEXT) || - (v->place->type == VSKIP))) { - if (v->place->type == LINENUM) { - lineno = (int)(v->place->item); - }; - if (v->place->type == NEWPAGE) { - newpage(); - }; - if (v->place->type == LEFT_TEXT) { - printtext(left, v->place->item, &textfont); - }; - if (v->place->type == CENTRE_TEXT) { - printtext(centre, v->place->item, &textfont); - }; - if (v->place->type == VSKIP) { - vskip((double)((int)v->place->item)); - }; - v->place = v->place->next; - }; - if (v->place == NULL) { - return(0); - }; - inend = 0; - ingrace = 0; - chordcount = 0; - v->beamed_tuple_pending = 0; - x = 20; - thischord = NULL; - if (v->keysig == NULL) { - event_error("Voice has no key signature"); - } else { - sharps = v->keysig->sharps; - }; - /* draw stave */ - ft = v->place; - blockline(v, &spacing); - if (spacing != NULL) { - newblock(spacing->height, spacing->descender); - staveline(); - fprintf(f, "0 0 M 0 24 L\n"); - }; - x = 0.0; - /* now draw the line */ - printedline = 0; - while ((ft != NULL) && (!printedline)) { - /* printf("type = %d\n", ft->type); */ - switch (ft->type) { - case SINGLE_BAR: - fprintf(f, "%.1f bar\n", ft->x); - printbarnumber(ft->x, (int)ft->item); - break; - case DOUBLE_BAR: - fprintf(f, "%.1f dbar\n", ft->x); - printbarnumber(ft->x, (int)ft->item); - inend = endrep(inend, endstr, xend, ft->x, spacing->yend); - break; - case BAR_REP: - fprintf(f, "%.1f fbar1 %.1f rdots\n", ft->x, ft->x+10); - printbarnumber(ft->x, (int)ft->item); - inend = endrep(inend, endstr, xend, ft->x, spacing->yend); - break; - case REP_BAR: - fprintf(f, "%.1f rdots %.1f fbar2\n", ft->x, ft->x+10); - printbarnumber(ft->x, (int)ft->item); - inend = endrep(inend, endstr, xend, ft->x, spacing->yend); - break; - case REP1: - inend = endrep(inend, endstr, xend, ft->x - ft->xleft, spacing->yend); - inend = 1; - strcpy(endstr, "1"); - xend = ft->x + ft->xright; - break; - case REP2: - inend = endrep(inend, endstr, xend, ft->x - ft->xleft, spacing->yend); - inend = 2; - strcpy(endstr, "2"); - xend = ft->x + ft->xright; - break; - case PLAY_ON_REP: - inend = endrep(inend, endstr, xend, ft->x - ft->xleft, spacing->yend); - inend = 1; - strcpy(endstr, ft->item); - xend = ft->x + ft->xright; - break; - case BAR1: - fprintf(f, "%.1f bar\n", ft->x); - printbarnumber(ft->x, (int)ft->item); - inend = endrep(inend, endstr, xend, ft->x - ft->xleft, spacing->yend); - inend = 1; - strcpy(endstr, "1"); - xend = ft->x + ft->xright; - break; - case REP_BAR2: - fprintf(f, "%.1f rdots %.1f fbar2\n", ft->x, ft->x+10); - printbarnumber(ft->x, (int)ft->item); - inend = endrep(inend, endstr, xend, ft->x - ft->xleft, spacing->yend); - inend = 2; - strcpy(endstr, "2"); - xend = ft->x + ft->xright; - break; - case DOUBLE_REP: - fprintf(f, "%.1f fbar1 %.1f rdots %.1f fbar2 %.1f rdots\n", - ft->x, ft->x+10, ft->x+2, ft->x-8); - inend = endrep(inend, endstr, xend, ft->x, spacing->yend); - break; - case THICK_THIN: - fprintf(f, "%.1f fbar1\n", ft->x); - inend = endrep(inend, endstr, xend, ft->x, spacing->yend); - break; - case THIN_THICK: - fprintf(f, "%.1f fbar2\n", ft->x); - inend = endrep(inend, endstr, xend, ft->x, spacing->yend); - break; - case PART: - astring = ft->item; - break; - case TEMPO: - draw_tempo(ft->x, spacing->yinstruct, ft->item); - break; - case TIME: - afract = ft->item; - if (afract==NULL) { - if (v->line == midline) { - draw_meter(&v->meter, ft->x); - }; - } else { - setfract(&v->meter, afract->num, afract->denom); - if (v->line == midline) { - draw_meter(afract, ft->x); - }; - }; - break; - case KEY: - akey = ft->item; - if (v->line == midline) { - draw_keysig(v->keysig->map, akey->map, akey->mult, ft->x, v->clef); - }; - set_keysig(v->keysig, akey); - break; - case REST: - arest = ft->item; - drawrest(arest, ft->x, spacing); - if (v->tuple_count > 0) { - if (v->tuple_count == v->tuplenotes) { - v->tuple_xstart = ft->x - ft->xleft; - }; - if (v->tuple_count == 1) { - v->tuple_xend = ft->x + ft->xright; - drawhtuple(v->tuple_xstart, v->tuple_xend, v->tuplelabel, - v->tuple_height+4.0); - }; - v->tuple_count = v->tuple_count - 1; - }; - break; - case TUPLE: - atuple = ft->item; - if (atuple->beamed) { - v->beamed_tuple_pending = atuple->n; - v->tuplenotes = atuple->r; - v->tuple_count = 0; - } else { - v->tuple_height = atuple->height; - v->tuplenotes = atuple->r; - v->tuplelabel = atuple->label; - v->tuple_count = atuple->r; - }; - break; - case NOTE: - anote = ft->item; - if (thischord == NULL) { - if (ingrace) { - drawgracenote(anote, ft->x, ft, thischord); - } else { - drawnote(anote, ft->x, ft, thischord, &v->beamed_tuple_pending, - spacing); - if (v->tuple_count > 0) { - if (v->tuple_count == v->tuplenotes) { - v->tuple_xstart = ft->x - ft->xleft; - }; - if (v->tuple_count == 1) { - v->tuple_xend = ft->x + ft->xright; - drawhtuple(v->tuple_xstart, v->tuple_xend, v->tuplelabel, - v->tuple_height+4.0); - }; - v->tuple_count = v->tuple_count - 1; - }; - }; - } else { - xchord = ft->x; /* make sure all chord heads are aligned */ - if (ingrace) { - handlegracebeam(anote, ft); - drawgracehead(anote, ft->x, ft, nostem); - } else { - handlebeam(anote, ft); - drawhead(anote, ft->x, ft); - if (chordcount == 0) { - notetext(anote, &v->beamed_tuple_pending, ft->x, ft, spacing); - }; - chordcount = chordcount + 1; - }; - }; - break; - case CHORDNOTE: - anote = ft->item; - if (ingrace) { - drawgracehead(anote, xchord, ft, nostem); - } else { - drawhead(anote, xchord, ft); - }; - break; - case NONOTE: - break; - case OLDTIE: - break; - case TEXT: - break; - case SLUR_ON: - drawslurtie(ft->item); - break; - case SLUR_OFF: - close_slurtie(ft->item); - break; - case TIE: - drawslurtie(ft->item); - break; - case CLOSE_TIE: - close_slurtie(ft->item); - break; - case TITLE: - break; - case CHANNEL: - break; - case TRANSPOSE: - break; - case RTRANSPOSE: - break; - case GRACEON: - ingrace = 1; - break; - case GRACEOFF: - ingrace = 0; - break; - case SETGRACE: - break; - case SETC: - break; - case GCHORD: - break; - case GCHORDON: - break; - case GCHORDOFF: - break; - case VOICE: - break; - case CHORDON: - thischord = ft->item; - chordcount = 0; - drawchordtail(thischord, ft->next->x); - break; - case CHORDOFF: - thischord = NULL; - if (v->tuple_count > 0) { - if (v->tuple_count == v->tuplenotes) { - v->tuple_xstart = ft->x - ft->xleft; - }; - if (v->tuple_count == 1) { - v->tuple_xend = ft->x + ft->xright; - drawhtuple(v->tuple_xstart, v->tuple_xend, v->tuplelabel, - v->tuple_height+4.0); - }; - v->tuple_count = v->tuple_count - 1; - }; - break; - case SLUR_TIE: - break; - case TNOTE: - break; - case LT: - break; - case GT: - break; - case DYNAMIC: - psaction = ft->item; - if(psaction->color == 'r') redcolor = 1; /* [SS] 2013-11-04 */ - if(psaction->color == 'b') redcolor = 0; - break; - case LINENUM: - lineno = (int)(ft->item); - break; - case MUSICLINE: - v->line = midline; - break; - case PRINTLINE: - inend = endrep(inend, endstr, xend, scaledwidth, spacing->yend); - printedline = 1; - v->line = newline; - break; - case MUSICSTOP: - break; - case WORDLINE: - break; - case WORDSTOP: - break; - case INSTRUCTION: - break; - case NOBEAM: - break; - case CLEF: - theclef = ft->item; - if (theclef != NULL) { - v->clef->type = theclef->type; - v->clef->octave = theclef->octave; - }; - if (v->line == midline) { - printclef(v->clef, ft->x, ft->yup, ft->ydown); - }; - break; - case NEWPAGE: - event_warning("newpage in music line ignored"); - break; - case LEFT_TEXT: - event_warning("text in music line ignored"); - break; - case CENTRE_TEXT: - event_warning("centred text in music line ignored"); - break; - case VSKIP: - event_warning("%%vskip in music line ignored"); - break; - case SPLITVOICE: - break; - default: - printf("unknown type: %d\n", (int)ft->type); - break; - }; - ft = ft->next; - }; - v->place = ft; - return(1); -} - -static int finalsizeline(struct voice* v) -/* does final beaming and works out vertical height for */ -/* one line of specified voice */ -{ - struct feature* ft; - double height, descender; - double yend, yinstruct, ygchord, ywords; - struct vertspacing* avertspacing; - - /* skip over line number so we can check for end of tune */ - while ((v->place != NULL) && - ((v->place->type == LINENUM) || (v->place->type == NEWPAGE) || - (v->place->type == LEFT_TEXT) || (v->place->type == CENTRE_TEXT) || - (v->place->type == VSKIP))) { - v->place = v->place->next; - }; - if (v->place == NULL) { - return(0); - }; - ft = v->place; - beamline(ft); - measureline(ft, &height, &descender, ¥d, &yinstruct, &ygchord, &ywords); - /* newblock(height, descender); */ - /* find end of line */ - while ((ft != NULL) && (ft->type != PRINTLINE)) { - ft = ft->next; - }; - if ((ft != NULL) && (ft->type == PRINTLINE)) { - avertspacing = ft->item; - avertspacing->height = (float) height; - avertspacing->descender = (float) descender; - avertspacing->yend = (float) yend; - avertspacing->yinstruct = (float) yinstruct; - avertspacing->ygchord = (float) ygchord; - avertspacing->ywords = (float) ywords; - ft = ft->next; - }; - v->place = ft; - return(1); -} - -static void finalsizetune(t) -struct tune* t; -/* calculate beaming and calculate height of each music line */ -{ - struct voice* thisvoice; - int doneline; - - thisvoice = firstitem(&t->voices); - while (thisvoice != NULL) { - doneline = 1; - while (doneline==1) { - doneline = finalsizeline(thisvoice); - }; - thisvoice = nextitem(&t->voices); - }; -} - -static int getlineheight(struct voice* v, double* height) -/* calculate vertical space for one line of music from specified voice */ -{ - struct vertspacing* spacing; - - /* go over between line stuff */ - while ((v->place != NULL) && - ((v->place->type == LINENUM) || (v->place->type == NEWPAGE) || - (v->place->type == LEFT_TEXT) || (v->place->type == CENTRE_TEXT) || - (v->place->type == VSKIP))) { - if (v->place->type == LINENUM) { - lineno = (int)(v->place->item); - }; - if (v->place->type == LEFT_TEXT) { - *height = *height + textfont.pointsize + textfont.space; - }; - if (v->place->type == CENTRE_TEXT) { - *height = *height + textfont.pointsize + textfont.space; - }; - if (v->place->type == VSKIP) { - *height = *height + (double)((int)v->place->item); - }; - v->place = v->place->next; - }; - if (v->place == NULL) { - return(0); - }; - /* skip over rest of line */ - while ((v->place != NULL) && (v->place->type != PRINTLINE)) { - v->place = v->place->next; - }; - if (v->place != NULL) { - spacing = v->place->item; - *height = *height + spacing->height + spacing->descender; - v->place = v->place->next; - }; - return(1); -} - -static double tuneheight(t) -struct tune* t; -/* measures the vertical space needed by the tune */ -{ - char* atitle; - char* wordline; - struct voice* thisvoice; - int doneline; - double height; - char *notesfield; /* from N: field */ - - height = 0.0; - resettune(t); - atitle = firstitem(&t->title); - while (atitle != NULL) { - height = height + titlefont.pointsize + titlefont.space; - atitle = nextitem(&t->title); - }; - if ((titleleft == 1) && (t->tempo != NULL)) { - height = height + titlefont.pointsize + 2*titlefont.space; - }; - if (t->composer != NULL) { - height = height + composerfont.pointsize + composerfont.space; - }; - if (t->origin != NULL) { - height = height + composerfont.pointsize + composerfont.space; - }; - if (t->parts != NULL) { - height = height + composerfont.pointsize + composerfont.space; - }; - notesfield = firstitem(&t->notes); - while (notesfield != NULL) { - height = height + composerfont.pointsize + composerfont.space; - notesfield = nextitem(&t->notes); - }; - thisvoice = firstitem(&t->voices); - if (separate_voices) { - thisvoice = firstitem(&t->voices); - while (thisvoice != NULL) { - doneline = 1; - while (doneline==1) { - doneline = getlineheight(thisvoice, &height); - }; - thisvoice = nextitem(&t->voices); - if (thisvoice != NULL) { - height = height + 2.0 * staffsep; - }; - }; - } else { - doneline = 1; - while(doneline) { - thisvoice = firstitem(&t->voices); - doneline = 0; - while (thisvoice != NULL) { - doneline = (getlineheight(thisvoice, &height) || doneline); - thisvoice = nextitem(&t->voices); - }; - }; - }; - wordline = firstitem(&t->words); - while (wordline != NULL) { - height = height + wordsfont.pointsize + wordsfont.space; - wordline = nextitem(&t->words); - }; - return(height); -} - -void printtune(struct tune* t) -/* draws the PostScript for an entire abc tune */ -{ - int i; - char *atitle; - char *notesfield; /* from N: field */ - char *wordline; - int notesdone; - int titleno; - struct voice* thisvoice; - int doneline; - double firstvline, lastvline; - struct voice* firstvoice; - char xtitle[200]; - struct bbox boundingbox; - enum placetype titleplace; - - redcolor = 0; /* [SS] 2013-11-04 */ - resettune(t); - sizetune(t); - resettune(t); - if (separate_voices) { - monospace(t); - } else { - spacevoices(t); - }; - resettune(t); - finalsizetune(t); - if (debugging) { - showtune(t); - }; - if (eps_out) { - if (landscape) { - boundingbox.llx = ymargin; - boundingbox.lly = xmargin; - boundingbox.urx = (int) (ymargin + tuneheight(t) * scale); - boundingbox.ury = xmargin + pagewidth; - } else { - boundingbox.llx = xmargin; - boundingbox.lly = (int) (ymargin + pagelen - tuneheight(t) * scale); - boundingbox.urx = xmargin + pagewidth; - boundingbox.ury = ymargin + pagelen; - }; - sprintf(outputname, "%s%d.eps", outputroot, t->no); - open_output_file(outputname, &boundingbox); - } else { - make_open(); - if ((totlen > 0.0) && - (totlen+descend+tuneheight(t) > scaledlen-(double)(pagenumbering))) { - newpage(); - }; - }; - resettune(t); - notesdone = 0; - if (titleleft) { - titleplace = left; - } else { - titleplace = centre; - }; - atitle = firstitem(&t->title); - titleno = 1; - while (atitle != NULL) { - if (titleno == 1) { - if (titlecaps) { - for (i=0; i< (int) strlen(atitle); i++) { - if (islower(atitle[i])) { - atitle[i] = atitle[i] + 'A' - 'a'; - }; - }; - }; - if ((print_xref) && (strlen(atitle) < 180)) { - sprintf(xtitle, "%d. %s", t->no, atitle); - printtext(titleplace, xtitle, &titlefont); - } else { - printtext(titleplace, atitle, &titlefont); - }; - } else { - printtext(titleplace, atitle, &subtitlefont); - }; - titleno = titleno + 1; - atitle = nextitem(&t->title); - }; - if (t->tempo != NULL) { - if (titleleft) { - newblock((double)(titlefont.pointsize + titlefont.space*2), 0.0); - }; - draw_tempo(10.0, 0.0, t->tempo); - }; - if (t->composer != NULL) { - printtext(right, t->composer, &composerfont); - }; - if (t->origin != NULL) { - printtext(right, t->origin, &composerfont); - }; - if (t->parts != NULL) { - printtext(right, t->parts, &composerfont); - }; - notesfield = firstitem(&t->notes); - while (notesfield != NULL) { - printtext(left, notesfield, &composerfont); - notesfield = nextitem(&t->notes); - }; - donemeter = 0; - /* musicsetup(); */ - thisvoice = firstitem(&t->voices); - while (thisvoice != NULL) { - thisvoice->clef->type = t->clef.type; - thisvoice->clef->octave = t->clef.octave; - setfract(&thisvoice->meter, t->meter.num, t->meter.denom); - thisvoice->place = thisvoice->first; - thisvoice = nextitem(&t->voices); - }; - if (separate_voices) { - thisvoice = firstitem(&t->voices); - while (thisvoice != NULL) { - doneline = 1; - while (doneline==1) { - doneline = printvoiceline(thisvoice); - }; - thisvoice = nextitem(&t->voices); - if (thisvoice != NULL) { - voicedivider(); - }; - }; - } else { - doneline = 1; - while(doneline) { - thisvoice = firstitem(&t->voices); - firstvoice = thisvoice; - doneline = 0; - while (thisvoice != NULL) { - doneline = (printvoiceline(thisvoice) || doneline); - if (thisvoice == firstvoice) { - firstvline = totlen; - } else { - lastvline = totlen; - if (lastvline > firstvline) { - fprintf(f, "0 24 M 0 %.1f L\n", - lastvline - firstvline); - }; - firstvline = totlen; - }; - thisvoice = nextitem(&t->voices); - }; - }; - }; - /* musicrestore(); */ - wordline = firstitem(&t->words); - while (wordline != NULL) { - printtext(left, wordline, &wordsfont); - wordline = nextitem(&t->words); - }; - if (eps_out) { - close_output_file(); - }; -} - +/* + * 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 + * + */ + +/* drawtune.c */ +/* This file contains routines for generating final PostScript Output */ +/* There are 2 stages to this process. First the symbol positions are */ +/* calculated, then the PostScript symbols are generated. */ + +#ifdef _MSC_VER +#define ANSILIBS 1 +#endif + +#include +#ifdef ANSILIBS +#include +#include +#include +#endif + +#include "abc.h" +#include "structs.h" +#include "sizes.h" +#include "drawtune.h" + +/* external functions and variables */ +extern struct tune thetune; +extern int debugging; +extern int pagenumbering; +extern int barnums, nnbars; +extern char outputname[256]; +extern char outputroot[256]; +extern int make_open(); +extern void printlib(); +extern int count_dots(int *base, int *base_exp, int n, int m); + +extern void monospace(struct tune* t); +extern void spacevoices(struct tune* t); + +/* provided by debug.c */ +extern void showtune(struct tune *t); + +struct key* newkey(char* name, int sharps, char accidental[], int mult[]); +struct aclef* newclef(enum cleftype t, int octave); +char* addstring(char *s); +extern int lineno; +extern int separate_voices; +extern int print_xref; +extern int landscape; + +static void pagebottom(); +static void setfont(int size, int num); + +FILE* f = NULL; +struct feature* beamset[64]; +struct feature* gracebeamset[32]; +int beamctr, gracebeamctr;; +int rootstem; +int fontsize, fontnum; +int donemeter; +int inchord; +int chordcount; +static int ingrace; /* [SDG] 2020-06-03 */ +struct feature* chordhead; + +double scale, descend, totlen, oldplace; +int pagecount = 1; +int pagelen, pagewidth, xmargin, ymargin; +double scaledlen, scaledwidth; +int staffsep; +int eps_out; +int titleleft = 0; +int titlecaps = 0; +int gchords_above = 1; +int redcolor; /* [SS] 2013-11-04*/ + +enum placetype {left, right, centre}; +struct font textfont; +struct font titlefont; +struct font subtitlefont; +struct font wordsfont; +struct font composerfont; +struct font vocalfont; +struct font gchordfont; +struct font partsfont; + +/* arrays giving character widths for characters 32-255 */ + +/* font for lyrics : 13 point Times-Bold */ +double timesbold_width[224] = { +3.249, 4.328, 7.214, 6.499, 6.499, 12.999, 10.828, 4.328, +4.328, 4.328, 6.499, 7.409, 3.249, 7.409, 3.249, 3.613, +6.499, 6.499, 6.499, 6.499, 6.499, 6.499, 6.499, 6.499, +6.499, 6.499, 4.328, 4.328, 7.409, 7.409, 7.409, 6.499, +12.089, 9.385, 8.67, 9.385, 9.385, 8.67, 7.942, 10.113, +10.113, 5.056, 6.499, 10.113, 8.67, 12.271, 9.385, 10.113, +7.942, 10.113, 9.385, 7.227, 8.67, 9.385, 9.385, 12.999, +9.385, 9.385, 8.67, 4.328, 3.613, 4.328, 7.552, 6.499, +4.328, 6.499, 7.227, 5.771, 7.227, 5.771, 4.328, 6.499, +7.227, 3.613, 4.328, 7.227, 3.613, 10.828, 7.227, 6.499, +7.227, 7.227, 5.771, 5.056, 4.328, 7.227, 6.499, 9.385, +6.499, 6.499, 5.771, 5.121, 2.859, 5.121, 6.759, 3.249, +3.249, 3.249, 3.249, 3.249, 3.249, 3.249, 3.249, 3.249, +3.249, 3.249, 3.249, 3.249, 3.249, 3.249, 3.249, 3.249, +3.613, 4.328, 4.328, 4.328, 4.328, 4.328, 4.328, 4.328, +4.328, 3.249, 4.328, 4.328, 3.249, 4.328, 4.328, 4.328, +3.249, 4.328, 6.499, 6.499, 6.499, 6.499, 2.859, 6.499, +4.328, 9.71, 3.899, 6.499, 7.409, 4.328, 9.71, 4.328, +5.199, 7.409, 3.899, 3.899, 4.328, 7.227, 7.019, 3.249, +4.328, 3.899, 4.289, 6.499, 9.749, 9.749, 9.749, 6.499, +9.385, 9.385, 9.385, 9.385, 9.385, 9.385, 12.999, 9.385, +8.67, 8.67, 8.67, 8.67, 5.056, 5.056, 5.056, 5.056, +9.385, 9.385, 10.113, 10.113, 10.113, 10.113, 10.113, 7.409, +10.113, 9.385, 9.385, 9.385, 9.385, 9.385, 7.942, 7.227, +6.499, 6.499, 6.499, 6.499, 6.499, 6.499, 9.385, 5.771, +5.771, 5.771, 5.771, 5.771, 3.613, 3.613, 3.613, 3.613, +6.499, 7.227, 6.499, 6.499, 6.499, 6.499, 6.499, 7.409, +6.499, 7.227, 7.227, 7.227, 7.227, 6.499, 7.227, 6.499, +}; + +/* font for chord names : 12 point Helvetica */ +double helvetica_width[224] = { +3.335, 3.335, 4.259, 6.671, 6.671, 10.667, 8.003, 2.651, +3.995, 3.995, 4.667, 7.007, 3.335, 3.995, 3.335, 3.335, +6.671, 6.671, 6.671, 6.671, 6.671, 6.671, 6.671, 6.671, +6.671, 6.671, 3.335, 3.335, 7.007, 7.007, 7.007, 6.671, +12.179, 8.003, 8.003, 8.663, 8.663, 8.003, 7.331, 9.335, +8.663, 3.335, 5.999, 8.003, 6.671, 9.995, 8.663, 9.335, +8.003, 9.335, 8.663, 8.003, 7.331, 8.663, 8.003, 11.327, +8.003, 8.003, 7.331, 3.335, 3.335, 3.335, 5.627, 6.671, +2.663, 6.671, 6.671, 5.999, 6.671, 6.671, 3.335, 6.671, +6.671, 2.663, 2.663, 5.999, 2.663, 9.995, 6.671, 6.671, +6.671, 6.671, 3.995, 5.999, 3.335, 6.671, 5.999, 8.663, +5.999, 5.999, 5.999, 4.007, 3.119, 4.007, 7.007, 3.335, +3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, +3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, +3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, +3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, +3.335, 3.995, 6.671, 6.671, 2.003, 6.671, 6.671, 6.671, +6.671, 2.291, 3.995, 6.671, 3.995, 3.995, 5.999, 5.999, +3.335, 6.671, 6.671, 6.671, 3.335, 3.335, 6.443, 4.199, +2.663, 3.995, 3.995, 6.671, 11.999, 11.999, 3.335, 7.331, +3.335, 3.995, 3.995, 3.995, 3.995, 3.995, 3.995, 3.995, +3.995, 3.335, 3.995, 3.995, 3.335, 3.995, 3.995, 3.995, +11.999, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, +3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, +3.335, 11.999, 3.335, 4.439, 3.335, 3.335, 3.335, 3.335, +6.671, 9.335, 11.999, 4.379, 3.335, 3.335, 3.335, 3.335, +3.335, 10.667, 3.335, 3.335, 3.335, 3.335, 3.335, 3.335, +2.663, 7.331, 11.327, 7.331, 3.335, 3.335, 3.335, 3.335, +}; + +static int ISOencoding(char ch1, char ch2) +/* converts the 2 characters following '\' into an ISO Latin 1 Font */ +/* character code in the range 128-255 */ +{ + int code; + + code = 0; + switch (ch1) { + case '`': + switch(ch2) { + case 'A': code = 0300; break; + case 'E': code = 0310; break; + case 'I': code = 0314; break; + case 'O': code = 0322; break; + case 'U': code = 0331; break; + case 'a': code = 0340; break; + case 'e': code = 0350; break; + case 'i': code = 0354; break; + case 'o': code = 0362; break; + case 'u': code = 0371; break; + default: + break; + }; + break; + case '\'': + switch(ch2) { + case 'A': code = 0301; break; + case 'E': code = 0311; break; + case 'I': code = 0315; break; + case 'U': code = 0332; break; + case 'Y': code = 0335; break; + case 'a': code = 0341; break; + case 'e': code = 0351; break; + case 'i': code = 0355; break; + case 'o': code = 0363; break; + case 'u': code = 0372; break; + case 'y': code = 0375; break; + default: + break; + }; + break; + case '^': + switch(ch2) { + case 'A': code = 0302; break; + case 'E': code = 0312; break; + case 'I': code = 0316; break; + case 'O': code = 0324; break; + case 'U': code = 0333; break; + case 'a': code = 0342; break; + case 'e': code = 0352; break; + case 'i': code = 0356; break; + case 'o': code = 0364; break; + case 'u': code = 0373; break; + default: + break; + }; + break; + case '"': + switch(ch2) { + case 'A': code = 0304; break; + case 'E': code = 0313; break; + case 'I': code = 0317; break; + case 'O': code = 0326; break; + case 'U': code = 0334; break; + case 'a': code = 0344; break; + case 'e': code = 0353; break; + case 'i': code = 0357; break; + case 'o': code = 0366; break; + case 'u': code = 0374; break; + case 'y': code = 0377; break; + default: + break; + }; + break; + case '~': + switch(ch2) { + case 'A': code = 0303; break; + case 'N': code = 0321; break; + case 'O': code = 0325; break; + case 'a': code = 0343; break; + case 'n': code = 0361; break; + case 'o': code = 0365; break; + default: + break; + }; + break; + case 'c': + switch(ch2) { + case 'C': code = 0307; break; + case 'c': code = 0347; break; + default: + break; + }; + break; + case 'o': + switch(ch2) { + case 'A': code = 0305; break; + case 'a': code = 0345; break; + default: + break; + }; + break; + case 'A': + if (ch2=='A') { + code = 0305; + }; + if (ch2=='E') { + code = 0305; + }; + break; + case 'a': + if (ch2=='a') { + code = 0345; + }; + if (ch2=='e') { + code = 0346; + }; + break; + case 's': + if (ch2=='s') { + code = 0337; + }; + break; + default: + break; + }; + return(code); +} + +static int getISO(char s[], int j, int* code) +/* convert input into 8-bit character code */ +/* s[] is input string, j is place in string */ +/* returns new place in string and writes 8-bit value to code */ +{ + int i; + int count; + + i =j; + if (s[i]=='\\') { + /* look for 2 character code */ + *code = ISOencoding(s[i+1], s[i+2]); + if (*code != 0) { + i = i+3; + } else { + /* look for octal code */ + i = i+1; + *code = 0; + count = 0; + while ((s[i]>='0')&&(s[i]<='7')&&(count<3)) { + *code = ((*code)*8+(int)s[i]-'0')%256; + count = count+1; + i = i+1; + }; + if (*code == 0) { + *code = '\\'; + }; + }; + } else { + *code = (int)s[i] & 0xFF; + i = i + 1; + }; + return(i); +} + +static void ISOdecode(char s[], char out[]) +/* convert coded characters to straight 8-bit ascii */ +{ + int i, j, len; + int code; + + len = strlen(s); + i = 0; + j = 0; + while (i 31) && (ch < 128)) { + fprintf(f, "%c", s[i]); + } else { + fprintf(f, "\\%03o", 0xFF & (int)s[i]); + }; + i = i+1; + break; + }; + }; +} + +static double stringwidth(char* str, double ptsize, int fontno) +/* calculate width of string */ +{ + int i, len, ch; + double width; + int code; + double baseptsize; + double* charwidth; + + if (fontno == 3) { + charwidth = &helvetica_width[0]; + baseptsize = 12.0; + } else { + charwidth = ×bold_width[0]; + baseptsize = 13.0; + }; + width = 0.0; + len = strlen(str); + i = 0; + while (i < len) { + i = getISO(str, i, &code); + ch = code - 32; + if ((ch >= 0) && (ch < 224)) { + width = width + charwidth[ch]; + } else { + width = width + charwidth[0]; /* approximate with space */ + }; + }; + return(width*ptsize/baseptsize); +} + +void setscaling(char* s) +/* scaling value from string following -s in arglist */ +{ + double f; + + f = -1.0; + sscanf(s, "%lf", &f); + if (f < 0.001) { + f = TUNE_SCALING; + }; + scale = f; +} + +void setmargins(s) +char* s; +/* set margin values from string following -M in arglist */ +{ + int i, j; + + xmargin = XMARGIN; + ymargin = YMARGIN; + if (s != NULL) { + i = -1; + j = -1; + sscanf(s, "%dx%d", &i, &j); + if ((i != -1) && (j != -1)) { + xmargin = i; + ymargin = j; + }; + }; +} + +void setpagesize(s) +char* s; +/* set page size from string following -P in arglist */ +{ + int i, j; + + pagelen = A4_PAGELEN - 2 * ymargin; + pagewidth = A4_PAGEWIDTH - 2 * xmargin; + if (s != NULL) { + i = 0; + j = 0; + sscanf(s, "%dx%d", &i, &j); + if (i == 0) { + pagelen = A4_PAGELEN - 2 * ymargin; + pagewidth = A4_PAGEWIDTH - 2 * xmargin; + }; + if (i == 1) { + pagelen = US_LETTER_PAGELEN - 2 * ymargin; + pagewidth = US_LETTER_PAGEWIDTH - 2 * xmargin; + }; + if ((i > 100) && (i > 2*xmargin) && (j > 100) && (j > 2*ymargin)) { + pagewidth = i - 2 * xmargin; + pagelen = j - 2 * ymargin; + }; + }; + if (landscape) { + i = xmargin; + xmargin = ymargin; + ymargin = i; + i = pagelen; + pagelen = pagewidth; + pagewidth = i; + }; + scaledlen = ((double)(pagelen))/scale; + scaledwidth = ((double)(pagewidth))/scale; +} + +static void set_space(afont, s) +/* set vertical space to appear above a line in the given font */ +/* s is a string specifying space in points (default), centimetres */ +/* or inches */ +struct font* afont; +char* s; +{ + int count; + double x; + char units[40]; + + count = sscanf(s, "%lf%s", &x, units); + if (count > 0) { + if ((count >= 2) && (strncmp(units, "cm", 2) == 0)) { + x = x*28.3; + }; + if ((count >= 2) && (strncmp(units, "in", 2) == 0)) { + x = x*72.0 ; + }; + afont->space = (int)x; + }; +} + +static void init_font(afont, ptsize, spce, defnum, specialnum) +/* initialize font structure with given values */ +struct font* afont; +int ptsize, spce, defnum, specialnum; +{ + afont->pointsize = ptsize; + afont->space = spce; + afont->default_num = defnum; + afont->special_num = specialnum; + afont->name = NULL; + afont->defined = 0; +} + +static void startpage() +/* Encapsulated PostScript for a page header */ +{ + fprintf(f, "%%%%Page: %d %d\n", pagecount, pagecount); + fprintf(f, "%%%%BeginPageSetup\n"); + fprintf(f, "gsave\n"); + if (landscape) { + fprintf(f, "90 rotate %d %d T\n", xmargin, -ymargin); + } else { + fprintf(f, "%d %d T\n", xmargin, ymargin+pagelen); + }; + fprintf(f, "0.8 setlinewidth 0 setlinecap\n"); + fprintf(f, "%.3f %.3f scale\n", scale, scale); + fprintf(f, "%%%%EndPageSetup\n\n"); + fontnum = 0; + fontsize = 0; + totlen = 0.0; + oldplace = 0.0; + descend = 0.0; +} + +static void closepage() +/* Encapsulated PostScript for page end */ +{ + + if (pagenumbering) { + setfont(12, 3); + pagebottom(); + fprintf(f, "(%d) %.1f 0 M cshow\n", pagecount, scaledwidth/2.0); + }; + fprintf(f, "%%%%PageTrailer\n"); + fprintf(f, "grestore\n"); + fprintf(f, "showpage\n\n"); + pagecount = pagecount + 1; +} + +void newpage() +/* create PostScript for a new page of paper */ +/* everything will now be printed on this fresh sheet */ +/* ignore the command if the sheet is completely blank */ +{ + if (totlen > 0.0) { + closepage(); + startpage(); + }; +} + +static void pagebottom() +/* move to the bottom of the page */ +{ + fprintf(f, "0 %.1f T\n", -(scaledlen - totlen)); + totlen = scaledlen; + descend = 0.0; +} + +static void newblock(double height, double descender) +/* The page is modelled as a series of vertical bands */ +/* This routine finds room for a fresh band below the current one */ +/* It also does a translation to set a new y=0 line */ +/* Subsequent calls may draw up to height units above or */ +/* descender units below the line y=0 */ +{ + if (totlen+(descend+height+descender) > scaledlen - (double)(pagenumbering*12)) { + newpage(); + }; + fprintf(f, "0 %.1f T\n", - descend - height); + totlen = totlen + (descend + height); + descend = descender; +} + +static void staveline() +/* draw 5 lines of a stave */ +{ + fprintf(f, "%.1f staff\n", scaledwidth); +} + +static void printclef(struct aclef* t, double x, double yup, double ydown) +/* draw a clef of the specified type */ +{ + switch (t->type) { + case treble: + fprintf(f, "%.1f tclef\n", x); + break; + case bass: + fprintf(f, "%.1f bclef\n", x); + break; + case alto: + fprintf(f, "%.1f cclef\n", x); + break; + case baritone: + fprintf(f, "0 %d T %.1f cclef 0 %d T\n", 4*TONE_HT, x, -4*TONE_HT); + break; + case tenor: + fprintf(f, "0 %d T %.1f cclef 0 %d T\n", 2*TONE_HT, x, -2*TONE_HT); + break; + case mezzo: + fprintf(f, "0 %d T %.1f cclef 0 %d T\n", -2*TONE_HT, x, 2*TONE_HT); + break; + case soprano: + fprintf(f, "0 %d T %.1f cclef 0 %d T\n", -4*TONE_HT, x, 4*TONE_HT); + break; + default: + break; + }; + if (t->octave > 0) { + fprintf(f, "%.1f %.1f (%d) bnum\n", x, yup - CLEFNUM_HT + 3, t->octave); + }; + if (t->octave < 0) { + fprintf(f, "%.1f %.1f (%d) bnum\n", x, -ydown, - t->octave); + }; +} + +void set_keysig(struct key* k, struct key* newval) +/* copy across key signature */ +{ + int i; + + for (i=0; i<7; i++) { + k->map[i] = newval->map[i]; + k->mult[i] = newval->mult[i]; + }; + k->sharps = newval->sharps; +} + +static double size_keysig(char oldmap[], char newmap[]) +/* compute width taken up by drawing the key signature */ +{ + int i, n; + + n = 0; + for (i=0; i<7; i++) { + if (((newmap[i] == '=')&&(oldmap[i] != '=')) || + (newmap[i] == '^') || (newmap[i] == '_')) { + n = n + 1; + }; + }; + return((double)n * 5.0); +} + +static double size_timesig(struct fract* meter) +/* compute width of the time signature */ +{ + double len1, len2; + char temp[20]; + + sprintf(temp, "%d", meter->num); + len1 = stringwidth(temp, 16.0, 2); + sprintf(temp, "%d", meter->denom); + len2 = stringwidth(temp, 16.0, 2); + if (len1 > len2) { + return(len1); + } else { + return(len2); + }; +} + +static void draw_keysig(char oldmap[], char newmap[], int newmult[], + double x, struct aclef* clef) +/* draw key specified key signature at position x on line */ +/* arrays oldmap[], newmap[], newmult[] are indexed with a=0 to g=7 */ +/* sharp_pos[] and flat_pos[] give order of notes */ +/* e.g. sharp_pos[0] = 8 means 1st sharp drawn is conventionally f */ +/* which is in position 8 on the stave (bottom line is E = 0) */ +{ + double xpos; + static int sharp_pos[7] = { 8, 5, 9, 6, 3, 7, 4 }; + static int flat_pos[7] = { 4, 7, 3, 6, 2, 5, 1}; + int i; + int offset, pos; + int note; + + switch (clef->type) { + case treble: + offset = 0; + break; + case soprano: + offset = 2; + break; + case mezzo: + offset = 4; + break; + case alto: + offset = 6; + break; + case tenor: + offset = 8; + break; + case baritone: + offset = 10; + break; + case bass: + offset = 12; + break; + case noclef: + break; + }; + xpos = x; + /* draw naturals to cancel out old accidentals */ + for (i=0; i<7; i++) { + note = (sharp_pos[i] + 4) % 7; + if ((newmap[note] == '=')&&(oldmap[note] != '=')) { + pos = (sharp_pos[i] + offset - 3) % 7 + 3; + fprintf(f, " %.1f %d nt0", xpos, pos*TONE_HT); + xpos = xpos + 5; + }; + }; + /* draw sharps */ + for (i=0; i<7; i++) { + note = (sharp_pos[i] + 4) % 7; + if (newmap[note] == '^') { + pos = (sharp_pos[i] + offset - 3) % 7 + 3; + if (newmult[note] == 2) { + fprintf(f, " %.1f %d dsh0", xpos, pos*TONE_HT); + } else { + fprintf(f, " %.1f %d sh0", xpos, pos*TONE_HT); + }; + xpos = xpos + 5; + }; + }; + /* draw flats */ + for (i=0; i<7; i++) { + note = (flat_pos[i] + 4) % 7; + if (newmap[note] == '_') { + pos = (flat_pos[i] + offset - 1)%7 + 1; + if (newmult[note] == 2) { + fprintf(f, " %.1f %d dft0", xpos, pos*TONE_HT); + } else { + fprintf(f, " %.1f %d ft0", xpos, pos*TONE_HT); + }; + xpos = xpos + 5; + }; + }; + fprintf(f, "\n"); +} + +static void draw_meter(struct fract* meter, double x) +/* draw meter (time signature) at specified x value */ +{ + fprintf(f, "%.1f (%d) (%d) tsig\n", x, meter->num, meter->denom); +} + +static double maxstrwidth(struct llist* strings, double ptsize, int fontno) +/* return the width of longest string in the list */ +{ + double width, max; + char* str; + + max = 0.0; + str = firstitem(strings); + while (str != NULL) { + width = stringwidth(str, ptsize, fontno); + if (width > max) { + max = width; + }; + str = nextitem(strings); + }; + return(max); +} + +static void sizerest(struct rest* r, struct feature* ft) +/* compute width of rest element */ +{ + double width; + + if (r->multibar > 0) { + ft->xleft = 0.0; + ft->xright = 40.0; + } else { + switch (r->base_exp) { + case 1: + case 0: + ft->xleft = 2.5; + ft->xright = 2.5; + break; + case -1: + ft->xleft = 2.5; + ft->xright = 2.5; + break; + case -2: + ft->xleft = 3.0; + ft->xright = 4.0; + break; + case -3: + ft->xleft = 4.0; + ft->xright = 3.0; + break; + case -4: + ft->xleft = 5.0; + ft->xright = 3.0; + break; + case -5: + ft->xleft = 6.0; + ft->xright = 5.0; + break; + case -6: + default: + ft->xleft = 7.0; + ft->xright = 5.0; + break; + }; + if (r->dots > 0) { + ft->xright = ft->xright + (r->dots* (float) DOT_SPACE); + }; + }; + if (r->gchords != NULL) { + width = maxstrwidth(r->gchords, (double)(gchordfont.pointsize), + gchordfont.default_num); + if (width > ft->xleft + ft->xright) { + ft->xright = (float) width - ft->xleft; + }; + }; +} + +static int acc_upsize(char ch) +/* returns height above note of accent */ +{ + int size; + + switch (ch) { + case '=': + size = NAT_UP; + break; + case '^': + size = SH_UP; + break; + case '_': + size = FLT_UP; + break; + default: + size = 0; + break; + }; + return(size); +} + +static int acc_downsize(char ch) +/* returns depth below note of accent */ +{ + int size; + + switch (ch) { + case '=': + size = NAT_DOWN; + break; + case '^': + size = SH_DOWN; + break; + case '_': + size = FLT_DOWN; + break; + default: + size = 0; + break; + }; + return(size); +} + +static void setstemlen(struct note* n, int ingrace) +/* work out how long stem needs to be for an isolated note */ +/* if the note is in a beamed set, the length may be altered later */ +{ + if (n->base_exp >= 0) { + n->stemlength = 0.0; + } else { + if (ingrace) { + if (n->beaming != single) { + n->stemlength = GRACE_STEMLEN; /* default stem length */ + /* value will be recalculated later by setbeams() */ + } else { + switch(n->base_exp) { + case -6: + n->stemlength = (float) GRACE_STEMLEN + 2* (float) TAIL_SEP* (float) 0.7; + break; + case -5: + n->stemlength = (float) GRACE_STEMLEN + (float) TAIL_SEP* (float) 0.7; + break; + default: + n->stemlength = (float) GRACE_STEMLEN; /* default stem length */ + break; + }; + }; + } else { + if (n->beaming != single) { + n->stemlength = STEMLEN; /* default stem length */ + /* value will be recalculated later by setbeams() */ + } else { + switch(n->base_exp) { + case -6: + n->stemlength = (float) STEMLEN + 2* (float) TAIL_SEP; + break; + case -5: + n->stemlength = (float) STEMLEN + (float) TAIL_SEP; + break; + default: + n->stemlength = (float) STEMLEN; /* default stem length */ + break; + }; + }; + }; + }; +} + +static void sizenote(struct note* n, struct feature* f, int ingrace) +/* compute width and height of note element */ +{ + double width; + char* decorators; + int i; + + f->ydown = (float) TONE_HT*(n->y - 2); + f->yup = (float) TONE_HT*(n->y + 2); + if (ingrace) { + f->xleft = (float) GRACE_HALF_HEAD; + f->xright = (float) GRACE_HALF_HEAD; + } else { + if (n->base_exp >= 1) { + f->xleft = HALF_BREVE; + f->xright = HALF_BREVE; + } else { + f->xleft = HALF_HEAD; + f->xright = HALF_HEAD; + }; + if (n->fliphead) { + if (n->stemup) { + f->xright = (float) f->xright + (float) HALF_HEAD * 2; + } else { + f->xleft = f->xleft + (float) HALF_HEAD * 2; + }; + }; + }; + if (n->dots > 0) { + f->xright = f->xright + (n->dots* (float) DOT_SPACE); + }; + if ((n->stemup) && (n->base_exp <= -3) && (n->beaming==single)) { + if (f->xright < (HALF_HEAD + TAILWIDTH)) { + f->xright = HALF_HEAD + TAILWIDTH; + }; + }; + if (n->accidental != ' ') { + f->xleft = (float) HALF_HEAD + (float) ACC_OFFSET + (n->acc_offset-1) * (float) ACC_OFFSET2; + if (n->stemup) { + f->ydown = (float) TONE_HT*(n->y) - (float)(acc_downsize(n->accidental)); + } else { + f->yup = (float) ((float) TONE_HT*(n->y) + (double)(acc_upsize(n->accidental))); + }; + }; + if (n->stemlength > 0.0) { + if (n->stemup) { + f->yup = TONE_HT*(n->y) + n->stemlength; + } else { + f->ydown = TONE_HT*(n->y) - n->stemlength; + }; + }; + if (n->accents != NULL) { + decorators = n->accents; + for (i=0; i<(int) strlen(decorators); i++) { + switch(decorators[i]) { + case 'H': + case '~': + case 'u': + case 'v': + case 'T': + f->yup = f->yup + BIG_DEC_HT; + break; + case 'R': + if (n->stemup) { + f->ydown = f->ydown + BIG_DEC_HT; + } else { + f->yup = f->yup + BIG_DEC_HT; + }; + break; + case 'M': + case '.': + if (n->stemup) { + f->ydown = f->ydown + SMALL_DEC_HT; + } else { + f->yup = f->yup + SMALL_DEC_HT; + }; + break; + default: + /* this should never happen */ + event_error("Un-recognized accent"); + break; + }; + }; + }; + if (n->syllables != NULL) { + width = maxstrwidth(n->syllables, (double)(vocalfont.pointsize), + vocalfont.default_num); + if (width > f->xleft + f->xright) { + f->xright = (float) width - f->xleft; + }; + }; + if (n->gchords != NULL) { + width = maxstrwidth(n->gchords, (double)(gchordfont.pointsize), + gchordfont.default_num); + if (width > f->xleft + f->xright) { + f->xright = (float) width - f->xleft; + }; + }; +} + +static void spacechord(struct feature* chordplace) +/* prevents collision of note heads and accidentals in a chord */ +/* sets fliphead and acc_offset for all notes in chord */ +{ + struct feature* place; + struct note* anote; + int thisy, lasty, lastflip; + int stemdir =0; /* [SDG] 2020-06-03 */ + int doneflip; + int ygap[10]; + int accplace; + int i; + + /* first flip heads */ + place = chordplace; + lasty = 200; + lastflip = 1; + doneflip = 0; + while ((place != NULL) && (place->type != CHORDOFF)) { + if ((place->type == CHORDNOTE) || (place->type == NOTE)) { + anote = place->item; + thisy = anote->y; + if ((lasty - thisy <= 1) && (lastflip != 1)) { + anote->fliphead = 1; + stemdir = anote->stemup; + lastflip = 1; + doneflip = 1; + } else { + lastflip = 0; + }; + lasty = thisy; + }; + place = place->next; + }; + /* now deal with accidentals */ + for (i=0; i<10; i++) { + ygap[i] = 200; + }; + if ((doneflip) && (stemdir == 0)) { + /* block off space where note is */ + ygap[0] = -200; + }; + place = chordplace; + while ((place != NULL) && (place->type != CHORDOFF)) { + if ((place->type == CHORDNOTE) || (place->type == NOTE)) { + anote = place->item; + thisy = anote->y; + if (anote->accidental != ' ') { + /* find space for this accidental */ + accplace = 0; + while ((accplace <10) && (ygap[accplace] < thisy)) { + accplace = accplace + 1; + }; + anote->acc_offset = accplace + 1; + ygap[accplace] = thisy - 6; + }; + }; + place = place->next; + }; +} + +static void sizechord(struct chord* ch, int ingrace) +/* decide on stem direction and set default stem length for a chord */ +{ + if ((ch->ytop + ch->ybot < 8)||(ingrace)) { + ch->stemup = 1; + } else { + ch->stemup = 0; + }; + ch->stemlength = STEMLEN; +} + +static void setxy(double* x, double* y, struct note* n, struct feature* ft, + double offset, double half_head) +/* compute x,y co-ordinates above note for drawing a beam */ +{ + if (n->stemup) { + *x = ft->x + half_head; + *y = (double)(TONE_HT*n->y) + n->stemlength - offset; + } else { + *x = ft->x - half_head; + *y = (double)(TONE_HT*n->y) - n->stemlength + offset; + }; +} + +static void drawtuple(struct feature* beamset[], int beamctr, int tupleno) +/* label a beam which is a tuplet of notes (e.g. triplet) */ +{ + double x0, x1, y0, y1; + struct note* n; + int stemup; + + x0 = beamset[0]->x; + n = beamset[0]->item; + stemup = n->stemup; + if (stemup) { + y0 = (double)(TONE_HT*n->y) + n->stemlength; + } else { + y0 = (double)(TONE_HT*n->y) - n->stemlength; + }; + x1 = beamset[beamctr-1]->x; + n = beamset[beamctr-1]->item; + if (stemup) { + y1 = (double)(TONE_HT*n->y) + n->stemlength; + } else { + y1 = (double)(TONE_HT*n->y) - n->stemlength; + }; + if (stemup) { + fprintf(f, " %.1f %.1f (%d) bnum", (x0+x1)/2, (y0+y1)/2 + TUPLE_UP, tupleno); + } else { + fprintf(f, " %.1f %.1f (%d) bnum", (x0+x1)/2, (y0+y1)/2 + TUPLE_DOWN, + tupleno); + }; +} + +static void drawhtuple(double xstart, double xend, int num, double y) +/* draw a tuple using half-brackets */ +/* used for tuples which do not correspond to a beam */ +{ + double xmid; + + xmid = (xstart + xend)/2; + fprintf(f, " %.1f %.1f %.1f %.1f hbr", xstart, y, xmid-6.0, y); + fprintf(f, " %.1f %.1f %.1f %.1f hbr", xend, y, xmid+6.0, y); + fprintf(f, " %.1f %.1f (%d) bnum\n", xmid, y-4.0, num); +} + +static void drawbeam(struct feature* beamset[], int beamctr, int dograce) +/* draw a beam spanning the notes in array beamset[] */ +{ + int i, d; + int donenotes; + int start, stop; + double x0, x1, y0, y1, offset; + struct note* n; + int stemup, beamdir; + double half_head; + + if (beamctr==0) { + event_error("Internal error: beam with 0 notes"); + showtune(&thetune); + return; + /* exit(0); [SS] 2017-11-17 */ + }; + if (beamset[0]->type != NOTE) { + event_error("Internal error: beam does not start with NOTE"); + exit(0); + }; + fprintf(f, "\n"); + if (redcolor) fprintf(f,"1.0 0.0 0.0 setrgbcolor\n"); + n = beamset[0]->item; + stemup = n->stemup; + beamdir = 2*stemup - 1; + donenotes = 1; + d = -3; + offset = 0.0; + if (dograce) { + half_head = GRACE_HALF_HEAD; + } else { + half_head = HALF_HEAD; + }; + while (donenotes) { + donenotes = 0; + start = -1; + for (i=0; iitem; + if (n->base_exp <= d) { + if (start == -1) { + start = i; + setxy(&x0, &y0, n, beamset[i], offset, half_head); + }; + stop = i; + setxy(&x1, &y1, n, beamset[i], offset, half_head); + }; + if ((n->beaming == endbeam) || (n->base_exp > d)) { + if (start != -1) { + /* draw unit */ + if (start == stop) { + if (start != 0) { + /* half line in front of note */ + n = beamset[start-1]->item; + setxy(&x0, &y0, n, beamset[start-1], offset, half_head); + x0 = x0 + (x1-x0)/2; + y0 = y0 + (y1-y0)/2; + } else { + /* half line behind note */ + n = beamset[start+1]->item; + setxy(&x1, &y1, n, beamset[start+1], offset, half_head); + x1 = x1 + (x0-x1)/2; + y1 = y1 + (y0-y1)/2; + }; + if (dograce) { + fprintf(f, "%.1f %.1f %.1f %.1f gbm2\n", x0, y0, x1, y1); + } else { + fprintf(f, "%.1f %.1f %.1f %.1f %.1f bm\n", x0, y0, x1, y1, + (double)(beamdir * TAIL_WIDTH)); + }; + } else { + if (dograce) { + fprintf(f, "%.1f %.1f %.1f %.1f gbm2\n", x0, y0, x1, y1); + } else { + fprintf(f, "%.1f %.1f %.1f %.1f %.1f bm\n", x0, y0, x1, y1, + (double)(beamdir * TAIL_WIDTH)); + }; + }; + donenotes = 1; + start = -1; + }; + }; + }; + d = d - 1; + offset = offset + TAIL_SEP; + }; + if (redcolor) fprintf(f,"0 setgray\n"); +} + +static void sizevoice(struct voice* v, struct tune* t) +/* compute width and height values for all elements in voice */ +{ + struct feature* ft; + struct note* anote; + struct key* akey; + struct aclef* theclef; + char* astring; + struct fract* afract; + struct rest* arest; + struct note* lastnote; + struct chord* thischord; + struct feature* chordplace; + struct tuple* thistuple; + enum tail_type chordbeaming; + int intuple, tuplecount; + struct feature* tuplefeature; + + ingrace = 0; + inchord = 0; + intuple = 0; + tuplefeature = NULL; + chordhead = NULL; + thischord = NULL; + chordplace = NULL; + v->clef->type = t->clef.type; + v->clef->octave = t->clef.octave; + if (v->keysig == NULL) { + event_error("Voice has no key signature"); + }; + ft = v->first; + lastnote = NULL; + while (ft != NULL) { + ft->xleft = 0.0; + ft->xright = 0.0; + ft->ydown = 0.0; + ft->yup = 0.0; + switch (ft->type) { + case SINGLE_BAR: + ft->xleft = (float) 0.8; + ft->xright = 0.0; + break; + case DOUBLE_BAR: + ft->xleft = 3.0; + ft->xright = 0.0; + break; + case BAR_REP: + ft->xleft = 1.0; + ft->xright = 10.0; + break; + case REP_BAR: + ft->xleft = 1.0; + ft->xright = 10.0; + break; + case REP1: + break; + case REP2: + break; + case PLAY_ON_REP: + break; + case BAR1: + break; + case REP_BAR2: + ft->xleft = 6.0; + ft->xright = 5.0; + break; + case DOUBLE_REP: + ft->xleft = 10.0; + ft->xright = 10.0; + break; + case THICK_THIN: + ft->xleft = 0.0; + ft->xright = 6.0; + break; + case THIN_THICK: + ft->xleft = 6.0; + ft->xright = 0.0; + break; + case PART: + astring = ft->item; + case TEMPO: + break; + case TIME: + afract = ft->item; + if (afract == NULL) { + afract = &v->meter; + }; + ft->xleft = 0; + ft->xright = (float) size_timesig(afract); + break; + case KEY: + ft->xleft = 0; + akey = ft->item; + ft->xright = (float) size_keysig(v->keysig->map, akey->map); + set_keysig(v->keysig, akey); + break; + case REST: + arest = ft->item; + sizerest(arest, ft); + if (intuple) { + if (ft->yup > thistuple->height) { + thistuple->height = ft->yup; + }; + tuplecount = tuplecount - 1; + if (tuplecount <= 0) { + tuplefeature->yup = thistuple->height + HTUPLE_HT; + intuple = 0; + thistuple = NULL; + tuplefeature = NULL; + }; + }; + break; + case TUPLE: + thistuple = ft->item; + if (thistuple->beamed == 0) { + intuple = 1; + tuplecount = thistuple ->r; + thistuple->height = (double)(10*TONE_HT); + tuplefeature = ft; + }; + break; + case NOTE: + anote = ft->item; + setstemlen(anote, ingrace); + sizenote(anote, ft, ingrace); + if (inchord) { + if (chordcount == 0) { + chordhead = ft; + thischord->ytop = anote->y; + thischord->ybot = anote->y; + chordbeaming = anote->beaming; + } else { + if (anote->y > thischord->ytop) { + thischord->ytop = anote->y; + }; + if (anote->y < thischord->ybot) { + thischord->ybot = anote->y; + }; + if (ft->xleft > chordhead->xleft) { + chordhead->xleft = ft->xleft; + }; + if (ft->xright > chordhead->xright) { + chordhead->xright = ft->xright; + }; + if (ft->yup > chordhead->yup) { + chordhead->yup = ft->yup; + }; + if (ft->ydown > chordhead->ydown) { + chordhead->ydown = ft->ydown; + }; + ft->type = CHORDNOTE; + }; + chordcount = chordcount + 1; + }; + if (intuple) { + if (ft->yup > thistuple->height) { + thistuple->height = ft->yup; + }; + tuplecount = tuplecount - 1; + if (tuplecount <= 0) { + tuplefeature->yup = thistuple->height + HTUPLE_HT; + intuple = 0; + thistuple = NULL; + tuplefeature =NULL; + }; + }; + break; + case NONOTE: + break; + case OLDTIE: + break; + case TEXT: + break; + case SLUR_ON: + break; + case SLUR_OFF: + break; + case TIE: + break; + case CLOSE_TIE: + break; + case TITLE: + break; + case CHANNEL: + break; + case TRANSPOSE: + break; + case RTRANSPOSE: + break; + case GRACEON: + ingrace = 1; + break; + case GRACEOFF: + ingrace = 0; + break; + case SETGRACE: + break; + case SETC: + break; + case GCHORD: + break; + case GCHORDON: + break; + case GCHORDOFF: + break; + case VOICE: + break; + case CHORDON: + inchord = 1; + chordcount = 0; + thischord = ft->item; + chordplace = ft; + spacechord(chordplace); + break; + case CHORDOFF: + if (thischord != NULL) { + anote = chordhead->item; + thischord->beaming = chordbeaming; + if (thischord->beaming == single) { + sizechord(thischord, ingrace); + }; + }; + inchord = 0; + chordhead = NULL; + thischord = NULL; + chordplace = NULL; + break; + case SLUR_TIE: + break; + case TNOTE: + break; + case LT: + break; + case GT: + break; + case DYNAMIC: + break; + case LINENUM: + lineno = (long)(ft->item); + break; + case MUSICLINE: + break; + case MUSICSTOP: + break; + case WORDLINE: + break; + case WORDSTOP: + break; + case INSTRUCTION: + break; + case NOBEAM: + break; + case CLEF: + theclef = ft->item; + if (theclef == NULL) { + theclef = v->clef; + }; + ft->yup = (double)8*TONE_HT; + ft->ydown = 0.0; + switch (theclef->type) { + case treble: + ft->yup = (double)TREBLE_UP; + ft->ydown = (double)TREBLE_DOWN; + ft->xright = TREBLE_RIGHT; + ft->xleft = TREBLE_LEFT; + break; + case baritone: + ft->yup = (double)12*TONE_HT; + ft->xright = CCLEF_RIGHT; + ft->xleft = CCLEF_LEFT; + break; + case tenor: + ft->yup = (double)10*TONE_HT; + ft->xright = CCLEF_RIGHT; + ft->xleft = CCLEF_LEFT; + break; + case alto: + ft->xright = CCLEF_RIGHT; + ft->xleft = CCLEF_LEFT; + break; + case mezzo: + ft->ydown = (double)2*TONE_HT; + ft->xright = CCLEF_RIGHT; + ft->xleft = CCLEF_LEFT; + break; + case soprano: + ft->ydown = (double)4*TONE_HT; + ft->xright = CCLEF_RIGHT; + ft->xleft = CCLEF_LEFT; + break; + case bass: + ft->xright = BASS_RIGHT; + ft->xleft = BASS_LEFT; + break; + case noclef: + break; + }; + if (theclef->octave > 0) { + ft->yup = ft->yup + CLEFNUM_HT; + }; + if (theclef->octave < 0) { + ft->ydown = ft->ydown + CLEFNUM_HT; + }; + v->clef->type = theclef->type; + v->clef->octave = theclef->octave; + break; + case PRINTLINE: + break; + case NEWPAGE: + break; + case LEFT_TEXT: + break; + case CENTRE_TEXT: + break; + case VSKIP: + break; + case SPLITVOICE: + break; + default: + printf("unknown type %d\n", ft->type); + break; + }; + ft = ft->next; + }; +} + +static void sizetune(struct tune* t) +/* compute width and height values for all elements in the tune */ +{ + struct voice* v; + + v = firstitem(&t->voices); + while (v != NULL) { + sizevoice(v, t); + v = nextitem(&t->voices); + }; +} + +static void singlehead(double x, double y, int base, int base_exp, int dots) +/* draws a note head */ +{ + int i; + double dot_offset; + + fprintf(f, "%.1f %.1f ", x, y); + /* note head */ + dot_offset = HALF_HEAD; + switch(base_exp) { + case 1: + fprintf(f, "BHD"); + dot_offset = HALF_BREVE; + break; + case 0: + fprintf(f, "HD"); + break; + case -1: + fprintf(f, "Hd"); + break; + default: + if (base_exp > 1) { + fprintf(f, "BHD"); + dot_offset = HALF_BREVE; + event_warning("Note value too long to represent"); + } else { + fprintf(f, "hd"); + }; + break; + }; + if (dots == -1) { + event_warning("Note value cannot be represented"); + }; + for (i=1; i <= dots; i++) { + fprintf(f, " %.1f 3.0 dt", (double)(dot_offset+DOT_SPACE*i)); + }; +} + +static void drawhead(struct note* n, double x, struct feature* ft) +/* draw just the head of a note together with any decorations */ +/* and accidentals */ +{ + int i; + char* decorators; + double ytop, ybot; + double notex, accspace; + int offset; + + if (n->fliphead) { + if (n->stemup) { + notex = x + 2.0 * HALF_HEAD; + } else { + notex = x - 2.0 * HALF_HEAD; + } + } else { + notex = x; + }; + singlehead(notex, (double)(TONE_HT*n->y), n->base, n->base_exp, n->dots); + if (n->acc_offset == 0) { + accspace = ACC_OFFSET; + } else { + if (n->fliphead) { + /* compensate for flipped note head */ + if (n->stemup) { + offset = n->acc_offset; + } else { + offset = n->acc_offset - 2; + }; + } else { + offset = n->acc_offset - 1; + }; + accspace = ACC_OFFSET + ACC_OFFSET2 * offset; + }; + switch (n->accidental) { + case '=': + fprintf(f, " %.1f nt", accspace); + break; + case '^': + if (n->mult == 1) { + fprintf(f, " %.1f sh", accspace); + } else { + fprintf(f, " %.1f dsh", accspace); + }; + break; + case '_': + if (n->mult == 1) { + fprintf(f, " %.1f ft", accspace); + } else { + fprintf(f, " %.1f dft", accspace); + }; + break; + default: + break; + }; + i = 10; + while (n->y >= i) { + fprintf(f, " %d hl", i*TONE_HT); + i = i + 2; + }; + i = -2; + while (n->y <= i) { + fprintf(f, " %d hl", i*TONE_HT); + i = i - 2; + }; + if (n->accents != NULL) { + decorators = n->accents; + ytop = (n->y + 2) * TONE_HT; + ybot = (n->y - 2) * TONE_HT; + if (n->stemup) { + ytop = ytop + n->stemlength; + } else { + ybot = ybot - n->stemlength; + }; + for (i=0; i< (int) strlen(decorators); i++) { + switch (decorators[i]) { + case '.': + if (n->stemup) { + fprintf(f, " %.1f stc", ybot+STC_OFF); + ybot = ybot - SMALL_DEC_HT; + } else { + fprintf(f, " %.1f stc", ytop+STC_OFF); + ytop = ytop + SMALL_DEC_HT; + }; + break; + case 'R': + if (n->stemup) { + fprintf(f, " %.1f cpd", ybot+CPD_OFF); + ybot = ybot - SMALL_DEC_HT; + } else { + fprintf(f, " %.1f cpu", ytop+CPU_OFF); + ytop = ytop + SMALL_DEC_HT; + }; + break; + case 'M': + if (n->stemup) { + fprintf(f, " %.1f emb", ybot+EMB_OFF); + ybot = ybot - SMALL_DEC_HT; + } else { + fprintf(f, " %.1f emb", ytop+EMB_OFF); + ytop = ytop + SMALL_DEC_HT; + }; + break; + case 'H': + fprintf(f, " %.1f hld", ytop+HLD_OFF); + ytop = ytop + BIG_DEC_HT; + break; + case '~': + fprintf(f, " %.1f grm", ytop+GRM_OFF); + ytop = ytop + BIG_DEC_HT; + break; + case 'u': + fprintf(f, " %.1f upb", ytop+UPB_OFF); + ytop = ytop + BIG_DEC_HT; + break; + case 'v': + fprintf(f, " %.1f dnb", ytop+DNB_OFF); + ytop = ytop + BIG_DEC_HT; + break; + case 'T': + fprintf(f, " %.1f trl", ytop+TRL_OFF); + ytop = ytop + BIG_DEC_HT; + break; + default: + break; + }; + }; + }; + fprintf(f, "\n"); +} + +static void drawgracehead(struct note* n, double x, struct feature* ft, + enum tail_type tail) +/* draw the head of a grace note */ +{ + int i; + double y; + + y = (double)(TONE_HT*n->y); + switch (tail) { + case nostem: + /* note head only */ + fprintf(f, "%.1f %.1f gn", x, y); + break; + case single: + /* note head with stem and tail */ + fprintf(f, "%.1f %.1f %.1f gn1", x, y, n->stemlength); + break; + case midbeam: + case startbeam: + case endbeam: + /* note head with stem and tail */ + fprintf(f, "%.1f %.1f %.1f gnt", x, y, n->stemlength); + break; + }; + switch (n->accidental) { + case '=': + fprintf(f, " %.1f %.1f gnt0", x-ACC_OFFSET, y); + break; + case '^': + if (n->mult == 1) { + fprintf(f, " %.1f %.1f gsh0", x-ACC_OFFSET, y); + } else { + fprintf(f, " %.1f %.1f gds0h", x-ACC_OFFSET, y); + }; + break; + case '_': + if (n->mult == 1) { + fprintf(f, " %.1f %.1f gft0", x-ACC_OFFSET, y); + } else { + fprintf(f, " %.1f %.1f gdf0", x-ACC_OFFSET, y); + }; + break; + default: + break; + }; + i = 10; + while (n->y >= i) { + fprintf(f, " %.1f %.1f ghl", x, (double)i*TONE_HT); + i = i + 2; + }; + i = -2; + while (n->y <= i) { + fprintf(f, " %.1f %.1f ghl", x, (double)i*TONE_HT); + i = i - 2; + }; + fprintf(f, "\n"); +} + +static void handlegracebeam(struct note *n, struct feature* ft) +/* this is called to set up the variables needed to lay out */ +/* beams for grace notes */ +{ + switch (n->beaming) { + case single: + gracebeamctr = -1; + break; + case startbeam: + gracebeamset[0] = ft; + gracebeamctr = 1; + break; + case midbeam: + case endbeam: + if (gracebeamctr > 0) { + gracebeamset[gracebeamctr] = ft; + if (gracebeamctr < 32) { + gracebeamctr = gracebeamctr + 1; + } else { + event_error("Too many notes under one gracebeam"); + }; + } else { + event_error("Internal beaming error"); + exit(1); + }; + break; + case nostem: + break; + }; +} + +static void drawgracenote(struct note* n, double x, struct feature* ft, + struct chord* thischord) +/* draw an entire grace note */ +{ + handlegracebeam(n, ft); + drawgracehead(n, x, ft, n->beaming); + if (n->beaming == endbeam) { + drawbeam(gracebeamset, gracebeamctr, 1); + }; + fprintf(f, "\n"); +} + +static void singletail(int base, int base_exp, int stemup, double stemlength) +/* draw the tail of a note if it has one */ +{ + switch(base_exp) { + case 0: + case -1: + case -2: + break; + case -3: + if (stemup) { + fprintf(f, " %.1f f1u", stemlength); + } else { + fprintf(f, " %.1f f1d", stemlength); + }; + break; + case -4: + if (stemup) { + fprintf(f, " %.1f f2u", stemlength); + } else { + fprintf(f, " %.1f f2d", stemlength); + }; + break; + case -5: + if (stemup) { + fprintf(f, " %.1f f3u", stemlength); + } else { + fprintf(f, " %.1f f3d", stemlength); + }; + break; + case -6: + if (stemup) { + fprintf(f, " %.1f f4u", stemlength); + } else { + fprintf(f, " %.1f f4d", stemlength); + }; + break; + default: + if (base_exp < -6) { + event_warning("Note value too small to represent"); + if (stemup) { + fprintf(f, " %.1f f4u", stemlength); + } else { + fprintf(f, " %.1f f4d", stemlength); + }; + }; + break; + }; +} + +static void drawchordtail(struct chord* ch, double x) +/* draw the tail to a set of notes belonging to a single chord */ +{ + if (ch->base > 1) { + fprintf(f, "%.1f setx ", x); + if (ch->stemup) { + fprintf(f, "%.1f sety ", (double)(ch->ybot*TONE_HT)); + fprintf(f, " %.1f su ", ch->stemlength + + (double)((ch->ytop - ch->ybot)*TONE_HT)); + } else { + fprintf(f, "%.1f sety ", (double)(ch->ytop*TONE_HT)); + fprintf(f, " %.1f sd ", ch->stemlength + + (double)((ch->ytop - ch->ybot)*TONE_HT)); + }; + if (ch->beaming == single) { + singletail(ch->base, ch->base_exp, ch->stemup, ch->stemlength+ + (double)((ch->ytop - ch->ybot)*TONE_HT)); + }; + fprintf(f, "\n"); + }; +} + +static void showtext(struct llist* textitems, double x, double ystart, double ygap) +/* This routine deals with lyrics, guitar chords and instructions */ +/* print text item at specified point */ +/* if a string starts with ':' this means it is a special symbol */ +{ + double ygc; + char* gc; + + gc = firstitem(textitems); + ygc = ystart; + while (gc != NULL) { + if (*gc == ':') { + switch (*(gc+1)) { + case 'p': /* part label */ + fprintf(f, " %.1f %.1f %.1f (", x, ygc, ygap); + ISOfprintf(gc+2); + fprintf(f, ") boxshow "); + break; + case 's': + fprintf(f, " %.1f %.1f segno\n", x, ygc); + break; + case 'c': + fprintf(f, " %.1f %.1f coda\n", x, ygc); + break; + default: + break; + }; + } else { + fprintf(f, " %.1f %.1f (", x, ygc); + ISOfprintf(gc); + fprintf(f, ") gc "); + }; + gc = nextitem(textitems); + ygc = ygc - ygap; + }; +} + +static void define_font(struct font* thefont, char* s) +/* set up a font structure with user-defined font and pointsize */ +{ + char fontname[80]; + int fontsize, params; + + params = sscanf(s, "%s %d", fontname, &fontsize); + if (params >= 1) { + if (strcmp(fontname, "-") != 0) { + if (thefont->name != NULL) { + free(thefont->name); + }; + thefont->name = addstring(fontname); + thefont->defined = 0; + }; + if (params == 2) { + thefont->pointsize = fontsize; + }; + }; +} + +int read_boolean(s) +char *s; +/* set logical parameter from %%command */ +/* part of the handling for event_specific */ +{ + char p[10]; + int result; + + p[0] = '\0'; + sscanf(s, " %10s", p); + result = 1; + if ((strcmp(p, "0") == 0) || (strcmp(p, "no") == 0) || + (strcmp(p, "false") == 0)) { + result = 0; + }; + return(result); +} + +void font_command(p, s) +/* execute font-related %%commands */ +/* called from event_specific */ +char* p; +char*s; +{ + if (strcmp(p, "textfont") == 0) { + define_font(&textfont, s); + }; + if (strcmp(p, "titlefont") == 0) { + define_font(&titlefont, s); + }; + if (strcmp(p, "subtitlefont") == 0) { + define_font(&subtitlefont, s); + }; + if (strcmp(p, "wordsfont") == 0) { + define_font(&wordsfont, s); + }; + if (strcmp(p, "composerfont") == 0) { + define_font(&composerfont, s); + }; + if (strcmp(p, "vocalfont") == 0) { + define_font(&vocalfont, s); + }; + if (strcmp(p, "gchordfont") == 0) { + define_font(&gchordfont, s); + }; + if (strcmp(p, "partsfont") == 0) { + define_font(&partsfont, s); + }; + if (strcmp(p, "titlespace") == 0) { + set_space(&titlefont, s); + }; + if (strcmp(p, "subtitlespace") == 0) { + set_space(&subtitlefont, s); + }; + if (strcmp(p, "composerspace") == 0) { + set_space(&composerfont, s); + }; + if (strcmp(p, "vocalspace") == 0) { + set_space(&vocalfont, s); + }; + if (strcmp(p, "wordsspace") == 0) { + set_space(&wordsfont, s); + }; + if (strcmp(p, "gchordspace") == 0) { + set_space(&gchordfont, s); + }; + if (strcmp(p, "textspace") == 0) { + set_space(&textfont, s); + }; + if (strcmp(p, "partsspace") == 0) { + set_space(&textfont, s); + }; + if (strcmp(p, "staffsep") == 0) { + sscanf(s, "%d", &staffsep); + }; + if (strcmp(p, "titleleft") == 0) { + titleleft = read_boolean(s); + }; + if (strcmp(p, "titlecaps") == 0) { + titlecaps = read_boolean(s); + }; +} + +static void setfont(int size, int num) +/* define font point size and style */ +{ + if ((fontsize != size) || (fontnum != num)) { + fprintf(f, "%d.0 F%d\n", size, num); + fontsize = size; + fontnum = num; + }; +} + +static void setfontstruct(struct font* thefont) +/* set font according to font structure */ +{ + if (thefont->name == NULL) { + setfont(thefont->pointsize, thefont->default_num); + } else { + if (thefont->defined == 0) { + /* define font */ + fprintf(f, "/%s-ISO /%s setISOfont\n", thefont->name, thefont->name); + fprintf(f, "/F%d { /%s-ISO exch selectfont} def\n", + thefont->special_num, thefont->name); + thefont->defined = 1; + } + setfont(thefont->pointsize, thefont->special_num); + }; +} + +static void notetext(struct note* n, int* tupleno, double x, struct feature* ft, + struct vertspacing* spacing) +/* print text associated with note */ +{ + if (n->beaming == endbeam) { + drawbeam(beamset, beamctr, 0); + if (*tupleno != 0) { + drawtuple(beamset, beamctr, *tupleno); + *tupleno = 0; + }; + }; + fprintf(f, "\n"); + if (n->instructions != NULL && spacing != NULL) { + setfontstruct(&partsfont); + showtext(n->instructions, x - ft->xleft, spacing->yinstruct, + (double)(partsfont.pointsize+partsfont.space)); + }; + if (n->gchords != NULL && spacing != NULL) { + setfontstruct(&gchordfont); + showtext(n->gchords, x - ft->xleft, spacing->ygchord, + (double)(gchordfont.pointsize+gchordfont.space)); + }; + if (n->syllables != NULL && spacing != NULL) { + setfontstruct(&vocalfont); + showtext(n->syllables, x - ft->xleft, spacing->ywords, + (double)(vocalfont.pointsize + vocalfont.space)); + }; +} + +static void handlebeam(struct note* n, struct feature* ft) +/* set up some variables needed to lay out the beam for a set of */ +/* notes beamed together */ +{ + switch (n->beaming) { + case single: + beamctr = -1; + break; + case startbeam: + beamset[0] = ft; + beamctr = 1; + rootstem = n->stemup; + break; + case midbeam: + case endbeam: + if (beamctr > 0) { + beamset[beamctr] = ft; + if (beamctr < 64) { + beamctr = beamctr + 1; + } else { + event_error("Too many notes under one beam"); + }; + } else { + event_error("Internal beaming error"); + }; + n->stemup = rootstem; + break; + }; +} + +static void drawnote(struct note* n, double x, struct feature* ft, + struct chord* thischord, + int* tupleno, struct vertspacing* spacing) +/* draw a note */ +{ + handlebeam(n, ft); + if (redcolor) fprintf(f,"1.0 0.0 0.0 setrgbcolor\n"); + drawhead(n, x, ft); + if (thischord == NULL) { + if (n->base > 1) { + if (n->stemup) { + fprintf(f, " %.1f su", n->stemlength); + } else { + fprintf(f, " %.1f sd", n->stemlength); + }; + }; + }; + if (n->beaming == single) { + singletail(n->base, n->base_exp, n->stemup, n->stemlength); + }; + fprintf(f, "\n"); + if (redcolor) fprintf(f,"0 setgray\n"); + notetext(n, tupleno, x, ft, spacing); +} + +static void drawrest(struct rest* r, double x, struct vertspacing* spacing) +/* draw a rest of specified duration */ +{ + int i; + + if (redcolor) fprintf(f,"1.0 0.0 0.0 setrgbcolor\n"); + if (r->multibar > 0) { + fprintf(f, "(%d) %.1f %d mrest ", r->multibar, x, 4*TONE_HT); + } else { + reducef(&r->len); + switch (r->base_exp) { + case 1: + fprintf(f, "%.1f %d r0 ", x, 4*TONE_HT); + break; + case 0: + fprintf(f, "%.1f %d r1 ", x, 4*TONE_HT); + break; + case -1: + fprintf(f, "%.1f %d r2 ", x, 4*TONE_HT); + break; + case -2: + fprintf(f, "%.1f %d r4 ", x, 4*TONE_HT); + break; + case -3: + fprintf(f, "%.1f %d r8 ", x, 4*TONE_HT); + break; + case -4: + fprintf(f, "%.1f %d r16 ", x, 4*TONE_HT); + break; + case -5: + fprintf(f, "%.1f %d r32 ", x, 4*TONE_HT); + break; + case -6: + fprintf(f, "%.1f %d r64 ", x, 4*TONE_HT); + break; + default: + event_error("Cannot represent rest length"); + break; + }; + }; + for (i=1; i <= r->dots; i++) { + fprintf(f, " %.1f 3.0 dt", (double)(HALF_HEAD+DOT_SPACE*i)); + }; + fprintf(f, "\n"); + if (redcolor) fprintf(f,"0 setgray\n"); + if (r->instructions != NULL) { + setfontstruct(&partsfont); + showtext(r->instructions, x, spacing->yinstruct, + (double)(partsfont.pointsize+partsfont.space)); + }; + if (r->gchords != NULL) { + setfontstruct(&gchordfont); + showtext(r->gchords, x, spacing->ygchord, + (double)(gchordfont.pointsize+gchordfont.space)); + }; +} + +static void setbeams(struct feature* note[], struct chord* chording[], int m, + double stemlen) +/* choose the spatial postioning of beams and compute appropriate stem */ +/* lengths for all notes within the beam */ +{ + int i; + double min[64]; + double lift; + struct note* anote; + int stemup; + double x1, y1, x2, y2, xi, yi; + double max; + + /* printf("Doing setbeams m=%d\n", m); */ + anote = note[0]->item; + stemup = anote->stemup; + /* calculate minimum feasible stem lengths */ + /* bearing in mind space needed for the tails */ + for (i=0; iitem; + anote->stemup = stemup; + switch (anote->base) { + default: + case 8: + min[i] = TAIL_SEP; + break; + case 16: + min[i] = 2*TAIL_SEP; + break; + case 32: + min[i] = 3*TAIL_SEP; + break; + case 64: + min[i] = 4*TAIL_SEP; + break; + }; + if (anote->accidental == ' ') { + min[i] = min[i] + TONE_HT; + } else { + if (stemup) { + min[i] = min[i] + (double)acc_upsize(anote->accidental); + } else { + min[i] = min[i] + (double)acc_downsize(anote->accidental); + }; + }; + min[i] = min[i] + TONE_HT; /* require at least this much clearance */ + if (stemup) { + if (chording[i] == NULL) { + min[i] = TONE_HT*anote->y + min[i]; + /* avoid collision with previous note */ + if ((i > 0) && (min[i-1] - TONE_HT < min[i])) { + min[i-1] = min[i]; + }; + } else { + min[i] = TONE_HT*chording[i]->ytop + min[i]; + }; + } else { + if (chording[i] == NULL) { + min[i] = TONE_HT*anote->y - min[i]; + /* avoid collision with previous note */ + if (i > 0) { + anote = note[i-1]->item; + if (anote->y*TONE_HT < min[i]) { + min[i] = anote->y*TONE_HT; + }; + }; + } else { + min[i] = TONE_HT*chording[i]->ybot - min[i]; + }; + }; + }; + /* work out clearance from a straight line between 1st and last note */ + x1 = note[0]->x; + anote = note[0]->item; + y1 = anote->y*TONE_HT; + x2 = note[m-1]->x; + anote = note[m-1]->item; + y2 = anote->y*TONE_HT; + if (x1 == x2) { + printf("Internal error: x1 = x2 = %.1f\n", x1); + x2 = x2 + 1; + }; + lift = 0.0; + max = 0.0; + if (stemup) { + for (i=0; ix; + yi = y1 + (y2-y1)*(xi-x1)/(x2-x1); + if (min[i] - yi > lift) { + lift = min[i] - yi; + }; + if (min[i] - yi < max) { + max = min[i] - yi; + }; + }; + if (lift - max < stemlen) { + lift = stemlen - max; + }; + } else { + for (i=0; ix; + yi = y1 + (y2-y1)*(xi-x1)/(x2-x1); + if (min[i] - yi < lift) { + lift = min[i] - yi; + }; + if (min[i] - yi > max) { + max = min[i] - yi; + }; + }; + if (max - lift < stemlen) { + lift = max - stemlen; + }; + }; + /* raise the line just enough to clear notes */ + for (i=0; iitem; + xi = note[i]->x; + if (stemup) { + anote->stemlength = (float) (y1 + (y2-y1)*(xi-x1)/(x2-x1) + lift - + TONE_HT*anote->y); + } else { + anote->stemlength = - (float) ((y1 + (y2-y1)*(xi-x1)/(x2-x1) + lift - + TONE_HT*anote->y)); + }; + }; + /* now transfer results to chords */ + for (i=0; iitem; + chording[i]->stemup = anote->stemup; + if (chording[i]->stemup) { + chording[i]->stemlength = anote->stemlength; + } else { + chording[i]->stemlength = (float) (anote->stemlength - + (double)((chording[i]->ytop - chording[i]->ybot)*TONE_HT)); + }; + }; + }; +}; + +static void beamline(struct feature* ft) +/* compute positioning of beams for one stave line of music */ +{ + struct feature* p; + struct note* n; + struct feature* beamnote[64]; + struct chord* chording[64]; + struct chord* lastchord; + struct feature* gracebeamnote[64]; + struct chord* gracechording[64]; + struct chord* gracelastchord; + int i, j; + int ingrace; + + + i = j = 0; /* [SS] 2019-08-11 not set in conditional line 2426 when + beamline() is called from finalsizeline() + https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=890250 */ + /* [SDG] 2020-06-03 also set j */ + p = ft; + ingrace = 0; + lastchord = NULL; + gracelastchord = NULL; + while ((p != NULL) && (p->type != PRINTLINE)) { + switch (p->type) { + case NOTE: + if (ingrace) { + n = p->item; + if (n == NULL) { + event_error("Missing NOTE!!!!"); + exit(0); + }; + if (n->beaming == startbeam) { + j = 0; + }; + if (n->beaming != single) { + gracebeamnote[j] = p; + gracechording[j] = gracelastchord; + j = j + 1; + }; + if ((n->beaming == endbeam)||(j==10)) { + setbeams(gracebeamnote, gracechording, j, GRACE_STEMLEN); + j = 0; + }; + } else { + /* non-grace notes*/ + n = p->item; + if (n == NULL) { + event_error("Missing NOTE!!!!"); + exit(0); + }; + if (n->beaming == startbeam) { + i = 0; + }; + if (n->beaming != single) { + beamnote[i] = p; + chording[i] = lastchord; + i = i + 1; + }; + if ((n->beaming == endbeam)||(i==64)) { + setbeams(beamnote, chording, i, STEMLEN); + i = 0; + }; + }; + break; + case CHORDON: + if (ingrace) { + gracelastchord = p->item; + } else { + lastchord = p->item; + }; + break; + case CHORDOFF: + if (ingrace) { + gracelastchord = NULL; + } else { + lastchord = NULL; + }; + break; + case GRACEON: + ingrace = 1; + break; + case GRACEOFF: + ingrace = 0; + gracelastchord = NULL; + break; + default: + break; + }; + p = p->next; + }; +} + +static void measureline(struct feature* ft, double* height, double* descender, + double* yend, double* yinstruct, double* ygchord, double* ywords) +/* calculate vertical space (height/descender) requirement of current line */ +{ + struct feature* p; + struct note* n; + struct rest* r; + double max, min; + int gotgchords, gcount; + int gotinstructions, icount; + int gotwords, wcount; + int gotends; + char* gc; + + p = ft; + min = 0.0; + max = 8*TONE_HT; + gotgchords = 0; + gotwords = 0; + gotinstructions = 0; + gotends = 0; + while ((p != NULL) && (p->type != PRINTLINE)) { + switch (p->type) { + case NOTE: + n = p->item; + if (n == NULL) { + event_error("Missing NOTE!!!!"); + exit(0); + } else { + if (n->gchords != NULL) { + gcount = 0; + gc = firstitem(n->gchords); + while (gc != NULL) { + gcount = gcount + 1; + gc = nextitem(n->gchords); + }; + if (gcount > gotgchords) { + gotgchords = gcount; + }; + }; + if (n->instructions != NULL) { + icount = 0; + gc = firstitem(n->instructions); + while (gc != NULL) { + icount = icount + 1; + gc = nextitem(n->instructions); + }; + if (icount > gotinstructions) { + gotinstructions = icount; + }; + }; + if (n->syllables != NULL) { + wcount = 0; + gc = firstitem(n->syllables); + while (gc != NULL) { + wcount = wcount + 1; + gc = nextitem(n->syllables); + }; + if (wcount > gotwords) { + gotwords = wcount; + }; + }; + /* recalculate note size in case of new stemlen */ + sizenote(n, p, ingrace); + if (p->yup > max) { + max = p->yup; + }; + if (p->ydown < min) { + min = p->ydown; + }; + }; + break; + case REST: + r = p->item; + if (r == NULL) { + event_error("Missing REST!!!!"); + exit(0); + } else { + if (r->gchords != NULL) { + gcount = 0; + gc = firstitem(r->gchords); + while (gc != NULL) { + gcount = gcount + 1; + gc = nextitem(r->gchords); + }; + if (gcount > gotgchords) { + gotgchords = gcount; + }; + }; + if (r->instructions != NULL) { + icount = 0; + gc = firstitem(r->instructions); + while (gc != NULL) { + icount = icount + 1; + gc = nextitem(r->instructions); + }; + if (icount > gotinstructions) { + gotinstructions = icount; + }; + }; + }; + break; + case REP1: + case REP2: + case PLAY_ON_REP: + case BAR1: + case REP_BAR2: + gotends = 1; + break; + case TUPLE: + case CLEF: + if (p->yup > max) { + max = p->yup; + }; + if (-(p->ydown) < min) { + min = -(p->ydown); + }; + break; + case TEMPO: + if (gotinstructions == 0) { + gotinstructions = 1; + }; + break; + default: + break; + }; + p = p->next; + }; + if ((gotgchords > 0) && (gchords_above)) { + max = max + (gchordfont.pointsize + gchordfont.space)*gotgchords; + *ygchord = max - (gchordfont.pointsize + gchordfont.space); + }; + if (gotinstructions > 0) { + max = max + INSTRUCT_HT*gotinstructions; + *yinstruct = max - INSTRUCT_HT; + }; + if (gotends) { + max = max + END_HT; + *yend = max - END_HT; + }; + if ((gotgchords > 0) && (!gchords_above)) { + *ygchord = min - (gchordfont.pointsize + gchordfont.space); + min = min - (gchordfont.pointsize + gchordfont.space)*gotgchords; + }; + if (gotwords > 0) { + *ywords = min - (vocalfont.pointsize + vocalfont.space); + min = min - (vocalfont.pointsize + vocalfont.space)*gotwords; + }; + *height = staffsep + max; + *descender = -min; +} + +static void printtext(place, s, afont) +enum placetype place; +char* s; +struct font* afont; +/* print text a line of text in specified font at specified place */ +{ + newblock((double)(afont->pointsize + afont->space), 0.0); + setfontstruct(afont); + fprintf(f, "("); + ISOfprintf(s); + switch (place) { + case left: + fprintf(f, ") 0 0 M show\n"); + break; + case right: + fprintf(f, ") %.1f 0 M lshow\n", (double) scaledwidth); + break; + case centre: + fprintf(f, ") %.1f 0 M cshow\n", scaledwidth/2.0); + break; + }; +} + +void vskip(double gap) +/* skip over specified amount of vertical space */ +{ + newblock(gap, 0.0); +} + +void centretext(s) +char* s; +/* print text centred in the middle of the page */ +/* used by %%centre command */ +{ + printtext(centre, s, &textfont); +} + +void lefttext(s) +char* s; +/* print text up against the left hand margin */ +/* used by %%text command */ +{ + printtext(left, s, &textfont); +} + +static void draw_tempo(double x_init, double y_init, struct atempo* t) +/* print out tempo as specified in Q: field */ +/* this goes to the left of the tune's title */ +{ + int base, base_exp, dots; + double x, y; + char ticks[30]; + + if (t != NULL) { + x = x_init; + y = y_init; + /* fprintf(f, "gsave 0.7 0.7 scale\n"); */ + setfont(12, 3); + if (t->pre != NULL) { + fprintf(f, "%.1f %.1f M (", x, y); + ISOfprintf(t->pre); + fprintf(f, ") show\n"); + x = x + stringwidth(t->pre, 12, 3) + HALF_HEAD * 2; + }; + if (t->count != 0) { + dots = count_dots(&base, &base_exp, t->basenote.num, t->basenote.denom); + singlehead(x, y, base, base_exp, dots); + if (base >= 2) { + fprintf(f, " %.1f su", TEMPO_STEMLEN); + }; + singletail(base, base_exp, 1, TEMPO_STEMLEN); + x = x + 20.0; + sprintf(ticks, "= %d", t->count); + fprintf(f, " %.1f %.1f M (%s) show\n", x, y, ticks); + x = x + stringwidth(ticks, 12, 3); + }; + if (t->post != NULL) { + fprintf(f, "%.1f %.1f M ( ", x, y); + ISOfprintf(t->post); + fprintf(f, ") show\n"); + }; + /* fprintf(f, "grestore\n"); */ + }; +} + +static void voicedivider() +/* This draws a horizontal line. It is used to mark where one voice */ +/* ends and the next one starts */ +{ + newblock((double)staffsep, 0.0); + fprintf(f, " %.1f %.1f M %.1f %.1f L\n", + scaledwidth/4.0, -descend, + scaledwidth*3/4.0, -descend); + newblock((double)staffsep, 0.0); +} + +static void resetvoice(struct tune* t, struct voice * v) +/* sets up voice with initial attributes as defined in abc tune header */ +{ + v->place = v->first; + if (v->clef == NULL) { + v->clef = newclef(t->clef.type, t->clef.octave); + } else { + v->clef->type = t->clef.type; + v->clef->octave = t->clef.octave; + }; + if (v->keysig == NULL) { + v->keysig = newkey(t->keysig->name, t->keysig->sharps, + t->keysig->map, t->keysig->mult); + } else { + free(v->keysig->name); + v->keysig->name = addstring(t->keysig->name); + set_keysig(v->keysig, t->keysig); + /* v->keysig->sharps = t->keysig->sharps; */ + }; + v->meter.num = t->meter.num; + v->meter.denom = t->meter.denom; +} + +static void resettune(struct tune* t) +/* initializes all voices to their start state */ +{ + struct voice* v; + + v = firstitem(&t->voices); + while (v != NULL) { + resetvoice(t, v); + v = nextitem(&t->voices); + }; +} + +void setup_fonts() +{ + init_font(&textfont, TEXT_HT, 1, 1, 5); + init_font(&titlefont, TITLE1_HT, 0, 0, 6); + init_font(&subtitlefont, TITLE2_HT, 0, 0, 7); + init_font(&wordsfont, WORDS_HT, 1, 0, 8); + init_font(&composerfont, COMP_HT, 1, 1, 9); + init_font(&vocalfont, LYRIC_HT, 1, 2, 10); + init_font(&gchordfont, CHORDNAME_HT, 1, 3, 11); + init_font(&partsfont, INSTRUCT_HT, 1, 3, 12); + staffsep = VERT_GAP; +} + +void open_output_file(filename, boundingbox) +char* filename; +struct bbox* boundingbox; +/* open output file and write the abc2ps library to it */ +{ + f = fopen(filename, "w"); + if (f == NULL) { + printf("Could not open file!!\n"); + exit(0); + }; + printf("writing file %s\n", filename); + printlib(f, filename, boundingbox); + fontsize = 0; + fontnum = 0; + startpage(); +} + +void close_output_file() +/* complete last page and close file */ +{ + if (f != NULL) { + closepage(); + fclose(f); + f = NULL; + }; +} + +static int endrep(inend, end_string, x1, x2, yend) +int inend; +char* end_string; +double x1, x2, yend; +/* draws either 1st or 2nd ending marker */ +{ + if (inend != 0) { + fprintf(f, "%.1f %.1f %.1f (%s) endy1\n", yend, x1, x2, end_string); + }; + return(0); +} + +static void drawslurtie(struct slurtie* s) +/* draws slur or tie corresponding to the given structure */ +{ + double x0, y0, x1, y1; + struct feature* ft; + struct note* n; + struct rest* r; + + ft = s->begin; + if (ft == NULL) { + event_error("Slur or tie missing start note"); + return; + }; + x0 = ft->x; + if ((ft->type == NOTE) || (ft->type == CHORDNOTE)) { + n = ft->item; + y0 = (double)(n->y * TONE_HT); + } else { + if (ft->type == REST) { + r = ft->item; + y0 = (double)(4 * TONE_HT); + } else { + printf("Internal error: NOT A NOTE/REST (%d)in slur/tie\n" ,ft->type); + return; + }; + }; + if (s->crossline) { + /* draw to end of stave */ + x1 = scaledwidth + xmargin/(2.0*scale); + y1 = y0; + } else { + /* draw slur/tie to other element */ + ft = s->end; + if (ft == NULL) { + event_error("Slur or tie missing end note"); + return; + }; + x1 = ft->x; + if ((ft->type == NOTE) || (ft->type == CHORDNOTE)) { + n = ft->item; + y1 = (double)(n->y * TONE_HT); + } else { + if (ft->type == REST) { + r = ft->item; + y1 = (double)(4 * TONE_HT); + } else { + printf("Internal error: NOT A NOTE/REST (%d)in slur/tie\n" ,ft->type); + return; + }; + }; + }; + if (n->stemup) { + fprintf(f, " %.1f %.1f %.1f %.1f slurdown\n", x0, y0, x1, y1); + } else { + fprintf(f, " %.1f %.1f %.1f %.1f slurup\n", x0, y0, x1, y1); + }; +} + +static void close_slurtie(struct slurtie* s) +/* This deals with the special case of a slur or tie running beyond */ +/* one line of music and into the next. This draws the part on the */ +/* second line of music */ +{ + double x0, y0, x1, y1; + struct feature* ft; + struct note* n; + struct rest* r; + + if ((s == NULL) || (s->crossline == 0)) { + return; + } else { + /* draw slur/tie to other element */ + ft = s->end; + if (ft == NULL) { + event_error("Slur or tie missing end note"); + return; + }; + x1 = ft->x; + if ((ft->type == NOTE) || (ft->type == CHORDNOTE)) { + n = ft->item; + y1 = (double)(n->y * TONE_HT); + } else { + if (ft->type == REST) { + r = ft->item; + y1 = (double)(4 * TONE_HT); + } else { + printf("Internal error: NOT A NOTE/REST (%d)in slur/tie\n" ,ft->type); + return; + }; + }; + }; + x0 = TREBLE_LEFT + TREBLE_RIGHT; + y0 = y1; + if (n->stemup) { + fprintf(f, " %.1f %.1f %.1f %.1f slurdown\n", x0, y0, x1, y1); + } else { + fprintf(f, " %.1f %.1f %.1f %.1f slurup\n", x0, y0, x1, y1); + }; +} + +static void blockline(struct voice* v, struct vertspacing** spacing) +/* gets vertical spacing for current line */ +{ + struct feature* ft; + /* struct vertspacing* spacing; */ + + ft = v->place; + /* find end of line */ + while ((ft != NULL) && (ft->type != PRINTLINE)) { + ft = ft->next; + }; + if ((ft != NULL) && (ft->type == PRINTLINE)) { + *spacing = ft->item; + } else { + *spacing = NULL; + event_error("Expecting line of music - possible voices mis-match"); + }; +} + +static void printbarnumber(double x, int n) +{ +if (barnums <0 ) return; +if (x + 15.0 > scaledwidth) return; +fprintf(f, " %.1f %.1f (%d) bnum\n", x, 28.0, n); +} + +static void underbar(struct feature* ft) +/* This is never normally called, but is useful as a debugging routine */ +/* shows width of a graphical element */ +{ + fprintf(f, " %.1f -10 moveto %.1f -10 lineto stroke\n", ft->x - ft->xleft, + ft->x + ft->xright); +} + +static int printvoiceline(struct voice* v) +/* draws one line of music from specified voice */ +{ + struct feature* ft; + struct note* anote; + struct key* akey; + char* astring; + struct fract* afract; + struct rest* arest; + struct tuple* atuple; + double x; + int sharps; + struct chord* thischord; + int chordcount; + double xchord; + struct aclef* theclef; + int printedline; + double xend; + int inend; + char endstr[80]; + int ingrace; + struct vertspacing* spacing; + struct dynamic *psaction; + + /* skip over line number so we can check for end of tune */ + while ((v->place != NULL) && + ((v->place->type == LINENUM) || (v->place->type == NEWPAGE) || + (v->place->type == LEFT_TEXT) || (v->place->type == CENTRE_TEXT) || + (v->place->type == VSKIP))) { + if (v->place->type == LINENUM) { + lineno = (long)(v->place->item); + }; + if (v->place->type == NEWPAGE) { + newpage(); + }; + if (v->place->type == LEFT_TEXT) { + printtext(left, v->place->item, &textfont); + }; + if (v->place->type == CENTRE_TEXT) { + printtext(centre, v->place->item, &textfont); + }; + if (v->place->type == VSKIP) { + vskip((double)((long)v->place->item)); + }; + v->place = v->place->next; + }; + if (v->place == NULL) { + return(0); + }; + inend = 0; + ingrace = 0; + chordcount = 0; + v->beamed_tuple_pending = 0; + x = 20; + thischord = NULL; + if (v->keysig == NULL) { + event_error("Voice has no key signature"); + } else { + sharps = v->keysig->sharps; + }; + /* draw stave */ + ft = v->place; + blockline(v, &spacing); + if (spacing != NULL) { + newblock(spacing->height, spacing->descender); + staveline(); + fprintf(f, "0 0 M 0 24 L\n"); + }; + x = 0.0; + /* now draw the line */ + printedline = 0; + while ((ft != NULL) && (!printedline)) { + /* printf("type = %d\n", ft->type); */ + switch (ft->type) { + case SINGLE_BAR: + fprintf(f, "%.1f bar\n", ft->x); + printbarnumber(ft->x, (long)ft->item); + break; + case DOUBLE_BAR: + fprintf(f, "%.1f dbar\n", ft->x); + printbarnumber(ft->x, (long)ft->item); + inend = endrep(inend, endstr, xend, ft->x, spacing->yend); + break; + case BAR_REP: + fprintf(f, "%.1f fbar1 %.1f rdots\n", ft->x, ft->x+10); + printbarnumber(ft->x, (long)ft->item); + inend = endrep(inend, endstr, xend, ft->x, spacing->yend); + break; + case REP_BAR: + fprintf(f, "%.1f rdots %.1f fbar2\n", ft->x, ft->x+10); + printbarnumber(ft->x, (long)ft->item); + inend = endrep(inend, endstr, xend, ft->x, spacing->yend); + break; + case REP1: + inend = endrep(inend, endstr, xend, ft->x - ft->xleft, spacing->yend); + inend = 1; + strcpy(endstr, "1"); + xend = ft->x + ft->xright; + break; + case REP2: + inend = endrep(inend, endstr, xend, ft->x - ft->xleft, spacing->yend); + inend = 2; + strcpy(endstr, "2"); + xend = ft->x + ft->xright; + break; + case PLAY_ON_REP: + inend = endrep(inend, endstr, xend, ft->x - ft->xleft, spacing->yend); + inend = 1; + strcpy(endstr, ft->item); + xend = ft->x + ft->xright; + break; + case BAR1: + fprintf(f, "%.1f bar\n", ft->x); + printbarnumber(ft->x, (long)ft->item); + inend = endrep(inend, endstr, xend, ft->x - ft->xleft, spacing->yend); + inend = 1; + strcpy(endstr, "1"); + xend = ft->x + ft->xright; + break; + case REP_BAR2: + fprintf(f, "%.1f rdots %.1f fbar2\n", ft->x, ft->x+10); + printbarnumber(ft->x, (long)ft->item); + inend = endrep(inend, endstr, xend, ft->x - ft->xleft, spacing->yend); + inend = 2; + strcpy(endstr, "2"); + xend = ft->x + ft->xright; + break; + case DOUBLE_REP: + fprintf(f, "%.1f fbar1 %.1f rdots %.1f fbar2 %.1f rdots\n", + ft->x, ft->x+10, ft->x+2, ft->x-8); + inend = endrep(inend, endstr, xend, ft->x, spacing->yend); + break; + case THICK_THIN: + fprintf(f, "%.1f fbar1\n", ft->x); + inend = endrep(inend, endstr, xend, ft->x, spacing->yend); + break; + case THIN_THICK: + fprintf(f, "%.1f fbar2\n", ft->x); + inend = endrep(inend, endstr, xend, ft->x, spacing->yend); + break; + case PART: + astring = ft->item; + break; + case TEMPO: + draw_tempo(ft->x, spacing->yinstruct, ft->item); + break; + case TIME: + afract = ft->item; + if (afract==NULL) { + if (v->line == midline) { + draw_meter(&v->meter, ft->x); + }; + } else { + setfract(&v->meter, afract->num, afract->denom); + if (v->line == midline) { + draw_meter(afract, ft->x); + }; + }; + break; + case KEY: + akey = ft->item; + if (v->line == midline) { + draw_keysig(v->keysig->map, akey->map, akey->mult, ft->x, v->clef); + }; + set_keysig(v->keysig, akey); + break; + case REST: + arest = ft->item; + drawrest(arest, ft->x, spacing); + if (v->tuple_count > 0) { + if (v->tuple_count == v->tuplenotes) { + v->tuple_xstart = ft->x - ft->xleft; + }; + if (v->tuple_count == 1) { + v->tuple_xend = ft->x + ft->xright; + drawhtuple(v->tuple_xstart, v->tuple_xend, v->tuplelabel, + v->tuple_height+4.0); + }; + v->tuple_count = v->tuple_count - 1; + }; + break; + case TUPLE: + atuple = ft->item; + if (atuple->beamed) { + v->beamed_tuple_pending = atuple->n; + v->tuplenotes = atuple->r; + v->tuple_count = 0; + } else { + v->tuple_height = atuple->height; + v->tuplenotes = atuple->r; + v->tuplelabel = atuple->label; + v->tuple_count = atuple->r; + }; + break; + case NOTE: + anote = ft->item; + if (thischord == NULL) { + if (ingrace) { + drawgracenote(anote, ft->x, ft, thischord); + } else { + drawnote(anote, ft->x, ft, thischord, &v->beamed_tuple_pending, + spacing); + if (v->tuple_count > 0) { + if (v->tuple_count == v->tuplenotes) { + v->tuple_xstart = ft->x - ft->xleft; + }; + if (v->tuple_count == 1) { + v->tuple_xend = ft->x + ft->xright; + drawhtuple(v->tuple_xstart, v->tuple_xend, v->tuplelabel, + v->tuple_height+4.0); + }; + v->tuple_count = v->tuple_count - 1; + }; + }; + } else { + xchord = ft->x; /* make sure all chord heads are aligned */ + if (ingrace) { + handlegracebeam(anote, ft); + drawgracehead(anote, ft->x, ft, nostem); + } else { + handlebeam(anote, ft); + drawhead(anote, ft->x, ft); + if (chordcount == 0) { + notetext(anote, &v->beamed_tuple_pending, ft->x, ft, spacing); + }; + chordcount = chordcount + 1; + }; + }; + break; + case CHORDNOTE: + anote = ft->item; + if (ingrace) { + drawgracehead(anote, xchord, ft, nostem); + } else { + drawhead(anote, xchord, ft); + }; + break; + case NONOTE: + break; + case OLDTIE: + break; + case TEXT: + break; + case SLUR_ON: + drawslurtie(ft->item); + break; + case SLUR_OFF: + close_slurtie(ft->item); + break; + case TIE: + drawslurtie(ft->item); + break; + case CLOSE_TIE: + close_slurtie(ft->item); + break; + case TITLE: + break; + case CHANNEL: + break; + case TRANSPOSE: + break; + case RTRANSPOSE: + break; + case GRACEON: + ingrace = 1; + break; + case GRACEOFF: + ingrace = 0; + break; + case SETGRACE: + break; + case SETC: + break; + case GCHORD: + break; + case GCHORDON: + break; + case GCHORDOFF: + break; + case VOICE: + break; + case CHORDON: + thischord = ft->item; + chordcount = 0; + drawchordtail(thischord, ft->next->x); + break; + case CHORDOFF: + thischord = NULL; + if (v->tuple_count > 0) { + if (v->tuple_count == v->tuplenotes) { + v->tuple_xstart = ft->x - ft->xleft; + }; + if (v->tuple_count == 1) { + v->tuple_xend = ft->x + ft->xright; + drawhtuple(v->tuple_xstart, v->tuple_xend, v->tuplelabel, + v->tuple_height+4.0); + }; + v->tuple_count = v->tuple_count - 1; + }; + break; + case SLUR_TIE: + break; + case TNOTE: + break; + case LT: + break; + case GT: + break; + case DYNAMIC: + psaction = ft->item; + if(psaction->color == 'r') redcolor = 1; /* [SS] 2013-11-04 */ + if(psaction->color == 'b') redcolor = 0; + break; + case LINENUM: + lineno = (long)(ft->item); + break; + case MUSICLINE: + v->line = midline; + break; + case PRINTLINE: + inend = endrep(inend, endstr, xend, scaledwidth, spacing->yend); + printedline = 1; + v->line = newline; + break; + case MUSICSTOP: + break; + case WORDLINE: + break; + case WORDSTOP: + break; + case INSTRUCTION: + break; + case NOBEAM: + break; + case CLEF: + theclef = ft->item; + if (theclef != NULL) { + v->clef->type = theclef->type; + v->clef->octave = theclef->octave; + }; + if (v->line == midline) { + printclef(v->clef, ft->x, ft->yup, ft->ydown); + }; + break; + case NEWPAGE: + event_warning("newpage in music line ignored"); + break; + case LEFT_TEXT: + event_warning("text in music line ignored"); + break; + case CENTRE_TEXT: + event_warning("centred text in music line ignored"); + break; + case VSKIP: + event_warning("%%vskip in music line ignored"); + break; + case SPLITVOICE: + break; + default: + printf("unknown type: %d\n", (int)ft->type); + break; + }; + ft = ft->next; + }; + v->place = ft; + return(1); +} + +static int finalsizeline(struct voice* v) +/* does final beaming and works out vertical height for */ +/* one line of specified voice */ +{ + struct feature* ft; + double height, descender; + double yend, yinstruct, ygchord, ywords; + struct vertspacing* avertspacing; + + /* skip over line number so we can check for end of tune */ + while ((v->place != NULL) && + ((v->place->type == LINENUM) || (v->place->type == NEWPAGE) || + (v->place->type == LEFT_TEXT) || (v->place->type == CENTRE_TEXT) || + (v->place->type == VSKIP))) { + v->place = v->place->next; + }; + if (v->place == NULL) { + return(0); + }; + ft = v->place; + beamline(ft); + measureline(ft, &height, &descender, ¥d, &yinstruct, &ygchord, &ywords); + /* newblock(height, descender); */ + /* find end of line */ + while ((ft != NULL) && (ft->type != PRINTLINE)) { + ft = ft->next; + }; + if ((ft != NULL) && (ft->type == PRINTLINE)) { + avertspacing = ft->item; + avertspacing->height = (float) height; + avertspacing->descender = (float) descender; + avertspacing->yend = (float) yend; + avertspacing->yinstruct = (float) yinstruct; + avertspacing->ygchord = (float) ygchord; + avertspacing->ywords = (float) ywords; + ft = ft->next; + }; + v->place = ft; + return(1); +} + +static void finalsizetune(t) +struct tune* t; +/* calculate beaming and calculate height of each music line */ +{ + struct voice* thisvoice; + int doneline; + + thisvoice = firstitem(&t->voices); + while (thisvoice != NULL) { + doneline = 1; + while (doneline==1) { + doneline = finalsizeline(thisvoice); + }; + thisvoice = nextitem(&t->voices); + }; +} + +static int getlineheight(struct voice* v, double* height) +/* calculate vertical space for one line of music from specified voice */ +{ + struct vertspacing* spacing; + + /* go over between line stuff */ + while ((v->place != NULL) && + ((v->place->type == LINENUM) || (v->place->type == NEWPAGE) || + (v->place->type == LEFT_TEXT) || (v->place->type == CENTRE_TEXT) || + (v->place->type == VSKIP))) { + if (v->place->type == LINENUM) { + lineno = (long)(v->place->item); + }; + if (v->place->type == LEFT_TEXT) { + *height = *height + textfont.pointsize + textfont.space; + }; + if (v->place->type == CENTRE_TEXT) { + *height = *height + textfont.pointsize + textfont.space; + }; + if (v->place->type == VSKIP) { + *height = *height + (double)((long)v->place->item); + }; + v->place = v->place->next; + }; + if (v->place == NULL) { + return(0); + }; + /* skip over rest of line */ + while ((v->place != NULL) && (v->place->type != PRINTLINE)) { + v->place = v->place->next; + }; + if (v->place != NULL) { + spacing = v->place->item; + *height = *height + spacing->height + spacing->descender; + v->place = v->place->next; + }; + return(1); +} + +static double tuneheight(t) +struct tune* t; +/* measures the vertical space needed by the tune */ +{ + char* atitle; + char* wordline; + struct voice* thisvoice; + int doneline; + double height; + char *notesfield; /* from N: field */ + + height = 0.0; + resettune(t); + atitle = firstitem(&t->title); + while (atitle != NULL) { + height = height + titlefont.pointsize + titlefont.space; + atitle = nextitem(&t->title); + }; + if ((titleleft == 1) && (t->tempo != NULL)) { + height = height + titlefont.pointsize + 2*titlefont.space; + }; + if (t->composer != NULL) { + height = height + composerfont.pointsize + composerfont.space; + }; + if (t->origin != NULL) { + height = height + composerfont.pointsize + composerfont.space; + }; + if (t->parts != NULL) { + height = height + composerfont.pointsize + composerfont.space; + }; + notesfield = firstitem(&t->notes); + while (notesfield != NULL) { + height = height + composerfont.pointsize + composerfont.space; + notesfield = nextitem(&t->notes); + }; + thisvoice = firstitem(&t->voices); + if (separate_voices) { + thisvoice = firstitem(&t->voices); + while (thisvoice != NULL) { + doneline = 1; + while (doneline==1) { + doneline = getlineheight(thisvoice, &height); + }; + thisvoice = nextitem(&t->voices); + if (thisvoice != NULL) { + height = height + 2.0 * staffsep; + }; + }; + } else { + doneline = 1; + while(doneline) { + thisvoice = firstitem(&t->voices); + doneline = 0; + while (thisvoice != NULL) { + doneline = (getlineheight(thisvoice, &height) || doneline); + thisvoice = nextitem(&t->voices); + }; + }; + }; + wordline = firstitem(&t->words); + while (wordline != NULL) { + height = height + wordsfont.pointsize + wordsfont.space; + wordline = nextitem(&t->words); + }; + return(height); +} + +void printtune(struct tune* t) +/* draws the PostScript for an entire abc tune */ +{ + int i; + char *atitle; + char *notesfield; /* from N: field */ + char *wordline; + int notesdone; + int titleno; + struct voice* thisvoice; + int doneline; + double firstvline, lastvline; + struct voice* firstvoice; + char xtitle[200]; + struct bbox boundingbox; + enum placetype titleplace; + + redcolor = 0; /* [SS] 2013-11-04 */ + resettune(t); + sizetune(t); + resettune(t); + if (separate_voices) { + monospace(t); + } else { + spacevoices(t); + }; + resettune(t); + finalsizetune(t); + if (debugging) { + showtune(t); + }; + if (eps_out) { + if (landscape) { + boundingbox.llx = ymargin; + boundingbox.lly = xmargin; + boundingbox.urx = (int) (ymargin + tuneheight(t) * scale); + boundingbox.ury = xmargin + pagewidth; + } else { + boundingbox.llx = xmargin; + boundingbox.lly = (int) (ymargin + pagelen - tuneheight(t) * scale); + boundingbox.urx = xmargin + pagewidth; + boundingbox.ury = ymargin + pagelen; + }; + sprintf(outputname, "%s%d.eps", outputroot, t->no); + open_output_file(outputname, &boundingbox); + } else { + make_open(); + if ((totlen > 0.0) && + (totlen+descend+tuneheight(t) > scaledlen-(double)(pagenumbering))) { + newpage(); + }; + }; + resettune(t); + notesdone = 0; + if (titleleft) { + titleplace = left; + } else { + titleplace = centre; + }; + atitle = firstitem(&t->title); + titleno = 1; + while (atitle != NULL) { + if (titleno == 1) { + if (titlecaps) { + for (i=0; i< (int) strlen(atitle); i++) { + if (islower(atitle[i])) { + atitle[i] = atitle[i] + 'A' - 'a'; + }; + }; + }; + if ((print_xref) && (strlen(atitle) < 180)) { + sprintf(xtitle, "%d. %s", t->no, atitle); + printtext(titleplace, xtitle, &titlefont); + } else { + printtext(titleplace, atitle, &titlefont); + }; + } else { + printtext(titleplace, atitle, &subtitlefont); + }; + titleno = titleno + 1; + atitle = nextitem(&t->title); + }; + if (t->tempo != NULL) { + if (titleleft) { + newblock((double)(titlefont.pointsize + titlefont.space*2), 0.0); + }; + draw_tempo(10.0, 0.0, t->tempo); + }; + if (t->composer != NULL) { + printtext(right, t->composer, &composerfont); + }; + if (t->origin != NULL) { + printtext(right, t->origin, &composerfont); + }; + if (t->parts != NULL) { + printtext(right, t->parts, &composerfont); + }; + notesfield = firstitem(&t->notes); + while (notesfield != NULL) { + printtext(left, notesfield, &composerfont); + notesfield = nextitem(&t->notes); + }; + donemeter = 0; + /* musicsetup(); */ + thisvoice = firstitem(&t->voices); + while (thisvoice != NULL) { + thisvoice->clef->type = t->clef.type; + thisvoice->clef->octave = t->clef.octave; + setfract(&thisvoice->meter, t->meter.num, t->meter.denom); + thisvoice->place = thisvoice->first; + thisvoice = nextitem(&t->voices); + }; + if (separate_voices) { + thisvoice = firstitem(&t->voices); + while (thisvoice != NULL) { + doneline = 1; + while (doneline==1) { + doneline = printvoiceline(thisvoice); + }; + thisvoice = nextitem(&t->voices); + if (thisvoice != NULL) { + voicedivider(); + }; + }; + } else { + doneline = 1; + while(doneline) { + thisvoice = firstitem(&t->voices); + firstvoice = thisvoice; + doneline = 0; + while (thisvoice != NULL) { + doneline = (printvoiceline(thisvoice) || doneline); + if (thisvoice == firstvoice) { + firstvline = totlen; + } else { + lastvline = totlen; + if (lastvline > firstvline) { + fprintf(f, "0 24 M 0 %.1f L\n", + lastvline - firstvline); + }; + firstvline = totlen; + }; + thisvoice = nextitem(&t->voices); + }; + }; + }; + /* musicrestore(); */ + wordline = firstitem(&t->words); + while (wordline != NULL) { + printtext(left, wordline, &wordsfont); + wordline = nextitem(&t->words); + }; + if (eps_out) { + close_output_file(); + }; +} + diff -Nru abcmidi-20200706/drawtune.h abcmidi-20200728/drawtune.h --- abcmidi-20200706/drawtune.h 2008-05-30 23:22:22.000000000 +0000 +++ abcmidi-20200728/drawtune.h 2020-07-27 15:52:43.000000000 +0000 @@ -1,29 +1,29 @@ -/* drawtune.h - part of yaps */ -/* this file declares the routines in drawtune.c that are used elsewhere */ - -/* for Microsoft Visual C++ version 6.0 or higher */ - -extern int eps_out; -/* bounding box for encapsulated PostScript */ -struct bbox { - int llx, lly, urx, ury; -}; -#ifdef ANSILIBS -extern void setmargins(char* s); -extern void setpagesize(char* s); -extern void open_output_file(char* filename, struct bbox* boundingbox); -extern void close_output_file(void); -extern void newpage(void); -extern void centretext(char* s); -extern void lefttext(char* s); -extern void vskip(double gap); -#else -extern void setmargins(); -extern void setpagesize(); -extern void open_output_file(); -extern void close_output_file(); -extern void newpage(); -extern void centretext(); -extern void lefttext(); -extern void vskip(); -#endif +/* drawtune.h - part of yaps */ +/* this file declares the routines in drawtune.c that are used elsewhere */ + +/* for Microsoft Visual C++ version 6.0 or higher */ + +extern int eps_out; +/* bounding box for encapsulated PostScript */ +struct bbox { + int llx, lly, urx, ury; +}; +#ifdef ANSILIBS +extern void setmargins(char* s); +extern void setpagesize(char* s); +extern void open_output_file(char* filename, struct bbox* boundingbox); +extern void close_output_file(void); +extern void newpage(void); +extern void centretext(char* s); +extern void lefttext(char* s); +extern void vskip(double gap); +#else +extern void setmargins(); +extern void setpagesize(); +extern void open_output_file(); +extern void close_output_file(); +extern void newpage(); +extern void centretext(); +extern void lefttext(); +extern void vskip(); +#endif diff -Nru abcmidi-20200706/genmidi.h abcmidi-20200728/genmidi.h --- abcmidi-20200706/genmidi.h 2015-03-27 17:24:54.000000000 +0000 +++ abcmidi-20200728/genmidi.h 2020-07-27 15:52:43.000000000 +0000 @@ -1,39 +1,39 @@ -/* genmidi.h - part of abc2midi */ -/* function prototypes for functions in genmidi.c used elsewhere */ - -#ifndef KANDR -/* functions required by store.c */ -extern void reduce(int* a, int* b); -extern void set_meter(int a, int b); -extern void set_gchords(char *s); -extern void set_drums(char *s); -extern void addunits(int a, int b); -/* required by queues.c */ -extern void midi_noteoff(long delta_time, int pitch, int chan); -extern void progress_sequence(int i); -#else -/* functions required by store.c */ -extern void reduce(); -extern void set_meter(); -extern void set_gchords(); -extern void addunits(); -extern void set_drums(); -/* required by queues.c */ -extern void midi_noteoff(); -extern void progress_sequence(); -#endif - - -/* introduced 2010-02-01 (feb 01) [SS] */ -struct trackstruct {enum {NOTES, WORDS, NOTEWORDS, GCHORDS, DRUMS, DRONE} tracktype; - int voicenum; - int midichannel; /* [SS] 2015-03-24 */ - }; - - - - -/* some definitions formerly in tomidi.c */ -#define DIV 480 -#define MAXPARTS 100 -#define MAXCHORDNAMES 80 +/* genmidi.h - part of abc2midi */ +/* function prototypes for functions in genmidi.c used elsewhere */ + +#ifndef KANDR +/* functions required by store.c */ +extern void reduce(int* a, int* b); +extern void set_meter(int a, int b); +extern void set_gchords(char *s); +extern void set_drums(char *s); +extern void addunits(int a, int b); +/* required by queues.c */ +extern void midi_noteoff(long delta_time, int pitch, int chan); +extern void progress_sequence(int i); +#else +/* functions required by store.c */ +extern void reduce(); +extern void set_meter(); +extern void set_gchords(); +extern void addunits(); +extern void set_drums(); +/* required by queues.c */ +extern void midi_noteoff(); +extern void progress_sequence(); +#endif + + +/* introduced 2010-02-01 (feb 01) [SS] */ +struct trackstruct {enum {NOTES, WORDS, NOTEWORDS, GCHORDS, DRUMS, DRONE} tracktype; + int voicenum; + int midichannel; /* [SS] 2015-03-24 */ + }; + + + + +/* some definitions formerly in tomidi.c */ +#define DIV 480 +#define MAXPARTS 100 +#define MAXCHORDNAMES 80 diff -Nru abcmidi-20200706/legacy_code/casecmp.c abcmidi-20200728/legacy_code/casecmp.c --- abcmidi-20200706/legacy_code/casecmp.c 2008-05-30 23:22:22.000000000 +0000 +++ abcmidi-20200728/legacy_code/casecmp.c 2020-07-27 15:52:43.000000000 +0000 @@ -1,69 +1,69 @@ -static int casecmp(s1, s2) -/* case-insensitive compare 2 strings */ -/* return 0 if equal */ -/* 1 if s1 > s2 */ -/* -1 if s1 > s2 */ -char s1[]; -char s2[]; -{ - int i, val, done; - char c1, c2; - - i = 0; - done = 0; - while (done == 0) { - c1 = tolower(s1[i]); - c2 = tolower(s2[i]); - if (c1 > c2) { - val = 1; - done = 1; - } else { - if (c1 < c2) { - val = -1; - done = 1; - } else { - if (c1 == '\0') { - val = 0; - done = 1; - } else { - i = i + 1; - }; - }; - }; - }; - return(val); -} - -static int stringcmp(s1, s2) -/* case sensitive compare 2 strings */ -/* return 0 if equal */ -/* 1 if s1 > s2 */ -/* -1 if s1 > s2 */ -char s1[]; -char s2[]; -{ - int i, val, done; - - i = 0; - done = 0; - while (done == 0) { - if (s1[i] > s2[i]) { - val = 1; - done = 1; - } else { - if (s1[i] < s2[i]) { - val = -1; - done = 1; - } else { - if (s1[i] == '\0') { - val = 0; - done = 1; - } else { - i = i + 1; - }; - }; - }; - }; - return(val); -} - +static int casecmp(s1, s2) +/* case-insensitive compare 2 strings */ +/* return 0 if equal */ +/* 1 if s1 > s2 */ +/* -1 if s1 > s2 */ +char s1[]; +char s2[]; +{ + int i, val, done; + char c1, c2; + + i = 0; + done = 0; + while (done == 0) { + c1 = tolower(s1[i]); + c2 = tolower(s2[i]); + if (c1 > c2) { + val = 1; + done = 1; + } else { + if (c1 < c2) { + val = -1; + done = 1; + } else { + if (c1 == '\0') { + val = 0; + done = 1; + } else { + i = i + 1; + }; + }; + }; + }; + return(val); +} + +static int stringcmp(s1, s2) +/* case sensitive compare 2 strings */ +/* return 0 if equal */ +/* 1 if s1 > s2 */ +/* -1 if s1 > s2 */ +char s1[]; +char s2[]; +{ + int i, val, done; + + i = 0; + done = 0; + while (done == 0) { + if (s1[i] > s2[i]) { + val = 1; + done = 1; + } else { + if (s1[i] < s2[i]) { + val = -1; + done = 1; + } else { + if (s1[i] == '\0') { + val = 0; + done = 1; + } else { + i = i + 1; + }; + }; + }; + }; + return(val); +} + diff -Nru abcmidi-20200706/Makefile abcmidi-20200728/Makefile --- abcmidi-20200706/Makefile 2018-01-25 13:03:53.000000000 +0000 +++ abcmidi-20200728/Makefile 2020-07-27 15:52:44.000000000 +0000 @@ -1,174 +1,174 @@ -# Generic unix/gcc Makefile for abcMIDI package -# -# -# compilation #ifdefs - you need to compile with these defined to get -# the code to compile with PCC. -# -# NOFTELL in midifile.c and genmidi.c selects a version of the file-writing -# code which doesn't use file seeking. -# -# PCCFIX in mftext.c midifile.c midi2abc.c -# comments out various things that aren't available in PCC -# -# ANSILIBS includes some ANSI header files (which gcc can live without, -# but other compilers may want). -# -# USE_INDEX causes index() to be used instead of strchr(). This is needed -# by some pre-ANSI C compilers. -# -# ASCTIME causes asctime() to be used instead of strftime() in pslib.c. -# If ANSILIBS is not set, neither routine is used. -# -# KANDR selects functions prototypes without argument prototypes. -# currently yaps will only compile in ANSI mode. -# -# -# On running make, you may get the mysterious message : -# -# ', needed by `parseabc.o'. Stop `abc.h -# -# This means you are using GNU make and this file is in DOS text format. To -# cure the problem, change this file from using PC-style end-of-line (carriage -# return and line feed) to unix style end-of-line (line feed). - -VERSION = @VERSION@ - -CC = gcc -INSTALL = /usr/bin/install -c -INSTALL_DATA = ${INSTALL} -m 644 -INSTALL_PROGRAM = ${INSTALL} - -CFLAGS = -DANSILIBS -O2 -CPPFLAGS = -DHAVE_CONFIG_H -I. -LDFLAGS = -lm - -prefix = /usr/local -exec_prefix = ${prefix} - -srcdir = . -VPATH = . -bindir = ${exec_prefix}/bin -libdir = ${exec_prefix}/lib -datadir = ${prefix}/share -docdir = ${prefix}/share/doc/abcmidi -mandir = ${prefix}/share/man/man1 - -binaries=abc2midi midi2abc abc2abc mftext yaps midicopy abcmatch - -all : abc2midi midi2abc abc2abc mftext yaps midicopy abcmatch - -OBJECTS_ABC2MIDI=parseabc.o store.o genmidi.o midifile.o queues.o parser2.o stresspat.o -abc2midi : $(OBJECTS_ABC2MIDI) - $(CC) $(CFLAGS) -o abc2midi $(OBJECTS_ABC2MIDI) $(LDFLAGS) -lm -$(OBJECTS_ABC2MIDI): abc.h parseabc.h config.h Makefile - -OBJECTS_ABC2ABC=parseabc.o toabc.o -abc2abc : $(OBJECTS_ABC2ABC) - $(CC) $(CFLAGS) -o abc2abc $(OBJECTS_ABC2ABC) $(LDFLAGS) -$(OBJECTS_ABC2ABC): abc.h parseabc.h config.h Makefile - -OBJECTS_MIDI2ABC=midifile.o midi2abc.o -midi2abc : $(OBJECTS_MIDI2ABC) - $(CC) $(CFLAGS) -o midi2abc $(OBJECTS_MIDI2ABC) $(LDFLAGS) -$(OBJECTS_MIDI2ABC): abc.h midifile.h config.h Makefile - -OBJECTS_MFTEXT=midifile.o mftext.o crack.o -mftext : $(OBJECTS_MFTEXT) - $(CC) $(CFLAGS) -o mftext $(OBJECTS_MFTEXT) $(LDFLAGS) -$(OBJECTS_MFTEXT): abc.h midifile.h config.h Makefile - -OBJECTS_YAPS=parseabc.o yapstree.o drawtune.o debug.o pslib.o position.o parser2.o -yaps : $(OBJECTS_YAPS) - $(CC) $(CFLAGS) -o yaps $(OBJECTS_YAPS) $(LDFLAGS) -$(OBJECTS_YAPS): abc.h midifile.h config.h Makefile - -OBJECTS_MIDICOPY=midicopy.o -midicopy : $(OBJECTS_MIDICOPY) - $(CC) $(CFLAGS) -o midicopy $(OBJECTS_MIDICOPY) $(LDFLAGS) -$(OBJECTS_MIDICOPY): abc.h midifile.h midicopy.h config.h Makefile - -OBJECTS_ABCMATCH=abcmatch.o matchsup.o parseabc.o -abcmatch : $(OBJECTS_ABCMATCH) - $(CC) $(CFLAGS) -o abcmatch $(OBJECTS_ABCMATCH) $(LDFLAGS) -$(OBJECTS_ABCMATCH): abc.h midifile.h config.h Makefile - -parseabc.o : parseabc.c abc.h parseabc.h - -parser2.o : parser2.c abc.h parseabc.h parser2.h - -toabc.o : toabc.c abc.h parseabc.h - -# could use -DNOFTELL here -genmidi.o : genmidi.c abc.h midifile.h genmidi.h - -stresspat.o : stresspat.c - -store.o : store.c abc.h parseabc.h midifile.h genmidi.h - -queues.o : queues.c genmidi.h - -# could use -DNOFTELL here -midifile.o : midifile.c midifile.h - -midi2abc.o : midi2abc.c midifile.h - -midicopy.o : midicopy.c midicopy.h - -abcmatch.o: abcmatch.c abc.h - -crack.o : crack.c - -mftext.o : mftext.c midifile.h - -# objects needed by yaps -# -yapstree.o: yapstree.c abc.h parseabc.h structs.h drawtune.h - -drawtune.o: drawtune.c structs.h sizes.h abc.h drawtune.h - -pslib.o: pslib.c drawtune.h - -position.o: position.c abc.h structs.h sizes.h - -debug.o: debug.c structs.h abc.h - -#objects for abcmatch -# -matchsup.o : matchsup.c abc.h parseabc.h parser2.h - -clean : - rm *.o ${binaries} - -install: abc2midi midi2abc abc2abc mftext midicopy yaps abcmatch - $(INSTALL) -d $(DESTDIR)$(bindir) - $(INSTALL) -m 755 ${binaries} $(DESTDIR)$(bindir) - - # install documentation - $(INSTALL) -d $(DESTDIR)${docdir} - $(INSTALL) -m 644 doc/*.txt $(DESTDIR)$(docdir) - $(INSTALL) -m 644 doc/AUTHORS $(DESTDIR)$(docdir) - $(INSTALL) -m 644 doc/CHANGES $(DESTDIR)$(docdir) - $(INSTALL) -m 644 VERSION $(DESTDIR)$(docdir) - - # install manpages - $(INSTALL) -d $(DESTDIR)${mandir} - $(INSTALL) -m 644 doc/*.1 $(DESTDIR)$(mandir) - - -uninstall: - echo "uninstalling..."; - #rm -f $(DESTDIR)$(bindir)/$(binaries) - rm -f $(DESTDIR)$(bindir)/abc2midi - rm -f $(DESTDIR)$(bindir)/abc2abc - rm -f $(DESTDIR)$(bindir)/yaps - rm -f $(DESTDIR)$(bindir)/midi2abc - rm -f $(DESTDIR)$(bindir)/mftext - rm -f $(DESTDIR)$(bindir)/abcmatch - rm -f $(DESTDIR)$(bindir)/midicopy - rm -f $(DESTDIR)$(docdir)/*.txt - rm -f $(DESTDIR)$(docdir)/AUTHORS - rm -f $(DESTDIR)$(docdir)/CHANGES - rm -f $(DESTDIR)$(docdir)/VERSION - rm -f $(DESTDIR)$(mandir)/*.1 - rmdir $(DESTDIR)$(docdir) - +# Generic unix/gcc Makefile for abcMIDI package +# +# +# compilation #ifdefs - you need to compile with these defined to get +# the code to compile with PCC. +# +# NOFTELL in midifile.c and genmidi.c selects a version of the file-writing +# code which doesn't use file seeking. +# +# PCCFIX in mftext.c midifile.c midi2abc.c +# comments out various things that aren't available in PCC +# +# ANSILIBS includes some ANSI header files (which gcc can live without, +# but other compilers may want). +# +# USE_INDEX causes index() to be used instead of strchr(). This is needed +# by some pre-ANSI C compilers. +# +# ASCTIME causes asctime() to be used instead of strftime() in pslib.c. +# If ANSILIBS is not set, neither routine is used. +# +# KANDR selects functions prototypes without argument prototypes. +# currently yaps will only compile in ANSI mode. +# +# +# On running make, you may get the mysterious message : +# +# ', needed by `parseabc.o'. Stop `abc.h +# +# This means you are using GNU make and this file is in DOS text format. To +# cure the problem, change this file from using PC-style end-of-line (carriage +# return and line feed) to unix style end-of-line (line feed). + +VERSION = @VERSION@ + +CC = gcc +INSTALL = /usr/bin/install -c +INSTALL_DATA = ${INSTALL} -m 644 +INSTALL_PROGRAM = ${INSTALL} + +CFLAGS = -DANSILIBS -O2 +CPPFLAGS = -DHAVE_CONFIG_H -I. +LDFLAGS = -lm + +prefix = /usr/local +exec_prefix = ${prefix} + +srcdir = . +VPATH = . +bindir = ${exec_prefix}/bin +libdir = ${exec_prefix}/lib +datadir = ${prefix}/share +docdir = ${prefix}/share/doc/abcmidi +mandir = ${prefix}/share/man/man1 + +binaries=abc2midi midi2abc abc2abc mftext yaps midicopy abcmatch + +all : abc2midi midi2abc abc2abc mftext yaps midicopy abcmatch + +OBJECTS_ABC2MIDI=parseabc.o store.o genmidi.o midifile.o queues.o parser2.o stresspat.o +abc2midi : $(OBJECTS_ABC2MIDI) + $(CC) $(CFLAGS) -o abc2midi $(OBJECTS_ABC2MIDI) $(LDFLAGS) -lm +$(OBJECTS_ABC2MIDI): abc.h parseabc.h config.h Makefile + +OBJECTS_ABC2ABC=parseabc.o toabc.o +abc2abc : $(OBJECTS_ABC2ABC) + $(CC) $(CFLAGS) -o abc2abc $(OBJECTS_ABC2ABC) $(LDFLAGS) +$(OBJECTS_ABC2ABC): abc.h parseabc.h config.h Makefile + +OBJECTS_MIDI2ABC=midifile.o midi2abc.o +midi2abc : $(OBJECTS_MIDI2ABC) + $(CC) $(CFLAGS) -o midi2abc $(OBJECTS_MIDI2ABC) $(LDFLAGS) +$(OBJECTS_MIDI2ABC): abc.h midifile.h config.h Makefile + +OBJECTS_MFTEXT=midifile.o mftext.o crack.o +mftext : $(OBJECTS_MFTEXT) + $(CC) $(CFLAGS) -o mftext $(OBJECTS_MFTEXT) $(LDFLAGS) +$(OBJECTS_MFTEXT): abc.h midifile.h config.h Makefile + +OBJECTS_YAPS=parseabc.o yapstree.o drawtune.o debug.o pslib.o position.o parser2.o +yaps : $(OBJECTS_YAPS) + $(CC) $(CFLAGS) -o yaps $(OBJECTS_YAPS) $(LDFLAGS) +$(OBJECTS_YAPS): abc.h midifile.h config.h Makefile + +OBJECTS_MIDICOPY=midicopy.o +midicopy : $(OBJECTS_MIDICOPY) + $(CC) $(CFLAGS) -o midicopy $(OBJECTS_MIDICOPY) $(LDFLAGS) +$(OBJECTS_MIDICOPY): abc.h midifile.h midicopy.h config.h Makefile + +OBJECTS_ABCMATCH=abcmatch.o matchsup.o parseabc.o +abcmatch : $(OBJECTS_ABCMATCH) + $(CC) $(CFLAGS) -o abcmatch $(OBJECTS_ABCMATCH) $(LDFLAGS) +$(OBJECTS_ABCMATCH): abc.h midifile.h config.h Makefile + +parseabc.o : parseabc.c abc.h parseabc.h + +parser2.o : parser2.c abc.h parseabc.h parser2.h + +toabc.o : toabc.c abc.h parseabc.h + +# could use -DNOFTELL here +genmidi.o : genmidi.c abc.h midifile.h genmidi.h + +stresspat.o : stresspat.c + +store.o : store.c abc.h parseabc.h midifile.h genmidi.h + +queues.o : queues.c genmidi.h + +# could use -DNOFTELL here +midifile.o : midifile.c midifile.h + +midi2abc.o : midi2abc.c midifile.h + +midicopy.o : midicopy.c midicopy.h + +abcmatch.o: abcmatch.c abc.h + +crack.o : crack.c + +mftext.o : mftext.c midifile.h + +# objects needed by yaps +# +yapstree.o: yapstree.c abc.h parseabc.h structs.h drawtune.h + +drawtune.o: drawtune.c structs.h sizes.h abc.h drawtune.h + +pslib.o: pslib.c drawtune.h + +position.o: position.c abc.h structs.h sizes.h + +debug.o: debug.c structs.h abc.h + +#objects for abcmatch +# +matchsup.o : matchsup.c abc.h parseabc.h parser2.h + +clean : + rm *.o ${binaries} + +install: abc2midi midi2abc abc2abc mftext midicopy yaps abcmatch + $(INSTALL) -d $(DESTDIR)$(bindir) + $(INSTALL) -m 755 ${binaries} $(DESTDIR)$(bindir) + + # install documentation + $(INSTALL) -d $(DESTDIR)${docdir} + $(INSTALL) -m 644 doc/*.txt $(DESTDIR)$(docdir) + $(INSTALL) -m 644 doc/AUTHORS $(DESTDIR)$(docdir) + $(INSTALL) -m 644 doc/CHANGES $(DESTDIR)$(docdir) + $(INSTALL) -m 644 VERSION $(DESTDIR)$(docdir) + + # install manpages + $(INSTALL) -d $(DESTDIR)${mandir} + $(INSTALL) -m 644 doc/*.1 $(DESTDIR)$(mandir) + + +uninstall: + echo "uninstalling..."; + #rm -f $(DESTDIR)$(bindir)/$(binaries) + rm -f $(DESTDIR)$(bindir)/abc2midi + rm -f $(DESTDIR)$(bindir)/abc2abc + rm -f $(DESTDIR)$(bindir)/yaps + rm -f $(DESTDIR)$(bindir)/midi2abc + rm -f $(DESTDIR)$(bindir)/mftext + rm -f $(DESTDIR)$(bindir)/abcmatch + rm -f $(DESTDIR)$(bindir)/midicopy + rm -f $(DESTDIR)$(docdir)/*.txt + rm -f $(DESTDIR)$(docdir)/AUTHORS + rm -f $(DESTDIR)$(docdir)/CHANGES + rm -f $(DESTDIR)$(docdir)/VERSION + rm -f $(DESTDIR)$(mandir)/*.1 + rmdir $(DESTDIR)$(docdir) + diff -Nru abcmidi-20200706/Makefile.in abcmidi-20200728/Makefile.in --- abcmidi-20200706/Makefile.in 2015-10-04 08:44:54.000000000 +0000 +++ abcmidi-20200728/Makefile.in 2020-07-27 15:52:43.000000000 +0000 @@ -1,174 +1,174 @@ -# Generic unix/gcc Makefile for abcMIDI package -# -# -# compilation #ifdefs - you need to compile with these defined to get -# the code to compile with PCC. -# -# NOFTELL in midifile.c and genmidi.c selects a version of the file-writing -# code which doesn't use file seeking. -# -# PCCFIX in mftext.c midifile.c midi2abc.c -# comments out various things that aren't available in PCC -# -# ANSILIBS includes some ANSI header files (which gcc can live without, -# but other compilers may want). -# -# USE_INDEX causes index() to be used instead of strchr(). This is needed -# by some pre-ANSI C compilers. -# -# ASCTIME causes asctime() to be used instead of strftime() in pslib.c. -# If ANSILIBS is not set, neither routine is used. -# -# KANDR selects functions prototypes without argument prototypes. -# currently yaps will only compile in ANSI mode. -# -# -# On running make, you may get the mysterious message : -# -# ', needed by `parseabc.o'. Stop `abc.h -# -# This means you are using GNU make and this file is in DOS text format. To -# cure the problem, change this file from using PC-style end-of-line (carriage -# return and line feed) to unix style end-of-line (line feed). - -VERSION = @VERSION@ - -CC = @CC@ -INSTALL = @INSTALL@ -INSTALL_DATA = @INSTALL_DATA@ -INSTALL_PROGRAM = @INSTALL_PROGRAM@ - -CFLAGS = -DANSILIBS @CFLAGS@ -CPPFLAGS = @DEFS@ @CPPFLAGS@ -I. -LDFLAGS = @LDFLAGS@ -lm - -prefix = @prefix@ -exec_prefix = @exec_prefix@ - -srcdir = @srcdir@ -VPATH = @srcdir@ -bindir = @bindir@ -libdir = @libdir@ -datadir = @datarootdir@ -docdir = @datarootdir@/doc/abcmidi -mandir = @datarootdir@/man/man1 - -binaries=abc2midi midi2abc abc2abc mftext yaps midicopy abcmatch - -all : abc2midi midi2abc abc2abc mftext yaps midicopy abcmatch - -OBJECTS_ABC2MIDI=parseabc.o store.o genmidi.o midifile.o queues.o parser2.o stresspat.o -abc2midi : $(OBJECTS_ABC2MIDI) - $(CC) $(CFLAGS) -o abc2midi $(OBJECTS_ABC2MIDI) $(LDFLAGS) -lm -$(OBJECTS_ABC2MIDI): abc.h parseabc.h config.h Makefile - -OBJECTS_ABC2ABC=parseabc.o toabc.o -abc2abc : $(OBJECTS_ABC2ABC) - $(CC) $(CFLAGS) -o abc2abc $(OBJECTS_ABC2ABC) $(LDFLAGS) -$(OBJECTS_ABC2ABC): abc.h parseabc.h config.h Makefile - -OBJECTS_MIDI2ABC=midifile.o midi2abc.o -midi2abc : $(OBJECTS_MIDI2ABC) - $(CC) $(CFLAGS) -o midi2abc $(OBJECTS_MIDI2ABC) $(LDFLAGS) -$(OBJECTS_MIDI2ABC): abc.h midifile.h config.h Makefile - -OBJECTS_MFTEXT=midifile.o mftext.o crack.o -mftext : $(OBJECTS_MFTEXT) - $(CC) $(CFLAGS) -o mftext $(OBJECTS_MFTEXT) $(LDFLAGS) -$(OBJECTS_MFTEXT): abc.h midifile.h config.h Makefile - -OBJECTS_YAPS=parseabc.o yapstree.o drawtune.o debug.o pslib.o position.o parser2.o -yaps : $(OBJECTS_YAPS) - $(CC) $(CFLAGS) -o yaps $(OBJECTS_YAPS) $(LDFLAGS) -$(OBJECTS_YAPS): abc.h midifile.h config.h Makefile - -OBJECTS_MIDICOPY=midicopy.o -midicopy : $(OBJECTS_MIDICOPY) - $(CC) $(CFLAGS) -o midicopy $(OBJECTS_MIDICOPY) $(LDFLAGS) -$(OBJECTS_MIDICOPY): abc.h midifile.h midicopy.h config.h Makefile - -OBJECTS_ABCMATCH=abcmatch.o matchsup.o parseabc.o -abcmatch : $(OBJECTS_ABCMATCH) - $(CC) $(CFLAGS) -o abcmatch $(OBJECTS_ABCMATCH) $(LDFLAGS) -$(OBJECTS_ABCMATCH): abc.h midifile.h config.h Makefile - -parseabc.o : parseabc.c abc.h parseabc.h - -parser2.o : parser2.c abc.h parseabc.h parser2.h - -toabc.o : toabc.c abc.h parseabc.h - -# could use -DNOFTELL here -genmidi.o : genmidi.c abc.h midifile.h genmidi.h - -stresspat.o : stresspat.c - -store.o : store.c abc.h parseabc.h midifile.h genmidi.h - -queues.o : queues.c genmidi.h - -# could use -DNOFTELL here -midifile.o : midifile.c midifile.h - -midi2abc.o : midi2abc.c midifile.h - -midicopy.o : midicopy.c midicopy.h - -abcmatch.o: abcmatch.c abc.h - -crack.o : crack.c - -mftext.o : mftext.c midifile.h - -# objects needed by yaps -# -yapstree.o: yapstree.c abc.h parseabc.h structs.h drawtune.h - -drawtune.o: drawtune.c structs.h sizes.h abc.h drawtune.h - -pslib.o: pslib.c drawtune.h - -position.o: position.c abc.h structs.h sizes.h - -debug.o: debug.c structs.h abc.h - -#objects for abcmatch -# -matchsup.o : matchsup.c abc.h parseabc.h parser2.h - -clean : - rm *.o ${binaries} - -install: abc2midi midi2abc abc2abc mftext midicopy yaps abcmatch - $(INSTALL) -d $(DESTDIR)$(bindir) - $(INSTALL) -m 755 ${binaries} $(DESTDIR)$(bindir) - - # install documentation - $(INSTALL) -d $(DESTDIR)${docdir} - $(INSTALL) -m 644 doc/*.txt $(DESTDIR)$(docdir) - $(INSTALL) -m 644 doc/AUTHORS $(DESTDIR)$(docdir) - $(INSTALL) -m 644 doc/CHANGES $(DESTDIR)$(docdir) - $(INSTALL) -m 644 VERSION $(DESTDIR)$(docdir) - - # install manpages - $(INSTALL) -d $(DESTDIR)${mandir} - $(INSTALL) -m 644 doc/*.1 $(DESTDIR)$(mandir) - - -uninstall: - echo "uninstalling..."; - #rm -f $(DESTDIR)$(bindir)/$(binaries) - rm -f $(DESTDIR)$(bindir)/abc2midi - rm -f $(DESTDIR)$(bindir)/abc2abc - rm -f $(DESTDIR)$(bindir)/yaps - rm -f $(DESTDIR)$(bindir)/midi2abc - rm -f $(DESTDIR)$(bindir)/mftext - rm -f $(DESTDIR)$(bindir)/abcmatch - rm -f $(DESTDIR)$(bindir)/midicopy - rm -f $(DESTDIR)$(docdir)/*.txt - rm -f $(DESTDIR)$(docdir)/AUTHORS - rm -f $(DESTDIR)$(docdir)/CHANGES - rm -f $(DESTDIR)$(docdir)/VERSION - rm -f $(DESTDIR)$(mandir)/*.1 - rmdir $(DESTDIR)$(docdir) - +# Generic unix/gcc Makefile for abcMIDI package +# +# +# compilation #ifdefs - you need to compile with these defined to get +# the code to compile with PCC. +# +# NOFTELL in midifile.c and genmidi.c selects a version of the file-writing +# code which doesn't use file seeking. +# +# PCCFIX in mftext.c midifile.c midi2abc.c +# comments out various things that aren't available in PCC +# +# ANSILIBS includes some ANSI header files (which gcc can live without, +# but other compilers may want). +# +# USE_INDEX causes index() to be used instead of strchr(). This is needed +# by some pre-ANSI C compilers. +# +# ASCTIME causes asctime() to be used instead of strftime() in pslib.c. +# If ANSILIBS is not set, neither routine is used. +# +# KANDR selects functions prototypes without argument prototypes. +# currently yaps will only compile in ANSI mode. +# +# +# On running make, you may get the mysterious message : +# +# ', needed by `parseabc.o'. Stop `abc.h +# +# This means you are using GNU make and this file is in DOS text format. To +# cure the problem, change this file from using PC-style end-of-line (carriage +# return and line feed) to unix style end-of-line (line feed). + +VERSION = @VERSION@ + +CC = @CC@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ + +CFLAGS = -DANSILIBS @CFLAGS@ +CPPFLAGS = @DEFS@ @CPPFLAGS@ -I. +LDFLAGS = @LDFLAGS@ -lm + +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +srcdir = @srcdir@ +VPATH = @srcdir@ +bindir = @bindir@ +libdir = @libdir@ +datadir = @datarootdir@ +docdir = @datarootdir@/doc/abcmidi +mandir = @datarootdir@/man/man1 + +binaries=abc2midi midi2abc abc2abc mftext yaps midicopy abcmatch + +all : abc2midi midi2abc abc2abc mftext yaps midicopy abcmatch + +OBJECTS_ABC2MIDI=parseabc.o store.o genmidi.o midifile.o queues.o parser2.o stresspat.o +abc2midi : $(OBJECTS_ABC2MIDI) + $(CC) $(CFLAGS) -o abc2midi $(OBJECTS_ABC2MIDI) $(LDFLAGS) -lm +$(OBJECTS_ABC2MIDI): abc.h parseabc.h config.h Makefile + +OBJECTS_ABC2ABC=parseabc.o toabc.o +abc2abc : $(OBJECTS_ABC2ABC) + $(CC) $(CFLAGS) -o abc2abc $(OBJECTS_ABC2ABC) $(LDFLAGS) +$(OBJECTS_ABC2ABC): abc.h parseabc.h config.h Makefile + +OBJECTS_MIDI2ABC=midifile.o midi2abc.o +midi2abc : $(OBJECTS_MIDI2ABC) + $(CC) $(CFLAGS) -o midi2abc $(OBJECTS_MIDI2ABC) $(LDFLAGS) +$(OBJECTS_MIDI2ABC): abc.h midifile.h config.h Makefile + +OBJECTS_MFTEXT=midifile.o mftext.o crack.o +mftext : $(OBJECTS_MFTEXT) + $(CC) $(CFLAGS) -o mftext $(OBJECTS_MFTEXT) $(LDFLAGS) +$(OBJECTS_MFTEXT): abc.h midifile.h config.h Makefile + +OBJECTS_YAPS=parseabc.o yapstree.o drawtune.o debug.o pslib.o position.o parser2.o +yaps : $(OBJECTS_YAPS) + $(CC) $(CFLAGS) -o yaps $(OBJECTS_YAPS) $(LDFLAGS) +$(OBJECTS_YAPS): abc.h midifile.h config.h Makefile + +OBJECTS_MIDICOPY=midicopy.o +midicopy : $(OBJECTS_MIDICOPY) + $(CC) $(CFLAGS) -o midicopy $(OBJECTS_MIDICOPY) $(LDFLAGS) +$(OBJECTS_MIDICOPY): abc.h midifile.h midicopy.h config.h Makefile + +OBJECTS_ABCMATCH=abcmatch.o matchsup.o parseabc.o +abcmatch : $(OBJECTS_ABCMATCH) + $(CC) $(CFLAGS) -o abcmatch $(OBJECTS_ABCMATCH) $(LDFLAGS) +$(OBJECTS_ABCMATCH): abc.h midifile.h config.h Makefile + +parseabc.o : parseabc.c abc.h parseabc.h + +parser2.o : parser2.c abc.h parseabc.h parser2.h + +toabc.o : toabc.c abc.h parseabc.h + +# could use -DNOFTELL here +genmidi.o : genmidi.c abc.h midifile.h genmidi.h + +stresspat.o : stresspat.c + +store.o : store.c abc.h parseabc.h midifile.h genmidi.h + +queues.o : queues.c genmidi.h + +# could use -DNOFTELL here +midifile.o : midifile.c midifile.h + +midi2abc.o : midi2abc.c midifile.h + +midicopy.o : midicopy.c midicopy.h + +abcmatch.o: abcmatch.c abc.h + +crack.o : crack.c + +mftext.o : mftext.c midifile.h + +# objects needed by yaps +# +yapstree.o: yapstree.c abc.h parseabc.h structs.h drawtune.h + +drawtune.o: drawtune.c structs.h sizes.h abc.h drawtune.h + +pslib.o: pslib.c drawtune.h + +position.o: position.c abc.h structs.h sizes.h + +debug.o: debug.c structs.h abc.h + +#objects for abcmatch +# +matchsup.o : matchsup.c abc.h parseabc.h parser2.h + +clean : + rm *.o ${binaries} + +install: abc2midi midi2abc abc2abc mftext midicopy yaps abcmatch + $(INSTALL) -d $(DESTDIR)$(bindir) + $(INSTALL) -m 755 ${binaries} $(DESTDIR)$(bindir) + + # install documentation + $(INSTALL) -d $(DESTDIR)${docdir} + $(INSTALL) -m 644 doc/*.txt $(DESTDIR)$(docdir) + $(INSTALL) -m 644 doc/AUTHORS $(DESTDIR)$(docdir) + $(INSTALL) -m 644 doc/CHANGES $(DESTDIR)$(docdir) + $(INSTALL) -m 644 VERSION $(DESTDIR)$(docdir) + + # install manpages + $(INSTALL) -d $(DESTDIR)${mandir} + $(INSTALL) -m 644 doc/*.1 $(DESTDIR)$(mandir) + + +uninstall: + echo "uninstalling..."; + #rm -f $(DESTDIR)$(bindir)/$(binaries) + rm -f $(DESTDIR)$(bindir)/abc2midi + rm -f $(DESTDIR)$(bindir)/abc2abc + rm -f $(DESTDIR)$(bindir)/yaps + rm -f $(DESTDIR)$(bindir)/midi2abc + rm -f $(DESTDIR)$(bindir)/mftext + rm -f $(DESTDIR)$(bindir)/abcmatch + rm -f $(DESTDIR)$(bindir)/midicopy + rm -f $(DESTDIR)$(docdir)/*.txt + rm -f $(DESTDIR)$(docdir)/AUTHORS + rm -f $(DESTDIR)$(docdir)/CHANGES + rm -f $(DESTDIR)$(docdir)/VERSION + rm -f $(DESTDIR)$(mandir)/*.1 + rmdir $(DESTDIR)$(docdir) + diff -Nru abcmidi-20200706/makefiles/djgpp.mak abcmidi-20200728/makefiles/djgpp.mak --- abcmidi-20200706/makefiles/djgpp.mak 2018-01-25 13:08:12.000000000 +0000 +++ abcmidi-20200728/makefiles/djgpp.mak 2020-07-27 15:52:43.000000000 +0000 @@ -1,146 +1,146 @@ -# DJGPP (DOS port of gcc) Makefile for abcMIDI package -# -# -# compilation #ifdefs - you may need to change these defined to get -# the code to compile with a different C compiler. -# -# NOFTELL in midifile.c and genmidi.c selects a version of the file-writing -# code which doesn't use file seeking. -# -# PCCFIX in mftext.c midifile.c midi2abc.c -# comments out various things that aren't available in PCC -# -# USE_INDEX causes index() to be used instead of strchr(). This is needed -# by some pre-ANSI C compilers. -# -# ASCTIME causes asctime() to be used instead of strftime() in pslib.c. -# If ANSILIBS is not set, neither routine is used. -# -# ANSILIBS causes code to include some ANSI standard headers -# -# KANDR selects functions prototypes without argument prototypes. -# -# NO_SNPRINTF causes code to use printf instead of snprintf which -# is less secure. -CC=gcc -CFLAGS=-c -ansi -DANSILIBS -DNO_SNPRINTF -Wformat -Wtraditional -# -ansi forces ANSI compliance -LNK=gcc - -all : abc2midi.exe midi2abc.exe abc2abc.exe mftext.exe yaps.exe\ - midicopy.exe abcmatch.exe - -abc2midi.exe : parseabc.o store.o genmidi.o queues.o midifile.o parser2.o - $(LNK) -o abc2midi.exe parseabc.o genmidi.o store.o \ - queues.o midifile.o parser2.o stresspat.o -lm - -abc2abc.exe : parseabc.o toabc.o - $(LNK) -o abc2abc.exe parseabc.o toabc.o - -midi2abc.exe : midifile.o midi2abc.o - $(LNK) midifile.o midi2abc.o -o midi2abc.exe -lm - -mftext.exe : midifile.o mftext.o crack.o - $(LNK) midifile.o mftext.o crack.o -o mftext.exe - -midicopy.exe : midicopy.o - $(LNK) midicopy.o -o midicopy.exe - -abcmatch.exe : abcmatch.o matchsup.o parseabc.o - $(LNK) abcmatch.o matchsup.o parseabc.o -o abcmatch.exe - - - -yaps.exe : parseabc.o yapstree.o drawtune.o debug.o pslib.o position.o parser2.o - $(LNK) -o yaps.exe parseabc.o yapstree.o drawtune.o debug.o \ - position.o pslib.o parser2.o - -# common parser object code -# -parseabc.o : parseabc.c abc.h parseabc.h - $(CC) $(CFLAGS) parseabc.c - -parser2.o : parser2.c parseabc.h parser2.h - $(CC) $(CFLAGS) parser2.c - -# objects needed by abc2abc -# -toabc.o : toabc.c abc.h parseabc.h - $(CC) $(CFLAGS) toabc.c - -# objects needed by abc2midi -# -store.o : store.c abc.h parseabc.h parser2.h genmidi.h - $(CC) $(CFLAGS) store.c - -genmidi.o : genmidi.c abc.h midifile.h genmidi.h - $(CC) $(CFLAGS) genmidi.c - -stresspat.o: stresspat.c - $(CC) $(CFLAGS) stresspat.c - -# could use -DNOFTELL here -tomidi.o : tomidi.c abc.h midifile.h - $(CC) $(CFLAGS) tomidi.c - -queues.o: queues.c genmidi.h - $(CC) $(CFLAGS) queues.c - -midicopy.o: midicopy.c midicopy.h - $(CC) $(CFLAGS) midicopy.c - -abcmatch.o: abcmatch.c abc.h - $(CC) $(CFLAGS) abcmatch.c - - - -# common midifile library -# -# could use -DNOFTELL here -midifile.o : midifile.c midifile.h - $(CC) $(CFLAGS) midifile.c - -# objects needed by yaps -# -yapstree.o: yapstree.c abc.h parseabc.h structs.h drawtune.h parser2.h - $(CC) $(CFLAGS) yapstree.c - -drawtune.o: drawtune.c structs.h sizes.h abc.h drawtune.h - $(CC) $(CFLAGS) drawtune.c - -pslib.o: pslib.c drawtune.h - $(CC) $(CFLAGS) pslib.c - -position.o: position.c abc.h structs.h sizes.h - $(CC) $(CFLAGS) position.c - -debug.o: debug.c structs.h abc.h - $(CC) $(CFLAGS) debug.c - -# objects needed by midi2abc -# -midi2abc.o : midi2abc.c midifile.h - $(CC) $(CFLAGS) midi2abc.c - -# objects for mftext -# -crack.o : crack.c - $(CC) $(CFLAGS) crack.c - -mftext.o : mftext.c midifile.h - $(CC) $(CFLAGS) mftext.c - -# objects for abcmatch -# -matchsup.o : matchsup.c abc.h parseabc.h parser2.h - $(CC) $(CFLAGS) matchsup.c - - - -clean: - del *.o - del *.exe - -zipfile: midi2abc.exe abc2midi.exe mftext.exe yaps.exe\ - abc2abc.exe midicopy.exe - zip pcexe2.zip *.exe readme.txt abcguide.txt demo.abc yaps.txt +# DJGPP (DOS port of gcc) Makefile for abcMIDI package +# +# +# compilation #ifdefs - you may need to change these defined to get +# the code to compile with a different C compiler. +# +# NOFTELL in midifile.c and genmidi.c selects a version of the file-writing +# code which doesn't use file seeking. +# +# PCCFIX in mftext.c midifile.c midi2abc.c +# comments out various things that aren't available in PCC +# +# USE_INDEX causes index() to be used instead of strchr(). This is needed +# by some pre-ANSI C compilers. +# +# ASCTIME causes asctime() to be used instead of strftime() in pslib.c. +# If ANSILIBS is not set, neither routine is used. +# +# ANSILIBS causes code to include some ANSI standard headers +# +# KANDR selects functions prototypes without argument prototypes. +# +# NO_SNPRINTF causes code to use printf instead of snprintf which +# is less secure. +CC=gcc +CFLAGS=-c -ansi -DANSILIBS -DNO_SNPRINTF -Wformat -Wtraditional +# -ansi forces ANSI compliance +LNK=gcc + +all : abc2midi.exe midi2abc.exe abc2abc.exe mftext.exe yaps.exe\ + midicopy.exe abcmatch.exe + +abc2midi.exe : parseabc.o store.o genmidi.o queues.o midifile.o parser2.o + $(LNK) -o abc2midi.exe parseabc.o genmidi.o store.o \ + queues.o midifile.o parser2.o stresspat.o -lm + +abc2abc.exe : parseabc.o toabc.o + $(LNK) -o abc2abc.exe parseabc.o toabc.o + +midi2abc.exe : midifile.o midi2abc.o + $(LNK) midifile.o midi2abc.o -o midi2abc.exe -lm + +mftext.exe : midifile.o mftext.o crack.o + $(LNK) midifile.o mftext.o crack.o -o mftext.exe + +midicopy.exe : midicopy.o + $(LNK) midicopy.o -o midicopy.exe + +abcmatch.exe : abcmatch.o matchsup.o parseabc.o + $(LNK) abcmatch.o matchsup.o parseabc.o -o abcmatch.exe + + + +yaps.exe : parseabc.o yapstree.o drawtune.o debug.o pslib.o position.o parser2.o + $(LNK) -o yaps.exe parseabc.o yapstree.o drawtune.o debug.o \ + position.o pslib.o parser2.o + +# common parser object code +# +parseabc.o : parseabc.c abc.h parseabc.h + $(CC) $(CFLAGS) parseabc.c + +parser2.o : parser2.c parseabc.h parser2.h + $(CC) $(CFLAGS) parser2.c + +# objects needed by abc2abc +# +toabc.o : toabc.c abc.h parseabc.h + $(CC) $(CFLAGS) toabc.c + +# objects needed by abc2midi +# +store.o : store.c abc.h parseabc.h parser2.h genmidi.h + $(CC) $(CFLAGS) store.c + +genmidi.o : genmidi.c abc.h midifile.h genmidi.h + $(CC) $(CFLAGS) genmidi.c + +stresspat.o: stresspat.c + $(CC) $(CFLAGS) stresspat.c + +# could use -DNOFTELL here +tomidi.o : tomidi.c abc.h midifile.h + $(CC) $(CFLAGS) tomidi.c + +queues.o: queues.c genmidi.h + $(CC) $(CFLAGS) queues.c + +midicopy.o: midicopy.c midicopy.h + $(CC) $(CFLAGS) midicopy.c + +abcmatch.o: abcmatch.c abc.h + $(CC) $(CFLAGS) abcmatch.c + + + +# common midifile library +# +# could use -DNOFTELL here +midifile.o : midifile.c midifile.h + $(CC) $(CFLAGS) midifile.c + +# objects needed by yaps +# +yapstree.o: yapstree.c abc.h parseabc.h structs.h drawtune.h parser2.h + $(CC) $(CFLAGS) yapstree.c + +drawtune.o: drawtune.c structs.h sizes.h abc.h drawtune.h + $(CC) $(CFLAGS) drawtune.c + +pslib.o: pslib.c drawtune.h + $(CC) $(CFLAGS) pslib.c + +position.o: position.c abc.h structs.h sizes.h + $(CC) $(CFLAGS) position.c + +debug.o: debug.c structs.h abc.h + $(CC) $(CFLAGS) debug.c + +# objects needed by midi2abc +# +midi2abc.o : midi2abc.c midifile.h + $(CC) $(CFLAGS) midi2abc.c + +# objects for mftext +# +crack.o : crack.c + $(CC) $(CFLAGS) crack.c + +mftext.o : mftext.c midifile.h + $(CC) $(CFLAGS) mftext.c + +# objects for abcmatch +# +matchsup.o : matchsup.c abc.h parseabc.h parser2.h + $(CC) $(CFLAGS) matchsup.c + + + +clean: + del *.o + del *.exe + +zipfile: midi2abc.exe abc2midi.exe mftext.exe yaps.exe\ + abc2abc.exe midicopy.exe + zip pcexe2.zip *.exe readme.txt abcguide.txt demo.abc yaps.txt diff -Nru abcmidi-20200706/makefiles/makefile.bcc abcmidi-20200728/makefiles/makefile.bcc --- abcmidi-20200706/makefiles/makefile.bcc 2012-04-02 01:31:52.000000000 +0000 +++ abcmidi-20200728/makefiles/makefile.bcc 2020-07-27 15:52:43.000000000 +0000 @@ -1,143 +1,143 @@ -# Borland command line compiler bcc version 5.5 Makefile for abcMIDI package -# Can also use mingw or gnu standard make program with this makefile. -# -# -# compilation #ifdefs - you may need to change these defined to get -# the code to compile with a different C compiler. -# -# NOFTELL in midifile.c and tomidi.c selects a version of the file-writing -# code which doesn't use file seeking. -# -# PCCFIX in mftext.c midifile.c midi2abc.c -# comments out various things that aren't available in PCC -# -# USE_INDEX causes index() to be used instead of strchr(). This is needed -# by some pre-ANSI C compilers. -# -# ASCTIME causes asctime() to be used instead of strftime() in pslib.c. -# If ANSILIBS is not set, neither routine is used. -# -# ANSILIBS causes code to include some ANSI standard headers -# -# KANDR selects functions prototypes without argument prototypes. -# -# If your Borland compiler is in another path, change the include -# and library options below in CFLAGS and LDFLAGS. -# -CC=bcc32 -CFLAGS=-g0 -v -WC -c -I/bcc55/include -LNK=ilink32 -LDFLAGS=/ap /L\bcc55\lib c0x32.obj -LDFLAGS2=import32.lib cw32.lib - -all : abc2midi.exe midi2abc.exe abc2abc.exe mftext.exe yaps.exe midicopy.exe abcmatch.exe - -abc2midi.exe : parseabc.obj store.obj genmidi.obj queues.obj midifile.obj parser2.obj stresspat.obj -lm - $(LNK) $(LDFLAGS) parseabc.obj genmidi.obj store.obj \ - queues.obj midifile.obj parser2.obj stresspat.obj, abc2midi.exe,, $(LDFLAGS2) - -abc2abc.exe : parseabc.obj toabc.obj - $(LNK) $(LDFLAGS) parseabc.obj toabc.obj, abc2abc.exe,, $(LDFLAGS2) - -midi2abc.exe : midifile.obj midi2abc.obj - $(LNK) $(LDFLAGS) midifile.obj midi2abc.obj, midi2abc.exe,, $(LDFLAGS2) - -mftext.exe : midifile.obj mftext.obj crack.obj - $(LNK) $(LDFLAGS) midifile.obj mftext.obj crack.obj, mftext.exe,, $(LDFLAGS2) - -yaps.exe : parseabc.obj yapstree.obj drawtune.obj debug.obj pslib.obj position.obj parser2.obj - $(LNK) $(LDFLAGS) parseabc.obj yapstree.obj drawtune.obj debug.obj \ - position.obj pslib.obj parser2.obj, yaps.exe,, $(LDFLAGS2) - -midicopy.exe: midicopy.obj - $(LNK) $(LDFLAGS) midicopy.obj, midicopy.exe,, $(LDFLAGS2) - -abcmatch.exe : abcmatch.obj matchsup.obj parseabc.obj - $(LNK) $(LDFLAGS) abcmatch.obj matchsup.obj parseabc.obj, abcmatch.exe,, $(LDFLAGS2) - - - -# common parser object code -# -parseabc.obj : parseabc.c abc.h parseabc.h - $(CC) $(CFLAGS) parseabc.c - -parser2.obj : parser2.c parseabc.h parser2.h - $(CC) $(CFLAGS) parser2.c - -# objects needed by abc2abc -# -toabc.obj : toabc.c abc.h parseabc.h - $(CC) $(CFLAGS) toabc.c - -# objects needed by abc2midi -# -store.obj : store.c abc.h parseabc.h parser2.h genmidi.h - $(CC) $(CFLAGS) store.c - -genmidi.obj : genmidi.c abc.h midifile.h genmidi.h - $(CC) $(CFLAGS) genmidi.c - -stresspat.obj : stresspat.c - $(CC) $(CFLAGS) stresspat.c - -queues.obj: queues.c genmidi.h - $(CC) $(CFLAGS) queues.c - -# common midifile library -# -# could use -DNOFTELL here -midifile.obj : midifile.c midifile.h - $(CC) $(CFLAGS) midifile.c - -# objects needed by yaps -# -yapstree.obj: yapstree.c abc.h parseabc.h structs.h drawtune.h parser2.h - $(CC) $(CFLAGS) yapstree.c - -drawtune.obj: drawtune.c structs.h sizes.h abc.h drawtune.h - $(CC) $(CFLAGS) drawtune.c - -pslib.obj: pslib.c drawtune.h - $(CC) $(CFLAGS) pslib.c - -position.obj: position.c abc.h structs.h sizes.h - $(CC) $(CFLAGS) position.c - -debug.obj: debug.c structs.h abc.h - $(CC) $(CFLAGS) debug.c - -# objects needed by midi2abc -# -midi2abc.obj : midi2abc.c midifile.h - $(CC) $(CFLAGS) midi2abc.c - -# objects for mftext -# -crack.obj : crack.c - $(CC) $(CFLAGS) crack.c - -mftext.obj : mftext.c midifile.h - $(CC) $(CFLAGS) mftext.c - -# objects for midicopy -# -midicopy.obj :midicopy.c midicopy.h - $(CC) $(CFLAGS) midicopy.c - -# objects for abcmatch -# -abcmatch.obj : abcmatch.c abc.h - $(CC) $(CFLAGS) abcmatch. - -matchsup.obj : matchsup.c abc.h parseabc.h parser2.h - $(CC) $(CFLAGS) matchsup.c - - - -clean: - rm *.obj - rm *.exe - -zipfile: midi2abc.exe abc2midi.exe mftext.exe yaps.exe abc2abc.exe midicopy.exe - zip pcexe2.zip *.exe readme.txt abcguide.txt demo.abc yaps.txt +# Borland command line compiler bcc version 5.5 Makefile for abcMIDI package +# Can also use mingw or gnu standard make program with this makefile. +# +# +# compilation #ifdefs - you may need to change these defined to get +# the code to compile with a different C compiler. +# +# NOFTELL in midifile.c and tomidi.c selects a version of the file-writing +# code which doesn't use file seeking. +# +# PCCFIX in mftext.c midifile.c midi2abc.c +# comments out various things that aren't available in PCC +# +# USE_INDEX causes index() to be used instead of strchr(). This is needed +# by some pre-ANSI C compilers. +# +# ASCTIME causes asctime() to be used instead of strftime() in pslib.c. +# If ANSILIBS is not set, neither routine is used. +# +# ANSILIBS causes code to include some ANSI standard headers +# +# KANDR selects functions prototypes without argument prototypes. +# +# If your Borland compiler is in another path, change the include +# and library options below in CFLAGS and LDFLAGS. +# +CC=bcc32 +CFLAGS=-g0 -v -WC -c -I/bcc55/include +LNK=ilink32 +LDFLAGS=/ap /L\bcc55\lib c0x32.obj +LDFLAGS2=import32.lib cw32.lib + +all : abc2midi.exe midi2abc.exe abc2abc.exe mftext.exe yaps.exe midicopy.exe abcmatch.exe + +abc2midi.exe : parseabc.obj store.obj genmidi.obj queues.obj midifile.obj parser2.obj stresspat.obj -lm + $(LNK) $(LDFLAGS) parseabc.obj genmidi.obj store.obj \ + queues.obj midifile.obj parser2.obj stresspat.obj, abc2midi.exe,, $(LDFLAGS2) + +abc2abc.exe : parseabc.obj toabc.obj + $(LNK) $(LDFLAGS) parseabc.obj toabc.obj, abc2abc.exe,, $(LDFLAGS2) + +midi2abc.exe : midifile.obj midi2abc.obj + $(LNK) $(LDFLAGS) midifile.obj midi2abc.obj, midi2abc.exe,, $(LDFLAGS2) + +mftext.exe : midifile.obj mftext.obj crack.obj + $(LNK) $(LDFLAGS) midifile.obj mftext.obj crack.obj, mftext.exe,, $(LDFLAGS2) + +yaps.exe : parseabc.obj yapstree.obj drawtune.obj debug.obj pslib.obj position.obj parser2.obj + $(LNK) $(LDFLAGS) parseabc.obj yapstree.obj drawtune.obj debug.obj \ + position.obj pslib.obj parser2.obj, yaps.exe,, $(LDFLAGS2) + +midicopy.exe: midicopy.obj + $(LNK) $(LDFLAGS) midicopy.obj, midicopy.exe,, $(LDFLAGS2) + +abcmatch.exe : abcmatch.obj matchsup.obj parseabc.obj + $(LNK) $(LDFLAGS) abcmatch.obj matchsup.obj parseabc.obj, abcmatch.exe,, $(LDFLAGS2) + + + +# common parser object code +# +parseabc.obj : parseabc.c abc.h parseabc.h + $(CC) $(CFLAGS) parseabc.c + +parser2.obj : parser2.c parseabc.h parser2.h + $(CC) $(CFLAGS) parser2.c + +# objects needed by abc2abc +# +toabc.obj : toabc.c abc.h parseabc.h + $(CC) $(CFLAGS) toabc.c + +# objects needed by abc2midi +# +store.obj : store.c abc.h parseabc.h parser2.h genmidi.h + $(CC) $(CFLAGS) store.c + +genmidi.obj : genmidi.c abc.h midifile.h genmidi.h + $(CC) $(CFLAGS) genmidi.c + +stresspat.obj : stresspat.c + $(CC) $(CFLAGS) stresspat.c + +queues.obj: queues.c genmidi.h + $(CC) $(CFLAGS) queues.c + +# common midifile library +# +# could use -DNOFTELL here +midifile.obj : midifile.c midifile.h + $(CC) $(CFLAGS) midifile.c + +# objects needed by yaps +# +yapstree.obj: yapstree.c abc.h parseabc.h structs.h drawtune.h parser2.h + $(CC) $(CFLAGS) yapstree.c + +drawtune.obj: drawtune.c structs.h sizes.h abc.h drawtune.h + $(CC) $(CFLAGS) drawtune.c + +pslib.obj: pslib.c drawtune.h + $(CC) $(CFLAGS) pslib.c + +position.obj: position.c abc.h structs.h sizes.h + $(CC) $(CFLAGS) position.c + +debug.obj: debug.c structs.h abc.h + $(CC) $(CFLAGS) debug.c + +# objects needed by midi2abc +# +midi2abc.obj : midi2abc.c midifile.h + $(CC) $(CFLAGS) midi2abc.c + +# objects for mftext +# +crack.obj : crack.c + $(CC) $(CFLAGS) crack.c + +mftext.obj : mftext.c midifile.h + $(CC) $(CFLAGS) mftext.c + +# objects for midicopy +# +midicopy.obj :midicopy.c midicopy.h + $(CC) $(CFLAGS) midicopy.c + +# objects for abcmatch +# +abcmatch.obj : abcmatch.c abc.h + $(CC) $(CFLAGS) abcmatch. + +matchsup.obj : matchsup.c abc.h parseabc.h parser2.h + $(CC) $(CFLAGS) matchsup.c + + + +clean: + rm *.obj + rm *.exe + +zipfile: midi2abc.exe abc2midi.exe mftext.exe yaps.exe abc2abc.exe midicopy.exe + zip pcexe2.zip *.exe readme.txt abcguide.txt demo.abc yaps.txt diff -Nru abcmidi-20200706/makefiles/makefile.ming abcmidi-20200728/makefiles/makefile.ming --- abcmidi-20200706/makefiles/makefile.ming 2018-01-25 13:09:02.000000000 +0000 +++ abcmidi-20200728/makefiles/makefile.ming 2020-07-27 15:52:43.000000000 +0000 @@ -1,136 +1,136 @@ -# Mingw Win32 Makefile for abcMIDI package -# -# -# compilation #ifdefs - you may need to change these defined to get -# the code to compile with a different C compiler. -# -# NOFTELL in midifile.c and tomidi.c selects a version of the file-writing -# code which doesn't use file seeking. -# -# PCCFIX in mftext.c midifile.c midi2abc.c -# comments out various things that aren't available in PCC -# -# USE_INDEX causes index() to be used instead of strchr(). This is needed -# by some pre-ANSI C compilers. -# -# ASCTIME causes asctime() to be used instead of strftime() in pslib.c. -# If ANSILIBS is not set, neither routine is used. -# -# ANSILIBS causes code to include some ANSI standard headers -# -# KANDR selects functions prototypes without argument prototypes. -# -# If your mingw compiler is in another path, change the include -# options below in CFLAGS. -# -CC=gcc -CFLAGS=-c -I/mingw/i386-mingw32/include -LNK=gcc -LDFLAGS=-o - -all : abc2midi.exe midi2abc.exe abc2abc.exe mftext.exe yaps.exe \ -midicopy.exe abcmatch.exe - -abc2midi.exe : parseabc.o store.o genmidi.o queues.o midifile.o parser2.o - $(LNK) $(LDFLAGS) abc2midi.exe parseabc.o genmidi.o store.o \ - queues.o midifile.o parser2.o -stresspat.o -lm - -abc2abc.exe : parseabc.o toabc.o - $(LNK) $(LDFLAGS) abc2abc.exe parseabc.o toabc.o - -midi2abc.exe : midifile.o midi2abc.o - $(LNK) $(LDFLAGS) midi2abc.exe midifile.o midi2abc.o -lm - -mftext.exe : midifile.o mftext.o crack.o - $(LNK) $(LDFLAGS) mftext.exe midifile.o mftext.o crack.o - -yaps.exe : parseabc.o yapstree.o drawtune.o debug.o pslib.o position.o parser2.o - $(LNK) $(LDFLAGS) yaps.exe parseabc.o yapstree.o drawtune.o debug.o \ - position.o pslib.o parser2.o - -midicopy.exe: midicopy.o - $(LNK) $(LDFLAGS) midicopy.exe midicopy.o - -abcmatch.exe : abcmatch.o matchsup.o parseabc.o - $(LNK) abcmatch.o matchsup.o parseabc.o -o abcmatch.exe - - - -# common parser object code -# -parseabc.o : parseabc.c abc.h parseabc.h - $(CC) $(CFLAGS) parseabc.c - -parser2.o : parser2.c parseabc.h parser2.h - $(CC) $(CFLAGS) parser2.c - -# objects needed by abc2abc -# -toabc.o : toabc.c abc.h parseabc.h - $(CC) $(CFLAGS) toabc.c - -# objects needed by abc2midi -# -store.o : store.c abc.h parseabc.h parser2.h genmidi.h - $(CC) $(CFLAGS) store.c - -genmidi.o : genmidi.c abc.h midifile.h genmidi.h - $(CC) $(CFLAGS) genmidi.c - -stresspat.o : stresspat.c - $(CC) $(CFLAGS) stresspat.c - -# could use -DNOFTELL here -tomidi.o : tomidi.c abc.h midifile.h - $(CC) $(CFLAGS) tomidi.c - -queues.o: queues.c genmidi.h - $(CC) $(CFLAGS) queues.c - -# common midifile library -# -# could use -DNOFTELL here -midifile.o : midifile.c midifile.h - $(CC) $(CFLAGS) midifile.c - -# objects needed by yaps -# -yapstree.o: yapstree.c abc.h parseabc.h structs.h drawtune.h parser2.h - $(CC) $(CFLAGS) yapstree.c - -drawtune.o: drawtune.c structs.h sizes.h abc.h drawtune.h - $(CC) $(CFLAGS) drawtune.c - -pslib.o: pslib.c drawtune.h - $(CC) $(CFLAGS) pslib.c - -position.o: position.c abc.h structs.h sizes.h - $(CC) $(CFLAGS) position.c - -debug.o: debug.c structs.h abc.h - $(CC) $(CFLAGS) debug.c - -# objects needed by midi2abc -# -midi2abc.o : midi2abc.c midifile.h - $(CC) $(CFLAGS) midi2abc.c - -# objects for mftext -# -crack.o : crack.c - $(CC) $(CFLAGS) crack.c - -mftext.o : mftext.c midifile.h - $(CC) $(CFLAGS) mftext.c - -# objects needed for midicopy -# -midicopy.o : midicopy.c - $(CC) $(CFLAGS) midicopy.c - -clean: - rm *.o - rm *.exe - -zipfile: midi2abc.exe abc2midi.exe mftext.exe yaps.exe abc2abc.exe midicopy.exe - zip pcexe2.zip *.exe readme.txt abcguide.txt demo.abc yaps.txt +# Mingw Win32 Makefile for abcMIDI package +# +# +# compilation #ifdefs - you may need to change these defined to get +# the code to compile with a different C compiler. +# +# NOFTELL in midifile.c and tomidi.c selects a version of the file-writing +# code which doesn't use file seeking. +# +# PCCFIX in mftext.c midifile.c midi2abc.c +# comments out various things that aren't available in PCC +# +# USE_INDEX causes index() to be used instead of strchr(). This is needed +# by some pre-ANSI C compilers. +# +# ASCTIME causes asctime() to be used instead of strftime() in pslib.c. +# If ANSILIBS is not set, neither routine is used. +# +# ANSILIBS causes code to include some ANSI standard headers +# +# KANDR selects functions prototypes without argument prototypes. +# +# If your mingw compiler is in another path, change the include +# options below in CFLAGS. +# +CC=gcc +CFLAGS=-c -I/mingw/i386-mingw32/include +LNK=gcc +LDFLAGS=-o + +all : abc2midi.exe midi2abc.exe abc2abc.exe mftext.exe yaps.exe \ +midicopy.exe abcmatch.exe + +abc2midi.exe : parseabc.o store.o genmidi.o queues.o midifile.o parser2.o + $(LNK) $(LDFLAGS) abc2midi.exe parseabc.o genmidi.o store.o \ + queues.o midifile.o parser2.o -stresspat.o -lm + +abc2abc.exe : parseabc.o toabc.o + $(LNK) $(LDFLAGS) abc2abc.exe parseabc.o toabc.o + +midi2abc.exe : midifile.o midi2abc.o + $(LNK) $(LDFLAGS) midi2abc.exe midifile.o midi2abc.o -lm + +mftext.exe : midifile.o mftext.o crack.o + $(LNK) $(LDFLAGS) mftext.exe midifile.o mftext.o crack.o + +yaps.exe : parseabc.o yapstree.o drawtune.o debug.o pslib.o position.o parser2.o + $(LNK) $(LDFLAGS) yaps.exe parseabc.o yapstree.o drawtune.o debug.o \ + position.o pslib.o parser2.o + +midicopy.exe: midicopy.o + $(LNK) $(LDFLAGS) midicopy.exe midicopy.o + +abcmatch.exe : abcmatch.o matchsup.o parseabc.o + $(LNK) abcmatch.o matchsup.o parseabc.o -o abcmatch.exe + + + +# common parser object code +# +parseabc.o : parseabc.c abc.h parseabc.h + $(CC) $(CFLAGS) parseabc.c + +parser2.o : parser2.c parseabc.h parser2.h + $(CC) $(CFLAGS) parser2.c + +# objects needed by abc2abc +# +toabc.o : toabc.c abc.h parseabc.h + $(CC) $(CFLAGS) toabc.c + +# objects needed by abc2midi +# +store.o : store.c abc.h parseabc.h parser2.h genmidi.h + $(CC) $(CFLAGS) store.c + +genmidi.o : genmidi.c abc.h midifile.h genmidi.h + $(CC) $(CFLAGS) genmidi.c + +stresspat.o : stresspat.c + $(CC) $(CFLAGS) stresspat.c + +# could use -DNOFTELL here +tomidi.o : tomidi.c abc.h midifile.h + $(CC) $(CFLAGS) tomidi.c + +queues.o: queues.c genmidi.h + $(CC) $(CFLAGS) queues.c + +# common midifile library +# +# could use -DNOFTELL here +midifile.o : midifile.c midifile.h + $(CC) $(CFLAGS) midifile.c + +# objects needed by yaps +# +yapstree.o: yapstree.c abc.h parseabc.h structs.h drawtune.h parser2.h + $(CC) $(CFLAGS) yapstree.c + +drawtune.o: drawtune.c structs.h sizes.h abc.h drawtune.h + $(CC) $(CFLAGS) drawtune.c + +pslib.o: pslib.c drawtune.h + $(CC) $(CFLAGS) pslib.c + +position.o: position.c abc.h structs.h sizes.h + $(CC) $(CFLAGS) position.c + +debug.o: debug.c structs.h abc.h + $(CC) $(CFLAGS) debug.c + +# objects needed by midi2abc +# +midi2abc.o : midi2abc.c midifile.h + $(CC) $(CFLAGS) midi2abc.c + +# objects for mftext +# +crack.o : crack.c + $(CC) $(CFLAGS) crack.c + +mftext.o : mftext.c midifile.h + $(CC) $(CFLAGS) mftext.c + +# objects needed for midicopy +# +midicopy.o : midicopy.c + $(CC) $(CFLAGS) midicopy.c + +clean: + rm *.o + rm *.exe + +zipfile: midi2abc.exe abc2midi.exe mftext.exe yaps.exe abc2abc.exe midicopy.exe + zip pcexe2.zip *.exe readme.txt abcguide.txt demo.abc yaps.txt diff -Nru abcmidi-20200706/makefiles/makefile.w32 abcmidi-20200728/makefiles/makefile.w32 --- abcmidi-20200706/makefiles/makefile.w32 2015-10-18 17:49:30.000000000 +0000 +++ abcmidi-20200728/makefiles/makefile.w32 2020-07-27 15:52:43.000000000 +0000 @@ -1,99 +1,99 @@ - -# tested with MS Visual C++ 2010 Express -# build with: -# nmake /F makefiles\makefile.w32 all - -!include - -# $(cvars) static link -# $(cvarsdll) dynamically link -# add -O2 to end of line for speed optimization -# add -O1 for size optimization -#comp = $(cc) /wd4996 -D_CRT_SECURE_NO_WARNINGS $(cflags) $(cvars) -comp = $(cc) /wd4996 -D_CRT_SECURE_NO_WARNINGS $(cflags) $(cvarsdll) - -#all: abc2midi midi2abc abc2abc mftext yaps midicopy abcmatch - -all: abc2midi.exe midi2abc.exe abc2abc.exe mftext.exe yaps.exe midicopy.exe abcmatch.exe - - -abc2midi.exe: parseabc.obj store.obj genmidi.obj midifile.obj queues.obj parser2.obj stresspat.obj - $(link) $(conflags) -out:abc2midi.exe parseabc.obj store.obj genmidi.obj queues.obj parser2.obj midifile.obj stresspat.obj - -abcmatch.exe: abcmatch.obj matchsup.obj parseabc.obj - $(link) $(conflags) -out:abcmatch.exe abcmatch.obj matchsup.obj parseabc.obj - -midi2abc.exe: midifile.obj midi2abc.obj - $(link) $(conflags) -out:midi2abc.exe midifile.obj midi2abc.obj - -abc2abc.exe: parseabc.obj toabc.obj - $(link) $(conflags) -out:abc2abc.exe parseabc.obj toabc.obj - -mftext.exe: midifile.obj mftext.obj crack.obj - $(link) $(conflags) -out:mftext.exe midifile.obj mftext.obj crack.obj - -midicopy.exe: midicopy.obj - $(link) $(conflags) -out:midicopy.exe midicopy.obj - -yaps.exe: parseabc.obj yapstree.obj drawtune.obj debug.obj pslib.obj position.obj parser2.obj - $(link) $(conflags) -out:yaps.exe parseabc.obj yapstree.obj drawtune.obj debug.obj position.obj pslib.obj parser2.obj $(conlibs) - - -abcmatch.obj: abcmatch.c abc.h - $(comp) abcmatch.c - -crack.obj: crack.c - $(comp) crack.c - -debug.obj: debug.c structs.h abc.h - $(comp) debug.c - -drawtune.obj: drawtune.c structs.h sizes.h abc.h drawtune.h - $(comp) drawtune.c - -genmidi.obj: genmidi.c abc.h midifile.h genmidi.h - $(comp) genmidi.c - -matchsup.obj: matchsup.c abc.h parseabc.h parser2.h - $(comp) matchsup.c - -mftext.obj: mftext.c midifile.h - $(comp) mftext.c - -midi2abc.obj: midi2abc.c midifile.h - $(comp) midi2abc.c - -midifile.obj: midifile.c midifile.h - $(comp) midifile.c - -parseabc.obj: parseabc.c abc.h parseabc.h - $(comp) parseabc.c - -parser2.obj: parser2.c abc.h parseabc.h parser2.h - $(comp) parser2.c - -position.obj: position.c abc.h structs.h sizes.h - $(comp) position.c - -pslib.obj: pslib.c drawtune.h - $(comp) pslib.c - -queues.obj: queues.c genmidi.h - $(comp) queues.c - -store.obj: store.c abc.h parseabc.h midifile.h genmidi.h - $(comp) store.c - -stresspat.obj: stresspat.c - $(comp) stresspat.c - -toabc.obj: toabc.c abc.h parseabc.h - $(comp) toabc.c - -yapstree.obj: yapstree.c abc.h parseabc.h structs.h drawtune.h - $(comp) yapstree.c - - -clean: - del *.obj - del *.exe + +# tested with MS Visual C++ 2010 Express +# build with: +# nmake /F makefiles\makefile.w32 all + +!include + +# $(cvars) static link +# $(cvarsdll) dynamically link +# add -O2 to end of line for speed optimization +# add -O1 for size optimization +#comp = $(cc) /wd4996 -D_CRT_SECURE_NO_WARNINGS $(cflags) $(cvars) +comp = $(cc) /wd4996 -D_CRT_SECURE_NO_WARNINGS $(cflags) $(cvarsdll) + +#all: abc2midi midi2abc abc2abc mftext yaps midicopy abcmatch + +all: abc2midi.exe midi2abc.exe abc2abc.exe mftext.exe yaps.exe midicopy.exe abcmatch.exe + + +abc2midi.exe: parseabc.obj store.obj genmidi.obj midifile.obj queues.obj parser2.obj stresspat.obj + $(link) $(conflags) -out:abc2midi.exe parseabc.obj store.obj genmidi.obj queues.obj parser2.obj midifile.obj stresspat.obj + +abcmatch.exe: abcmatch.obj matchsup.obj parseabc.obj + $(link) $(conflags) -out:abcmatch.exe abcmatch.obj matchsup.obj parseabc.obj + +midi2abc.exe: midifile.obj midi2abc.obj + $(link) $(conflags) -out:midi2abc.exe midifile.obj midi2abc.obj + +abc2abc.exe: parseabc.obj toabc.obj + $(link) $(conflags) -out:abc2abc.exe parseabc.obj toabc.obj + +mftext.exe: midifile.obj mftext.obj crack.obj + $(link) $(conflags) -out:mftext.exe midifile.obj mftext.obj crack.obj + +midicopy.exe: midicopy.obj + $(link) $(conflags) -out:midicopy.exe midicopy.obj + +yaps.exe: parseabc.obj yapstree.obj drawtune.obj debug.obj pslib.obj position.obj parser2.obj + $(link) $(conflags) -out:yaps.exe parseabc.obj yapstree.obj drawtune.obj debug.obj position.obj pslib.obj parser2.obj $(conlibs) + + +abcmatch.obj: abcmatch.c abc.h + $(comp) abcmatch.c + +crack.obj: crack.c + $(comp) crack.c + +debug.obj: debug.c structs.h abc.h + $(comp) debug.c + +drawtune.obj: drawtune.c structs.h sizes.h abc.h drawtune.h + $(comp) drawtune.c + +genmidi.obj: genmidi.c abc.h midifile.h genmidi.h + $(comp) genmidi.c + +matchsup.obj: matchsup.c abc.h parseabc.h parser2.h + $(comp) matchsup.c + +mftext.obj: mftext.c midifile.h + $(comp) mftext.c + +midi2abc.obj: midi2abc.c midifile.h + $(comp) midi2abc.c + +midifile.obj: midifile.c midifile.h + $(comp) midifile.c + +parseabc.obj: parseabc.c abc.h parseabc.h + $(comp) parseabc.c + +parser2.obj: parser2.c abc.h parseabc.h parser2.h + $(comp) parser2.c + +position.obj: position.c abc.h structs.h sizes.h + $(comp) position.c + +pslib.obj: pslib.c drawtune.h + $(comp) pslib.c + +queues.obj: queues.c genmidi.h + $(comp) queues.c + +store.obj: store.c abc.h parseabc.h midifile.h genmidi.h + $(comp) store.c + +stresspat.obj: stresspat.c + $(comp) stresspat.c + +toabc.obj: toabc.c abc.h parseabc.h + $(comp) toabc.c + +yapstree.obj: yapstree.c abc.h parseabc.h structs.h drawtune.h + $(comp) yapstree.c + + +clean: + del *.obj + del *.exe diff -Nru abcmidi-20200706/makefiles/makefile.wat abcmidi-20200728/makefiles/makefile.wat --- abcmidi-20200706/makefiles/makefile.wat 2012-04-02 01:30:48.000000000 +0000 +++ abcmidi-20200728/makefiles/makefile.wat 2020-07-27 15:52:43.000000000 +0000 @@ -1,141 +1,141 @@ -# Watcom Win32 Makefile for abcMIDI package -# Use mingw or gnu standard make program with this makefile. -# -# -# compilation #ifdefs - you may need to change these defined to get -# the code to compile with a different C compiler. -# -# NOFTELL in midifile.c and genmidi.c selects a version of the file-writing -# code which doesn't use file seeking. -# -# PCCFIX in mftext.c midifile.c midi2abc.c -# comments out various things that aren't available in PCC -# -# USE_INDEX causes index() to be used instead of strchr(). This is needed -# by some pre-ANSI C compilers. -# -# ASCTIME causes asctime() to be used instead of strftime() in pslib.c. -# If ANSILIBS is not set, neither routine is used. -# -# ANSILIBS causes code to include some ANSI standard headers -# -# KANDR selects functions prototypes without argument prototypes. -# -CC=wcc386 -CFLAGS=-ic:\\watcom\\h;c:\\watcom\\h\\nt -w4 -e25 -zq -od -d2 -5r -bt=nt -mf -DANSILIBS -LDFLAGS=sys nt name -LDFLAGS2=d all op inc op st=200000 op maxe=25 op q op symf -LNK=wlink - -all : abc2midi.exe midi2abc.exe abc2abc.exe mftext.exe yaps.exe midicopy.exe abcmatch.exe - -abc2midi.exe : parseabc.obj store.obj genmidi.obj queues.obj midifile.obj parser2.obj stresspat.obj -lm - $(LNK) $(LDFLAGS) abc2midi.exe $(LDFLAGS2) FILE parseabc.obj FILE genmidi.obj FILE store.obj \ - FILE queues.obj FILE midifile.obj FILE parser2.obj FILE stresspat.obj - -abc2abc.exe : parseabc.obj toabc.obj - $(LNK) $(LDFLAGS) abc2abc.exe $(LDFLAGS2) FILE parseabc.obj FILE toabc.obj - -midi2abc.exe : midifile.obj midi2abc.obj - $(LNK) $(LDFLAGS) midi2abc.exe $(LDFLAGS2) FILE midifile.obj FILE midi2abc.obj - -mftext.exe : midifile.obj mftext.obj crack.obj - $(LNK) $(LDFLAGS) mftext.exe $(LDFLAGS2) FILE midifile.obj FILE mftext.obj FILE crack.obj - -midicopy.exe : midicopy.obj - $(LNK) $(LDFLAGS) midicopy.exe $(LDFLAGS2) FILE midicopy.obj - -yaps.exe : parseabc.obj yapstree.obj drawtune.obj debug.obj pslib.obj position.obj parser2.obj - $(LNK) $(LDFLAGS) yaps.exe $(LDFLAGS2) FILE parseabc.obj FILE yapstree.obj FILE drawtune.obj FILE debug.obj FILE position.obj FILE pslib.obj FILE parser2.obj - - -abcmatch.exe : abcmatch.obj matchsup.obj parseabc.obj - $(LNK) $(LDFLAGS) abcmatch.exe $(LDFLAGS2) FILE abcmatch.obj FILE matchsup.obj FILE parseabc.obj - - -# common parser object code -# -parseabc.obj : parseabc.c abc.h parseabc.h - $(CC) $(CFLAGS) parseabc.c - -parser2.obj : parser2.c parseabc.h parser2.h - $(CC) $(CFLAGS) parser2.c - -# objects needed by abc2abc -# -toabc.obj : toabc.c abc.h parseabc.h - $(CC) $(CFLAGS) toabc.c - -# objects needed by abc2midi -# -store.obj : store.c abc.h parseabc.h parser2.h genmidi.h - $(CC) $(CFLAGS) store.c - -genmidi.obj : genmidi.c abc.h midifile.h genmidi.h - $(CC) $(CFLAGS) genmidi.c - -stresspat.obj : stresspat.c - $(CC) $(CFLAGS) stresspat.c - -# could use -DNOFTELL here -tomidi.obj : tomidi.c abc.h midifile.h - $(CC) $(CFLAGS) tomidi.c - -queues.obj: queues.c genmidi.h - $(CC) $(CFLAGS) queues.c - -# common midifile library -# -# could use -DNOFTELL here -midifile.obj : midifile.c midifile.h - $(CC) $(CFLAGS) midifile.c - -# objects needed by yaps -# -yapstree.obj: yapstree.c abc.h parseabc.h structs.h drawtune.h parser2.h - $(CC) $(CFLAGS) yapstree.c - -drawtune.obj: drawtune.c structs.h sizes.h abc.h drawtune.h - $(CC) $(CFLAGS) drawtune.c - -pslib.obj: pslib.c drawtune.h - $(CC) $(CFLAGS) pslib.c - -position.obj: position.c abc.h structs.h sizes.h - $(CC) $(CFLAGS) position.c - -debug.obj: debug.c structs.h abc.h - $(CC) $(CFLAGS) debug.c - -# objects needed by midi2abc -# -midi2abc.obj : midi2abc.c midifile.h - $(CC) $(CFLAGS) midi2abc.c - -# objects for mftext -# -crack.obj : crack.c - $(CC) $(CFLAGS) crack.c - -mftext.obj : mftext.c midifile.h - $(CC) $(CFLAGS) mftext.c - -# objects for midicopy -# -midicopy.obj : midicopy.c midicopy.h - $(CC) $(CFLAGS) midicopy.c - -#objects for abcmtch -# -abcmatch.obj : abcmatch.c abc.h - $(CC) $(CFLAGS) abcmatch.c - -matchsup.obj : matchsup.c abc.h parseabc.h parser2.h genmidi.h - $(CC) $(CFLAGS) matchsup.c - -clean: - rm *.obj - rm *.exe - -zipfile: midi2abc.exe abc2midi.exe mftext.exe yaps.exe abc2abc.exe abcmatch.exe - zip pcexe2.zip *.exe readme.txt abcguide.txt demo.abc yaps.txt +# Watcom Win32 Makefile for abcMIDI package +# Use mingw or gnu standard make program with this makefile. +# +# +# compilation #ifdefs - you may need to change these defined to get +# the code to compile with a different C compiler. +# +# NOFTELL in midifile.c and genmidi.c selects a version of the file-writing +# code which doesn't use file seeking. +# +# PCCFIX in mftext.c midifile.c midi2abc.c +# comments out various things that aren't available in PCC +# +# USE_INDEX causes index() to be used instead of strchr(). This is needed +# by some pre-ANSI C compilers. +# +# ASCTIME causes asctime() to be used instead of strftime() in pslib.c. +# If ANSILIBS is not set, neither routine is used. +# +# ANSILIBS causes code to include some ANSI standard headers +# +# KANDR selects functions prototypes without argument prototypes. +# +CC=wcc386 +CFLAGS=-ic:\\watcom\\h;c:\\watcom\\h\\nt -w4 -e25 -zq -od -d2 -5r -bt=nt -mf -DANSILIBS +LDFLAGS=sys nt name +LDFLAGS2=d all op inc op st=200000 op maxe=25 op q op symf +LNK=wlink + +all : abc2midi.exe midi2abc.exe abc2abc.exe mftext.exe yaps.exe midicopy.exe abcmatch.exe + +abc2midi.exe : parseabc.obj store.obj genmidi.obj queues.obj midifile.obj parser2.obj stresspat.obj -lm + $(LNK) $(LDFLAGS) abc2midi.exe $(LDFLAGS2) FILE parseabc.obj FILE genmidi.obj FILE store.obj \ + FILE queues.obj FILE midifile.obj FILE parser2.obj FILE stresspat.obj + +abc2abc.exe : parseabc.obj toabc.obj + $(LNK) $(LDFLAGS) abc2abc.exe $(LDFLAGS2) FILE parseabc.obj FILE toabc.obj + +midi2abc.exe : midifile.obj midi2abc.obj + $(LNK) $(LDFLAGS) midi2abc.exe $(LDFLAGS2) FILE midifile.obj FILE midi2abc.obj + +mftext.exe : midifile.obj mftext.obj crack.obj + $(LNK) $(LDFLAGS) mftext.exe $(LDFLAGS2) FILE midifile.obj FILE mftext.obj FILE crack.obj + +midicopy.exe : midicopy.obj + $(LNK) $(LDFLAGS) midicopy.exe $(LDFLAGS2) FILE midicopy.obj + +yaps.exe : parseabc.obj yapstree.obj drawtune.obj debug.obj pslib.obj position.obj parser2.obj + $(LNK) $(LDFLAGS) yaps.exe $(LDFLAGS2) FILE parseabc.obj FILE yapstree.obj FILE drawtune.obj FILE debug.obj FILE position.obj FILE pslib.obj FILE parser2.obj + + +abcmatch.exe : abcmatch.obj matchsup.obj parseabc.obj + $(LNK) $(LDFLAGS) abcmatch.exe $(LDFLAGS2) FILE abcmatch.obj FILE matchsup.obj FILE parseabc.obj + + +# common parser object code +# +parseabc.obj : parseabc.c abc.h parseabc.h + $(CC) $(CFLAGS) parseabc.c + +parser2.obj : parser2.c parseabc.h parser2.h + $(CC) $(CFLAGS) parser2.c + +# objects needed by abc2abc +# +toabc.obj : toabc.c abc.h parseabc.h + $(CC) $(CFLAGS) toabc.c + +# objects needed by abc2midi +# +store.obj : store.c abc.h parseabc.h parser2.h genmidi.h + $(CC) $(CFLAGS) store.c + +genmidi.obj : genmidi.c abc.h midifile.h genmidi.h + $(CC) $(CFLAGS) genmidi.c + +stresspat.obj : stresspat.c + $(CC) $(CFLAGS) stresspat.c + +# could use -DNOFTELL here +tomidi.obj : tomidi.c abc.h midifile.h + $(CC) $(CFLAGS) tomidi.c + +queues.obj: queues.c genmidi.h + $(CC) $(CFLAGS) queues.c + +# common midifile library +# +# could use -DNOFTELL here +midifile.obj : midifile.c midifile.h + $(CC) $(CFLAGS) midifile.c + +# objects needed by yaps +# +yapstree.obj: yapstree.c abc.h parseabc.h structs.h drawtune.h parser2.h + $(CC) $(CFLAGS) yapstree.c + +drawtune.obj: drawtune.c structs.h sizes.h abc.h drawtune.h + $(CC) $(CFLAGS) drawtune.c + +pslib.obj: pslib.c drawtune.h + $(CC) $(CFLAGS) pslib.c + +position.obj: position.c abc.h structs.h sizes.h + $(CC) $(CFLAGS) position.c + +debug.obj: debug.c structs.h abc.h + $(CC) $(CFLAGS) debug.c + +# objects needed by midi2abc +# +midi2abc.obj : midi2abc.c midifile.h + $(CC) $(CFLAGS) midi2abc.c + +# objects for mftext +# +crack.obj : crack.c + $(CC) $(CFLAGS) crack.c + +mftext.obj : mftext.c midifile.h + $(CC) $(CFLAGS) mftext.c + +# objects for midicopy +# +midicopy.obj : midicopy.c midicopy.h + $(CC) $(CFLAGS) midicopy.c + +#objects for abcmtch +# +abcmatch.obj : abcmatch.c abc.h + $(CC) $(CFLAGS) abcmatch.c + +matchsup.obj : matchsup.c abc.h parseabc.h parser2.h genmidi.h + $(CC) $(CFLAGS) matchsup.c + +clean: + rm *.obj + rm *.exe + +zipfile: midi2abc.exe abc2midi.exe mftext.exe yaps.exe abc2abc.exe abcmatch.exe + zip pcexe2.zip *.exe readme.txt abcguide.txt demo.abc yaps.txt diff -Nru abcmidi-20200706/makefiles/makefile.wd abcmidi-20200728/makefiles/makefile.wd --- abcmidi-20200706/makefiles/makefile.wd 2012-04-02 01:32:42.000000000 +0000 +++ abcmidi-20200728/makefiles/makefile.wd 2020-07-27 15:52:43.000000000 +0000 @@ -1,139 +1,139 @@ -# Watcom DOS 32 bit Makefile for abcMIDI package -# Use mingw or gnu standard make program with this makefile. -# -# -# compilation #ifdefs - you may need to change these defined to get -# the code to compile with a different C compiler. -# -# NOFTELL in midifile.c and genmidi.c selects a version of the file-writing -# code which doesn't use file seeking. -# -# PCCFIX in mftext.c midifile.c midi2abc.c -# comments out various things that aren't available in PCC -# -# USE_INDEX causes index() to be used instead of strchr(). This is needed -# by some pre-ANSI C compilers. -# -# ASCTIME causes asctime() to be used instead of strftime() in pslib.c. -# If ANSILIBS is not set, neither routine is used. -# -# ANSILIBS causes code to include some ANSI standard headers -# -# KANDR selects functions prototypes without argument prototypes. -# -CC=wcc386 -CFLAGS=-ic:\\watcom\\h -w4 -e25 -zq -od -d2 -5r -bt=dos -mf -DANSILIBS -LDFLAGS=sys dos4g name -LDFLAGS2=d all op inc op st=200000 op maxe=25 op q op symf -LNK=wlink - -all : abc2midi.exe midi2abc.exe abc2abc.exe mftext.exe yaps.exe midicopy.exe abcmatch.exe - -abc2midi.exe : parseabc.obj store.obj genmidi.obj queues.obj midifile.obj parser2.obj -lm - $(LNK) $(LDFLAGS) abc2midi.exe $(LDFLAGS2) FILE parseabc.obj FILE genmidi.obj FILE store.obj \ - FILE queues.obj FILE midifile.obj FILE parser2.obj FILE stresspat.obj - -abc2abc.exe : parseabc.obj toabc.obj - $(LNK) $(LDFLAGS) abc2abc.exe $(LDFLAGS2) FILE parseabc.obj FILE toabc.obj - -midi2abc.exe : midifile.obj midi2abc.obj - $(LNK) $(LDFLAGS) midi2abc.exe $(LDFLAGS2) FILE midifile.obj FILE midi2abc.obj - -mftext.exe : midifile.obj mftext.obj crack.obj - $(LNK) $(LDFLAGS) mftext.exe $(LDFLAGS2) FILE midifile.obj FILE mftext.obj FILE crack.obj - -midicopy.exe: midicopy.obj - $(LNK) $(LDFLAGS) midicopy.exe $(LDFLAGS2) FILE midicopy.obj - -abcmatch.exe : abcmatch.obj matchsup.obj parseabc.obj - $(LNK) $(LDFLAGS) abcmatch.exe $(LDFLAGS) FILE abcmatch.obj matchsup.obj parseabc.obj - - - -yaps.exe : parseabc.obj yapstree.obj drawtune.obj debug.obj pslib.obj position.obj parser2.obj - $(LNK) $(LDFLAGS) yaps.exe $(LDFLAGS2) FILE parseabc.obj FILE yapstree.obj FILE drawtune.obj FILE debug.obj FILE position.obj FILE pslib.obj FILE parser2.obj - -# common parser object code -# -parseabc.obj : parseabc.c abc.h parseabc.h - $(CC) $(CFLAGS) parseabc.c - -parser2.obj : parser2.c parseabc.h parser2.h - $(CC) $(CFLAGS) parser2.c - -# objects needed by abc2abc -# -toabc.obj : toabc.c abc.h parseabc.h - $(CC) $(CFLAGS) toabc.c - -# objects needed by abc2midi -# -store.obj : store.c abc.h parseabc.h parser2.h genmidi.h - $(CC) $(CFLAGS) store.c - -genmidi.obj : genmidi.c abc.h midifile.h genmidi.h - $(CC) $(CFLAGS) genmidi.c - -stresspat.obj : stresspat.c - $(CC) $(CFLAGS) stresspat.c - -# could use -DNOFTELL here - -queues.obj: queues.c genmidi.h - $(CC) $(CFLAGS) queues.c - -# common midifile library -# -# could use -DNOFTELL here -midifile.obj : midifile.c midifile.h - $(CC) $(CFLAGS) midifile.c - -# objects needed by yaps -# -yapstree.obj: yapstree.c abc.h parseabc.h structs.h drawtune.h parser2.h - $(CC) $(CFLAGS) yapstree.c - -drawtune.obj: drawtune.c structs.h sizes.h abc.h drawtune.h - $(CC) $(CFLAGS) drawtune.c - -pslib.obj: pslib.c drawtune.h - $(CC) $(CFLAGS) pslib.c - -position.obj: position.c abc.h structs.h sizes.h - $(CC) $(CFLAGS) position.c - -debug.obj: debug.c structs.h abc.h - $(CC) $(CFLAGS) debug.c - -# objects needed by midi2abc -# -midi2abc.obj : midi2abc.c midifile.h - $(CC) $(CFLAGS) midi2abc.c - -# objects for mftext -# -crack.obj : crack.c - $(CC) $(CFLAGS) crack.c - -mftext.obj : mftext.c midifile.h - $(CC) $(CFLAGS) mftext.c - -# objects for midicopy -# -midicopy.obj :midicopy.c midicopy.h - $(CC) $(CFLAGS) midicopy.c - -# objects for abcmatch -# -abcmatch.obj :abcmatch.c abc.h - $(CC) $(CFLAGS) abcmatch.c - -matchsup.obj :matchsup.c abc.h parseabc.h parser2.h - $(CC) $(CFLAGS) matchsup.c - -clean: - rm *.obj - rm *.exe - -zipfile: midi2abc.exe abc2midi.exe mftext.exe yaps.exe abc2abc.exe - zip pcexe2.zip *.exe readme.txt abcguide.txt demo.abc yaps.txt +# Watcom DOS 32 bit Makefile for abcMIDI package +# Use mingw or gnu standard make program with this makefile. +# +# +# compilation #ifdefs - you may need to change these defined to get +# the code to compile with a different C compiler. +# +# NOFTELL in midifile.c and genmidi.c selects a version of the file-writing +# code which doesn't use file seeking. +# +# PCCFIX in mftext.c midifile.c midi2abc.c +# comments out various things that aren't available in PCC +# +# USE_INDEX causes index() to be used instead of strchr(). This is needed +# by some pre-ANSI C compilers. +# +# ASCTIME causes asctime() to be used instead of strftime() in pslib.c. +# If ANSILIBS is not set, neither routine is used. +# +# ANSILIBS causes code to include some ANSI standard headers +# +# KANDR selects functions prototypes without argument prototypes. +# +CC=wcc386 +CFLAGS=-ic:\\watcom\\h -w4 -e25 -zq -od -d2 -5r -bt=dos -mf -DANSILIBS +LDFLAGS=sys dos4g name +LDFLAGS2=d all op inc op st=200000 op maxe=25 op q op symf +LNK=wlink + +all : abc2midi.exe midi2abc.exe abc2abc.exe mftext.exe yaps.exe midicopy.exe abcmatch.exe + +abc2midi.exe : parseabc.obj store.obj genmidi.obj queues.obj midifile.obj parser2.obj -lm + $(LNK) $(LDFLAGS) abc2midi.exe $(LDFLAGS2) FILE parseabc.obj FILE genmidi.obj FILE store.obj \ + FILE queues.obj FILE midifile.obj FILE parser2.obj FILE stresspat.obj + +abc2abc.exe : parseabc.obj toabc.obj + $(LNK) $(LDFLAGS) abc2abc.exe $(LDFLAGS2) FILE parseabc.obj FILE toabc.obj + +midi2abc.exe : midifile.obj midi2abc.obj + $(LNK) $(LDFLAGS) midi2abc.exe $(LDFLAGS2) FILE midifile.obj FILE midi2abc.obj + +mftext.exe : midifile.obj mftext.obj crack.obj + $(LNK) $(LDFLAGS) mftext.exe $(LDFLAGS2) FILE midifile.obj FILE mftext.obj FILE crack.obj + +midicopy.exe: midicopy.obj + $(LNK) $(LDFLAGS) midicopy.exe $(LDFLAGS2) FILE midicopy.obj + +abcmatch.exe : abcmatch.obj matchsup.obj parseabc.obj + $(LNK) $(LDFLAGS) abcmatch.exe $(LDFLAGS) FILE abcmatch.obj matchsup.obj parseabc.obj + + + +yaps.exe : parseabc.obj yapstree.obj drawtune.obj debug.obj pslib.obj position.obj parser2.obj + $(LNK) $(LDFLAGS) yaps.exe $(LDFLAGS2) FILE parseabc.obj FILE yapstree.obj FILE drawtune.obj FILE debug.obj FILE position.obj FILE pslib.obj FILE parser2.obj + +# common parser object code +# +parseabc.obj : parseabc.c abc.h parseabc.h + $(CC) $(CFLAGS) parseabc.c + +parser2.obj : parser2.c parseabc.h parser2.h + $(CC) $(CFLAGS) parser2.c + +# objects needed by abc2abc +# +toabc.obj : toabc.c abc.h parseabc.h + $(CC) $(CFLAGS) toabc.c + +# objects needed by abc2midi +# +store.obj : store.c abc.h parseabc.h parser2.h genmidi.h + $(CC) $(CFLAGS) store.c + +genmidi.obj : genmidi.c abc.h midifile.h genmidi.h + $(CC) $(CFLAGS) genmidi.c + +stresspat.obj : stresspat.c + $(CC) $(CFLAGS) stresspat.c + +# could use -DNOFTELL here + +queues.obj: queues.c genmidi.h + $(CC) $(CFLAGS) queues.c + +# common midifile library +# +# could use -DNOFTELL here +midifile.obj : midifile.c midifile.h + $(CC) $(CFLAGS) midifile.c + +# objects needed by yaps +# +yapstree.obj: yapstree.c abc.h parseabc.h structs.h drawtune.h parser2.h + $(CC) $(CFLAGS) yapstree.c + +drawtune.obj: drawtune.c structs.h sizes.h abc.h drawtune.h + $(CC) $(CFLAGS) drawtune.c + +pslib.obj: pslib.c drawtune.h + $(CC) $(CFLAGS) pslib.c + +position.obj: position.c abc.h structs.h sizes.h + $(CC) $(CFLAGS) position.c + +debug.obj: debug.c structs.h abc.h + $(CC) $(CFLAGS) debug.c + +# objects needed by midi2abc +# +midi2abc.obj : midi2abc.c midifile.h + $(CC) $(CFLAGS) midi2abc.c + +# objects for mftext +# +crack.obj : crack.c + $(CC) $(CFLAGS) crack.c + +mftext.obj : mftext.c midifile.h + $(CC) $(CFLAGS) mftext.c + +# objects for midicopy +# +midicopy.obj :midicopy.c midicopy.h + $(CC) $(CFLAGS) midicopy.c + +# objects for abcmatch +# +abcmatch.obj :abcmatch.c abc.h + $(CC) $(CFLAGS) abcmatch.c + +matchsup.obj :matchsup.c abc.h parseabc.h parser2.h + $(CC) $(CFLAGS) matchsup.c + +clean: + rm *.obj + rm *.exe + +zipfile: midi2abc.exe abc2midi.exe mftext.exe yaps.exe abc2abc.exe + zip pcexe2.zip *.exe readme.txt abcguide.txt demo.abc yaps.txt diff -Nru abcmidi-20200706/makefiles/pcc.mak abcmidi-20200728/makefiles/pcc.mak --- abcmidi-20200706/makefiles/pcc.mak 2012-04-02 01:31:14.000000000 +0000 +++ abcmidi-20200728/makefiles/pcc.mak 2020-07-27 15:52:43.000000000 +0000 @@ -1,89 +1,89 @@ -# PCC Makefile for abcMIDI package -# -# -# compilation #ifdefs - you need to define some of these to get -# the code to compile with PCC. -# -# NOFTELL in midifile.c and genmidi.c selects a version of the file-writing -# code which doesn't use file seeking. -# -# PCCFIX in mftext.c midifile.c midi2abc.c -# comments out various things that aren't available in PCC -# and applies a fix needed for file writing -# -# ANSILIBS causes appropriate ANSI .h files to be #included. -# -# KANDR selects function prototypes with argument prototypes. -# -# USE_INDEX replaces calls to strchr() with calls to index(). -# - -CC=pcc -CFLAGS=-nPCCFIX -nNOFTELL -nUSE_INDEX -nKANDR -LNK=pccl - -all : abc2midi.exe midi2abc.exe abc2abc.exe mftext.exe yaps.exe midicopy.exe abcmatch.exe - -abc2midi.exe : parseabc.o store.o genmidi.o queues.o midifile.o parser2.o stresspat.o -lm - $(LNK) -Lc:\bin\pcc\ -Oabc2midi parseabc.o store.o genmidi.o queues.o midifile.o parser2.o stresspat.o - -abc2abc.exe : parseabc.o toabc.o - $(LNK) -Lc:\bin\pcc\ -Oabc2abc parseabc.o toabc.o - -midi2abc.exe : midifile.o midi2abc.o - $(LNK) -Lc:\bin\pcc\ midifile.o midi2abc.o -Omidi2abc - -mftext.exe : midifile.o mftext.o crack.o - $(LNK) -Lc:\bin\pcc\ midifile.o mftext.o crack.o -Omftext - -midicopy.exe: midicopy.o - $(LNK) -Lc:\bin\pcc\ midicopy.o -Omidicopy $(CFLAGS) - -parseabc.o : parseabc.c abc.h parseabc.h - $(CC) parseabc.c $(CFLAGS) - -parser2.o : parser2.c abc.h parseabc.h parser2.h - $(CC) parser2.c $(CFLAGS) - -toabc.o : toabc.c abc.h parseabc.h - $(CC) toabc.c $(CFLAGS) - -genmidi.o : genmidi.c abc.h midifile.h parseabc.h genmidi.h - $(CC) genmidi.c $(CFLAGS) - -stresspat.o : stresspat.c - $(CC) stresspat.c $(CFLAGS) - -store.o : store.c abc.h midifile.h parseabc.h - $(CC) store.c $(CFLAGS) - -queues.o : queues.c genmidi.h - $(CC) queues.c $(CFLAGS) - -midifile.o : midifile.c midifile.h - $(CC) midifile.c $(CFLAGS) - -midi2abc.o : midi2abc.c midifile.h - $(CC) midi2abc.c $(CFLAGS) - -crack.o : crack.c - $(CC) crack.c $(CFLAGS) - -mftext.o : mftext.c midifile.h - $(CC) mftext.c $(CFLAGS) - -midicopy.o : midicopy.c - $(CC) midicopy.c $(CFLAGS) - -abcmatch.o : abcmatch.c - $(CC) abcmatch.c $(CFLAGS) - -matchsup.o : matchsup.c - $(CC) matchsup.c $(CFLAGS) - -clean: - del *.exe - del *.o - -zipfile: abc2midi.exe midi2abc.exe abc2abc.exe mftext.exe midicopy.exe abcmatch.exe - zip pcexe.zip *.exe readme.txt abcguide.txt demo.abc +# PCC Makefile for abcMIDI package +# +# +# compilation #ifdefs - you need to define some of these to get +# the code to compile with PCC. +# +# NOFTELL in midifile.c and genmidi.c selects a version of the file-writing +# code which doesn't use file seeking. +# +# PCCFIX in mftext.c midifile.c midi2abc.c +# comments out various things that aren't available in PCC +# and applies a fix needed for file writing +# +# ANSILIBS causes appropriate ANSI .h files to be #included. +# +# KANDR selects function prototypes with argument prototypes. +# +# USE_INDEX replaces calls to strchr() with calls to index(). +# + +CC=pcc +CFLAGS=-nPCCFIX -nNOFTELL -nUSE_INDEX -nKANDR +LNK=pccl + +all : abc2midi.exe midi2abc.exe abc2abc.exe mftext.exe yaps.exe midicopy.exe abcmatch.exe + +abc2midi.exe : parseabc.o store.o genmidi.o queues.o midifile.o parser2.o stresspat.o -lm + $(LNK) -Lc:\bin\pcc\ -Oabc2midi parseabc.o store.o genmidi.o queues.o midifile.o parser2.o stresspat.o + +abc2abc.exe : parseabc.o toabc.o + $(LNK) -Lc:\bin\pcc\ -Oabc2abc parseabc.o toabc.o + +midi2abc.exe : midifile.o midi2abc.o + $(LNK) -Lc:\bin\pcc\ midifile.o midi2abc.o -Omidi2abc + +mftext.exe : midifile.o mftext.o crack.o + $(LNK) -Lc:\bin\pcc\ midifile.o mftext.o crack.o -Omftext + +midicopy.exe: midicopy.o + $(LNK) -Lc:\bin\pcc\ midicopy.o -Omidicopy $(CFLAGS) + +parseabc.o : parseabc.c abc.h parseabc.h + $(CC) parseabc.c $(CFLAGS) + +parser2.o : parser2.c abc.h parseabc.h parser2.h + $(CC) parser2.c $(CFLAGS) + +toabc.o : toabc.c abc.h parseabc.h + $(CC) toabc.c $(CFLAGS) + +genmidi.o : genmidi.c abc.h midifile.h parseabc.h genmidi.h + $(CC) genmidi.c $(CFLAGS) + +stresspat.o : stresspat.c + $(CC) stresspat.c $(CFLAGS) + +store.o : store.c abc.h midifile.h parseabc.h + $(CC) store.c $(CFLAGS) + +queues.o : queues.c genmidi.h + $(CC) queues.c $(CFLAGS) + +midifile.o : midifile.c midifile.h + $(CC) midifile.c $(CFLAGS) + +midi2abc.o : midi2abc.c midifile.h + $(CC) midi2abc.c $(CFLAGS) + +crack.o : crack.c + $(CC) crack.c $(CFLAGS) + +mftext.o : mftext.c midifile.h + $(CC) mftext.c $(CFLAGS) + +midicopy.o : midicopy.c + $(CC) midicopy.c $(CFLAGS) + +abcmatch.o : abcmatch.c + $(CC) abcmatch.c $(CFLAGS) + +matchsup.o : matchsup.c + $(CC) matchsup.c $(CFLAGS) + +clean: + del *.exe + del *.o + +zipfile: abc2midi.exe midi2abc.exe abc2abc.exe mftext.exe midicopy.exe abcmatch.exe + zip pcexe.zip *.exe readme.txt abcguide.txt demo.abc diff -Nru abcmidi-20200706/makefiles/unix.mak abcmidi-20200728/makefiles/unix.mak --- abcmidi-20200706/makefiles/unix.mak 2018-01-25 13:09:54.000000000 +0000 +++ abcmidi-20200728/makefiles/unix.mak 2020-07-27 15:52:43.000000000 +0000 @@ -1,131 +1,131 @@ -# Generic unix/gcc Makefile for abcMIDI package -# -# -# compilation #ifdefs - you need to compile with these defined to get -# the code to compile with PCC. -# -# NOFTELL in midifile.c and genmidi.c selects a version of the file-writing -# code which doesn't use file seeking. -# -# PCCFIX in mftext.c midifile.c midi2abc.c -# comments out various things that aren't available in PCC -# -# ANSILIBS includes some ANSI header files (which gcc can live without, -# but other compilers may want). -# -# USE_INDEX causes index() to be used instead of strchr(). This is needed -# by some pre-ANSI C compilers. -# -# ASCTIME causes asctime() to be used instead of strftime() in pslib.c. -# If ANSILIBS is not set, neither routine is used. -# -# KANDR selects functions prototypes without argument prototypes. -# currently yaps will only compile in ANSI mode. -# -# -# On running make, you may get the mysterious message : -# -# ', needed by `parseabc.o'. Stop `abc.h -# -# This means you are using GNU make and this file is in DOS text format. To -# cure the problem, change this file from using PC-style end-of-line (carriage -# return and line feed) to unix style end-of-line (line feed). - -CC=gcc -CFLAGS=-DANSILIBS -O2 -LNK=gcc -INSTALL=install - -prefix=/usr/local -binaries=abc2midi midi2abc abc2abc mftext yaps midicopy abcmatch - -docdir=share/doc/abcmidi -bindir=bin -mandir=share/man/man1 - -all : abc2midi midi2abc abc2abc mftext yaps midicopy abcmatch - -abc2midi : parseabc.o store.o genmidi.o midifile.o queues.o parser2.o stresspat.o -lm - $(LNK) -o abc2midi parseabc.o store.o genmidi.o queues.o \ - parser2.o midifile.o stresspat.o - -abc2abc : parseabc.o toabc.o - $(LNK) -o abc2abc parseabc.o toabc.o - -midi2abc : midifile.o midi2abc.o - $(LNK) midifile.o midi2abc.o -o midi2abc -lm - -mftext : midifile.o mftext.o crack.o - $(LNK) midifile.o mftext.o crack.o -o mftext - -yaps : parseabc.o yapstree.o drawtune.o debug.o pslib.o position.o parser2.o - $(LNK) -o yaps parseabc.o yapstree.o drawtune.o debug.o \ - position.o pslib.o parser2.o -o yaps - -midicopy : midicopy.o - $(LNK) -o midicopy midicopy.o - -abcmatch : abcmatch.o matchsup.o parseabc.o - $(LNK) abcmatch.o matchsup.o parseabc.o -o abcmatch - -parseabc.o : parseabc.c abc.h parseabc.h - -parser2.o : parser2.c abc.h parseabc.h parser2.h - -toabc.o : toabc.c abc.h parseabc.h - -# could use -DNOFTELL here -genmidi.o : genmidi.c abc.h midifile.h genmidi.h - -stresspat.o : stresspat.c - -store.o : store.c abc.h parseabc.h midifile.h genmidi.h - -queues.o : queues.c genmidi.h - -# could use -DNOFTELL here -midifile.o : midifile.c midifile.h - -midi2abc.o : midi2abc.c midifile.h - -midicopy.o : midicopy.c midicopy.h - -abcmatch.o: abcmatch.c abc.h - -crack.o : crack.c - -mftext.o : mftext.c midifile.h - -# objects needed by yaps -# -yapstree.o: yapstree.c abc.h parseabc.h structs.h drawtune.h - -drawtune.o: drawtune.c structs.h sizes.h abc.h drawtune.h - -pslib.o: pslib.c drawtune.h - -position.o: position.c abc.h structs.h sizes.h - -debug.o: debug.c structs.h abc.h - -#objects for abcmatch -# -matchsup.o : matchsup.c abc.h parseabc.h parser2.h - -clean : - rm *.o ${binaries} - -install: abc2midi midi2abc abc2abc mftext midicopy yaps abcmatch - $(INSTALL) -m 755 ${binaries} ${prefix}/${bindir} - - # install documentation - test -d ${PREFIX}/share/doc/abcmidi || mkdir -p ${prefix}/${docdir} - $(INSTALL) -m 644 doc/*.txt ${prefix}/${docdir} - $(INSTALL) -m 644 doc/AUTHORS ${prefix}/${docdir} - $(INSTALL) -m 644 doc/CHANGES ${prefix}/${docdir} - $(INSTALL) -m 644 VERSION ${prefix}/${docdir} - - # install manpages - test -d ${prefix}/${mandir} || mkdir -p ${prefix}/${mandir}; - $(INSTALL) -m 644 doc/*.1 ${prefix}/${mandir} - +# Generic unix/gcc Makefile for abcMIDI package +# +# +# compilation #ifdefs - you need to compile with these defined to get +# the code to compile with PCC. +# +# NOFTELL in midifile.c and genmidi.c selects a version of the file-writing +# code which doesn't use file seeking. +# +# PCCFIX in mftext.c midifile.c midi2abc.c +# comments out various things that aren't available in PCC +# +# ANSILIBS includes some ANSI header files (which gcc can live without, +# but other compilers may want). +# +# USE_INDEX causes index() to be used instead of strchr(). This is needed +# by some pre-ANSI C compilers. +# +# ASCTIME causes asctime() to be used instead of strftime() in pslib.c. +# If ANSILIBS is not set, neither routine is used. +# +# KANDR selects functions prototypes without argument prototypes. +# currently yaps will only compile in ANSI mode. +# +# +# On running make, you may get the mysterious message : +# +# ', needed by `parseabc.o'. Stop `abc.h +# +# This means you are using GNU make and this file is in DOS text format. To +# cure the problem, change this file from using PC-style end-of-line (carriage +# return and line feed) to unix style end-of-line (line feed). + +CC=gcc +CFLAGS=-DANSILIBS -O2 +LNK=gcc +INSTALL=install + +prefix=/usr/local +binaries=abc2midi midi2abc abc2abc mftext yaps midicopy abcmatch + +docdir=share/doc/abcmidi +bindir=bin +mandir=share/man/man1 + +all : abc2midi midi2abc abc2abc mftext yaps midicopy abcmatch + +abc2midi : parseabc.o store.o genmidi.o midifile.o queues.o parser2.o stresspat.o -lm + $(LNK) -o abc2midi parseabc.o store.o genmidi.o queues.o \ + parser2.o midifile.o stresspat.o + +abc2abc : parseabc.o toabc.o + $(LNK) -o abc2abc parseabc.o toabc.o + +midi2abc : midifile.o midi2abc.o + $(LNK) midifile.o midi2abc.o -o midi2abc -lm + +mftext : midifile.o mftext.o crack.o + $(LNK) midifile.o mftext.o crack.o -o mftext + +yaps : parseabc.o yapstree.o drawtune.o debug.o pslib.o position.o parser2.o + $(LNK) -o yaps parseabc.o yapstree.o drawtune.o debug.o \ + position.o pslib.o parser2.o -o yaps + +midicopy : midicopy.o + $(LNK) -o midicopy midicopy.o + +abcmatch : abcmatch.o matchsup.o parseabc.o + $(LNK) abcmatch.o matchsup.o parseabc.o -o abcmatch + +parseabc.o : parseabc.c abc.h parseabc.h + +parser2.o : parser2.c abc.h parseabc.h parser2.h + +toabc.o : toabc.c abc.h parseabc.h + +# could use -DNOFTELL here +genmidi.o : genmidi.c abc.h midifile.h genmidi.h + +stresspat.o : stresspat.c + +store.o : store.c abc.h parseabc.h midifile.h genmidi.h + +queues.o : queues.c genmidi.h + +# could use -DNOFTELL here +midifile.o : midifile.c midifile.h + +midi2abc.o : midi2abc.c midifile.h + +midicopy.o : midicopy.c midicopy.h + +abcmatch.o: abcmatch.c abc.h + +crack.o : crack.c + +mftext.o : mftext.c midifile.h + +# objects needed by yaps +# +yapstree.o: yapstree.c abc.h parseabc.h structs.h drawtune.h + +drawtune.o: drawtune.c structs.h sizes.h abc.h drawtune.h + +pslib.o: pslib.c drawtune.h + +position.o: position.c abc.h structs.h sizes.h + +debug.o: debug.c structs.h abc.h + +#objects for abcmatch +# +matchsup.o : matchsup.c abc.h parseabc.h parser2.h + +clean : + rm *.o ${binaries} + +install: abc2midi midi2abc abc2abc mftext midicopy yaps abcmatch + $(INSTALL) -m 755 ${binaries} ${prefix}/${bindir} + + # install documentation + test -d ${PREFIX}/share/doc/abcmidi || mkdir -p ${prefix}/${docdir} + $(INSTALL) -m 644 doc/*.txt ${prefix}/${docdir} + $(INSTALL) -m 644 doc/AUTHORS ${prefix}/${docdir} + $(INSTALL) -m 644 doc/CHANGES ${prefix}/${docdir} + $(INSTALL) -m 644 VERSION ${prefix}/${docdir} + + # install manpages + test -d ${prefix}/${mandir} || mkdir -p ${prefix}/${mandir}; + $(INSTALL) -m 644 doc/*.1 ${prefix}/${mandir} + diff -Nru abcmidi-20200706/matchsup.c abcmidi-20200728/matchsup.c --- abcmidi-20200706/matchsup.c 2020-07-06 12:02:54.000000000 +0000 +++ abcmidi-20200728/matchsup.c 2020-07-27 15:52:43.000000000 +0000 @@ -1,2026 +1,2026 @@ -/* - matchsup.c (abcmatch support functions) - Seymour Shlien - - - - - * originally store.c - * abc2midi - program to convert abc files to MIDI files. - * Copyright (C) 1999 James Allwright - * e-mail: J.R.Allwright@westminster.ac.uk - -store.c was adapted for the purpose of analyzing the contents -of abc tunes. The code is much the same except many functions -related to the generation of midi files has been trimmed off. -In particular: links to the code in genmidi.c and queue.c are gone. -All the gchord stuff is removed. There are options to suppress -all warning and error messages. Certain variables are no longer -extern's (parts, partno, bar_num, global_transpose etc.). -Output file generation stuff is gone. A new variable xrefno is -added to pass the reference number to abcmatch. Event_init has -been moved to the main program abcmatch.c and maxnotes has -been raised to 3000 since there seems to be a problem with -the autoextend procedure. Some DEBUG statements were -added to addfeature - (they can probably be removed). Support -for text features and comments are gone. All the karaoke stuff -is gone. Tempo stuff, slurs, grace notes, decorations, hornpipe -indications are all ignored. Event_note was changed to ignore -trills and rolls. When a [chord] is included, only the highest -note is extracted from the chord. For debugging, a new function -print_feature_list was introduced.etc. - -Essentially for comparing abc files, we want to ignore repeats, -grace notes, staccato indications and midi indications. We are -only interested in comparing the tunes. The main output -which is used is the feature[],pitch,num[],denom[] list which -is used by abcmatch to create a new representation of the abc -file. - -Some abc files with interleaved voices will not be treated -propertly with this program. In general parts, repeats and -voice indications are ignored. - - - * - * 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 - * - */ - - -#define XTEN1 1 - -#include "abc.h" -#include "parseabc.h" -#include "parser2.h" -#include -#include - -#ifdef WIN32 -#include /* [gjg] 2012-02-01 Visual C++ 2010 Express compatability */ -#endif - -#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 -#include -#include -#else -extern char* strchr(); -extern void reduce(); -#endif - -#define MAXLINE 500 -#define INITTEXTS 20 -#define INITWORDS 20 -#define MAXCHANS 16 - - -/*#define DEBUG*/ - - -/* global variables grouped roughly by function */ - -FILE *fp; - -programname fileprogram = ABCMATCH; - - -/* 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; -int foundtitle; /* flag for capturing on the first title */ - -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; - -int note_unit_length=8; - -/* general purpose storage structure */ -int maxnotes; -int *pitch, *num, *denom; -featuretype *feature; -int *pitchline; -int notes; - -int verbose = 0; -int nowarn=1; -int noerror=1; -int xmatch; -int sf, mi; - -/* Part handling */ -struct vstring part; -int parts, partno, partlabel; -int part_start[26], part_count[26]; - -int voicesused; - -/* Tempo handling (Q: field) */ -int time_num, time_denom; -long tempo; -int tempo_num, tempo_denom; -int relative_tempo, Qtempo; -extern int division; -extern int div_factor; - -/* output file generation */ -int check; -int ntracks; - -/* bar length checking */ -int bar_num, bar_denom; -int barchecking; -int beat; - -/* generating MIDI output */ -int middle_c; -extern int channels[MAXCHANS + 3]; - -int global_transpose; - -int additive; -int gfact_num, gfact_denom; - -/* karaoke handling */ -int karaoke, wcount; -char** words; -int maxwords = INITWORDS; -int xrefno; - -extern int intune; /* signals to parsetune that tune is finished */ -extern char titlename[48]; /* stores title of tune */ -extern char keysignature[16]; - -/* Many of these functions have been retained in order to link with parseabc. -As I have been forced to also modifiy parseabc, now called abcparse, these -functions can also be removed eventually. - */ - -char *featname[] = { -"SINGLE_BAR", "DOUBLE_BAR", "BAR_REP", "REP_BAR", -"PLAY_ON_REP", "REP1", "REP2", "BAR1", -"REP_BAR2", "DOUBLE_REP", "THICK_THIN", "THIN_THICK", -"PART", "TEMPO", "TIME", "KEY", -"REST", "TUPLE", "NOTE", "NONOTE", -"OLDTIE", "TEXT", "SLUR_ON", "SLUR_OFF", -"TIE", "CLOSE_TIE", "TITLE", "CHANNEL", -"TRANSPOSE", "RTRANSPOSE", "GTRANSPOSE", "GRACEON", -"GRACEOFF", "SETGRACE", "SETC", "SETTRIM", "GCHORD", -"GCHORDON", "GCHORDOFF", "VOICE", "CHORDON", -"CHORDOFF", "CHORDOFFEX", "DRUMON", "DRUMOFF", -"DRONEON", "DRONEOFF", "SLUR_TIE", "TNOTE", -"LT", "GT", "DYNAMIC", "LINENUM", -"MUSICLINE", "MUSICSTOP", "WORDLINE", "WORDSTOP", -"INSTRUCTION", "NOBEAM", "CHORDNOTE", "CLEF", -"PRINTLINE", "NEWPAGE", "LEFT_TEXT", "CENTRE_TEXT", -"VSKIP", "COPYRIGHT", "COMPOSER", "ARPEGGIO", -"SPLITVOICE", "META", "PEDAL_ON", "PEDAL_OFF", "EFFECT" -}; - - - -void event_info (place) -char * place; -{ -} - -void event_gchord (chord) -char * chord; -{ -} - -void event_slur (t) -int t; -{ -} - - -void event_instruction (s) -char *s; -{ -} - - -void event_reserved (p) -char p; -{ -} - -int bar_num, bar_denom, barno, barsize; -int b_num,b_denom; - -void reduce(a, b) -/* elimate common factors in fraction a/b */ -int *a, *b; -{ - int sign; - int t, n, m; - - if (*a < 0) { - sign = -1; - *a = -*a; - } else { - sign = 1; - }; - /* find HCF using Euclid's algorithm */ - if (*a > *b) { - n = *a; - m = *b; - } else { - n = *b; - m = *a; - }; - while (m != 0) { - t = n % m; - n = m; - m = t; - }; - *a = (*a/n)*sign; - *b = *b/n; -} - -void addunits(a, b) -/* add a/b to the count of units in the bar */ -int a, b; -{ - bar_num = bar_num*(b*b_denom) + (a*b_num)*bar_denom; - bar_denom = bar_denom * (b*b_denom); - reduce(&bar_num, &bar_denom); -} - - -void set_meter(n, m) -/* set up variables associated with meter */ -int n, m; -{ - /* set up barsize */ - barsize = n; - if (barsize % 3 == 0) { - beat = 3; - } else { - if (barsize % 2 == 0) { - beat = 2; - } else { - beat = barsize; - }; - }; - /* correction factor to make sure we count in the right units */ - if (m > 4) { - b_num = m/4; - b_denom = 1; - } else { - b_num = 1; - b_denom = 4/m; - }; -} - -int dummydecorator[DECSIZE]; /* used in event_chord */ - - -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; -} - - - - -void event_text(s) -/* text found in abc file */ -char *s; -{ -} - - -void event_specific (package, s) -char *package, *s; -{ -} - - -void event_x_reserved(p) -/* reserved character H-Z found in abc file */ -char p; -{ -} - -void event_abbreviation(symbol, string, container) -/* abbreviation encountered - this is handled within the parser */ -char symbol; -char *string; -char container; -{ -} - - -void event_acciaccatura() -{ -/* does nothing here but outputs a / in abc2abc */ -return; -} - -/* [SS] 2015-03-23 */ -void event_start_extended_overlay() -{ -event_error("extended overlay not implemented in abcmatch"); -} - -void event_stop_extended_overlay() -{ -event_error("extended overlay not implemented in abcmatch"); -} - - -void event_split_voice() -{ -} - - - -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; -{ -if (noerror) return; -#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; -{ -if (nowarn) return; -#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) { - 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; -{ -} - -void event_comment(s) -/* comment found in abc */ -char *s; -{ -} - - -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 event_field(k, f) -/* Handles R: T: and any other field not handled elsewhere */ -char k; -char *f; -{ - if (dotune) { - switch (k) { - case 'R': - { - char* p; - - p = f; - skipspace(&p); -/******** if ((strncmp(p, "Hornpipe", 8) == 0) || - (strncmp(p, "hornpipe", 8) == 0)) { - hornpipe = 1; - }; -********/ - }; - break; - case 'T': - if (foundtitle == 0) strncpy(titlename,f,46); - foundtitle = 1; /* [SS] 2014-01-01 */ - break; - default: - { - }; - }; - }; -} - -void event_words(p, continuation) -/* handles a w: field in the abc */ -char* p; -int continuation; -{ -} - -/* [SS] 2014-08-16 */ -void appendfield (morewords) -char *morewords; -{ -printf("appendfield not implemented here\n"); -} - - -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(1,1); - }; - if (v->ingrace != 0) { - event_error("Previous voice has unfinished grace notes"); - v->ingrace = 0; - }; -} - - -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; -{ -} - -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_octave(int, int); - -void event_voice(n, s, vp) -/* handles a V: field in the abc */ -int n; -char *s; -struct voice_params *vp; -{ - if (pastheader || XTEN1) { - voicesused = 1; - if (pastheader) checkbreak(); - v = getvoicecontext(n); - addfeature(VOICE, v->indexno, 0, 0); - if (vp->gotoctave) { - event_octave(vp->octave,1); - }; - if (vp->gottranspose) { - addfeature(TRANSPOSE, vp->transpose, 0, 0); - }; - } else { - event_warning("V: in header ignored"); - }; -} - - -void event_length(n) -/* handles an L: field in the abc */ -int n; -{ - note_unit_length = 8; - 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; -{ -} - -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; -{ -} - - -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, local) -/* used internally by other routines when octave=N is encountered */ -/* in I: or K: fields */ -int num; -{ - if (dotune) { - if (pastheader || local) { - 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,0); - }; -} - -static void stack_broken(v) -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) -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 */ -{ -} - -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 { - 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(1,1); - } else { - event_chordon(dummydecorator); - }; -} - -void event_ignore () { }; /* [SS] 2018-12-21 */ - - -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 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; - default: - num1=num2=1; /* [SDG] 2020-06-03 */ - }; - 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(); -} - -/* just a stub to ignore 'y' */ -void event_spacing(n, m) -int n,m; -{ -} - -void event_rest(decorators,n,m,type) -/* rest of n/m in the abc */ -int n, m,type; -int decorators[DECSIZE]; -{ - 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)) { - 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,c) -/* multiple bar rest of n/m in the abc */ -/* we check for m == 1 in the parser */ -int n, m; -char c; /* [SS] 2017-04-19 to distinguish X from Z in abc2abc */ -{ - int i; - int decorators[DECSIZE]; - decorators[FERMATA]=0; -/* it is not legal to pass a fermata to a multirest */ - - for (i=0; idefault_length), time_denom,0); - if (i != n-1) { - event_bar(SINGLE_BAR, ""); - }; - }; -} - - - -void event_chordon(int chorddecorators[]) -/* handles a chord start [ in the abc */ -/* the array chorddecorators is needed in toabc.c and yapstree.c */ -/* but is not relevant here. */ - -{ - 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(int chord_n, int chord_m) -/* handles a chord close ] in the abc */ -{ - if (!v->inchord) { - event_error("Chord already finished"); - } else { - - if(chord_m == 1 && chord_n == 1) /* chord length not set outside [] */ - addfeature(CHORDOFF, 0, v->chord_num, v->chord_denom); - else - addfeature(CHORDOFFEX, 0, chord_n*4, chord_m*v->default_length); - - v->inchord = 0; - v->chordcount = 0; - marknoteend(); - if (tuplecount > 0) --tuplecount; - }; -} - - -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 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 pitch_noacc; - 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); - pitch_noacc = pitchof(note,0,0,octave,0); - if (decorators[FERMATA]) { - num = num*2; - }; - if (v->chordcount == 1) { - v->chord_num = num*4; - v->chord_denom = denom*(v->default_length); - }; - - pitchline[notes] = pitch_noacc; - addfeature(NOTE, pitch, num*4, denom*(v->default_length)); - marknote(); -} - -void event_microtone(int dir, int a, int b) -{ -} - -void event_temperament(char **line) { -} - - -void event_normal_tone() -{ -} - - - -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; -{ -} - -void event_handle_instruction(s) -/* handler for ! ! instructions */ -/* does ppp pp p mp mf f ff fff */ -/* also does !drum! and !nodrum! */ -char* s; -{ -} - -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,voiceno) -/* called in preprocessing stage to handle ties */ -/* we need the voiceno in case a tie is broken by a */ -/* voice switch. */ -int j, xinchord,voiceno; -{ - int tienote, place; - int tietodo, done; - int lastnote, lasttie; - int inchord; - int tied_num, tied_denom; - int localvoiceno; - int samechord; - - /* find note to be tied */ - samechord = 0; - if (xinchord) samechord = 1; - tienote = j; - localvoiceno = voiceno; - 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)) { - /* printf("%d %s %d %d/%d ",place,featname[feature[place]],pitch[place],num[place],denom[place]); */ - switch (feature[place]) { - case NOTE: - if(localvoiceno != voiceno) break; - lastnote = place; - if ((tied_num == 0) && (tietodo == 0)) { - done = 1; - }; - if ((pitchline[place] == pitchline[tienote]) - && (tietodo == 1) && (samechord == 0)) { - /* tie in note */ - if (tied_num != 0) { - event_error("Time mismatch at tie"); - }; - tietodo = 0; - pitch[place] = pitch[tienote]; /* in case accidentals did not - propagate */ - /* 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(localvoiceno != voiceno) break; - 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(localvoiceno != voiceno) break; - if (lastnote == -1) { - event_error("Bad tie: possibly two ties in a row"); - } else { - if (pitch[lastnote] == pitch[tienote] && samechord == 0) { - lasttie = place; - tietodo = 1; - if (inchord) samechord = 1; - }; - }; - break; - case CHORDON: - if(localvoiceno != voiceno) break; - inchord = 1; - break; - case CHORDOFF: - case CHORDOFFEX: - samechord = 0; - if(localvoiceno != voiceno) break; - inchord = 0; - /* subtract time from tied time */ - addfract(&tied_num, &tied_denom, -num[place], denom[place]); - break; - case VOICE: - localvoiceno = pitch[place]; - default: - break; - }; - /*printf("tied_num = %d done = %d inchord = %d\n",tied_num, done, inchord); */ - place = place + 1; - }; - if (tietodo == 1) { - event_error("Could not find note to be tied"); - }; - }; -/* printf("dotie finished\n"); */ -} - -static void tiefix() -/* connect up tied notes and cleans up the */ -/* note lengths in the chords (eg [ace]3 ) */ -{ - int j; - int inchord; - int chord_num=-1, chord_denom=1; /*[SDG] 2020-06-03 */ - int chord_start,chord_end; - int voiceno; - - j = 0; - inchord = 0; - voiceno = 1; - while (j 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); - }; -*/ -} - - - - -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; - time_num = 4; - time_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); - } - - -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; */ - voicesused = 0; -} - -void event_key(sharps, s, modeindex, modmap, modmul, modmicrotone, gotkey, gotclef, clefname, - octave, transpose, gotoctave, gottranspose, explict) -/* handles a K: field */ -int sharps; /* sharps is number of sharps in key signature */ -int modeindex; /* 0 major, 1,2,3 minor, 4 locrian, etc. */ -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) */ -struct fraction modmicrotone[7]; /* [SS] 2014-01-06 */ -int gotkey, gotclef; -int octave, transpose, gotoctave, gottranspose; -int explict; -char* clefname; -{ - int minor; - strncpy(keysignature,s,16); - if (modeindex >0 && modeindex <4) minor = 1; - 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,0); - }; - }; -} - - - -void print_feature_list () -{ -int i,length; -float fract; -printf("feature list \n"); -for (i=0;i -1) { - addfeature(PART, ' ', 0, 0); - }; - if (headerpartlabel == 1) { - event_error("P: field in header should go after K: field"); - }; - - tiefix(); - - - }; - for (i=0; i73) printf("illegal feature[%d] = %d\n",i,j); /* [SS] 2012-11-25 */ - else printf("%d %s %d %d %d \n",i,featname[j],pitch[i],num[i],denom[i]); - /* [SDG] 2020-06-03 removed extra %d */ - } -} - - +/* + matchsup.c (abcmatch support functions) + Seymour Shlien + + + + + * originally store.c + * abc2midi - program to convert abc files to MIDI files. + * Copyright (C) 1999 James Allwright + * e-mail: J.R.Allwright@westminster.ac.uk + +store.c was adapted for the purpose of analyzing the contents +of abc tunes. The code is much the same except many functions +related to the generation of midi files has been trimmed off. +In particular: links to the code in genmidi.c and queue.c are gone. +All the gchord stuff is removed. There are options to suppress +all warning and error messages. Certain variables are no longer +extern's (parts, partno, bar_num, global_transpose etc.). +Output file generation stuff is gone. A new variable xrefno is +added to pass the reference number to abcmatch. Event_init has +been moved to the main program abcmatch.c and maxnotes has +been raised to 3000 since there seems to be a problem with +the autoextend procedure. Some DEBUG statements were +added to addfeature - (they can probably be removed). Support +for text features and comments are gone. All the karaoke stuff +is gone. Tempo stuff, slurs, grace notes, decorations, hornpipe +indications are all ignored. Event_note was changed to ignore +trills and rolls. When a [chord] is included, only the highest +note is extracted from the chord. For debugging, a new function +print_feature_list was introduced.etc. + +Essentially for comparing abc files, we want to ignore repeats, +grace notes, staccato indications and midi indications. We are +only interested in comparing the tunes. The main output +which is used is the feature[],pitch,num[],denom[] list which +is used by abcmatch to create a new representation of the abc +file. + +Some abc files with interleaved voices will not be treated +propertly with this program. In general parts, repeats and +voice indications are ignored. + + + * + * 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 + * + */ + + +#define XTEN1 1 + +#include "abc.h" +#include "parseabc.h" +#include "parser2.h" +#include +#include + +#ifdef WIN32 +#include /* [gjg] 2012-02-01 Visual C++ 2010 Express compatability */ +#endif + +#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 +#include +#include +#else +extern char* strchr(); +extern void reduce(); +#endif + +#define MAXLINE 500 +#define INITTEXTS 20 +#define INITWORDS 20 +#define MAXCHANS 16 + + +/*#define DEBUG*/ + + +/* global variables grouped roughly by function */ + +FILE *fp; + +programname fileprogram = ABCMATCH; + + +/* 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; +int foundtitle; /* flag for capturing on the first title */ + +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; + +int note_unit_length=8; + +/* general purpose storage structure */ +int maxnotes; +int *pitch, *num, *denom; +featuretype *feature; +int *pitchline; +int notes; + +int verbose = 0; +int nowarn=1; +int noerror=1; +int xmatch; +int sf, mi; + +/* Part handling */ +struct vstring part; +int parts, partno, partlabel; +int part_start[26], part_count[26]; + +int voicesused; + +/* Tempo handling (Q: field) */ +int time_num, time_denom; +long tempo; +int tempo_num, tempo_denom; +int relative_tempo, Qtempo; +extern int division; +extern int div_factor; + +/* output file generation */ +int check; +int ntracks; + +/* bar length checking */ +int bar_num, bar_denom; +int barchecking; +int beat; + +/* generating MIDI output */ +int middle_c; +extern int channels[MAXCHANS + 3]; + +int global_transpose; + +int additive; +int gfact_num, gfact_denom; + +/* karaoke handling */ +int karaoke, wcount; +char** words; +int maxwords = INITWORDS; +int xrefno; + +extern int intune; /* signals to parsetune that tune is finished */ +extern char titlename[48]; /* stores title of tune */ +extern char keysignature[16]; + +/* Many of these functions have been retained in order to link with parseabc. +As I have been forced to also modifiy parseabc, now called abcparse, these +functions can also be removed eventually. + */ + +char *featname[] = { +"SINGLE_BAR", "DOUBLE_BAR", "BAR_REP", "REP_BAR", +"PLAY_ON_REP", "REP1", "REP2", "BAR1", +"REP_BAR2", "DOUBLE_REP", "THICK_THIN", "THIN_THICK", +"PART", "TEMPO", "TIME", "KEY", +"REST", "TUPLE", "NOTE", "NONOTE", +"OLDTIE", "TEXT", "SLUR_ON", "SLUR_OFF", +"TIE", "CLOSE_TIE", "TITLE", "CHANNEL", +"TRANSPOSE", "RTRANSPOSE", "GTRANSPOSE", "GRACEON", +"GRACEOFF", "SETGRACE", "SETC", "SETTRIM", "GCHORD", +"GCHORDON", "GCHORDOFF", "VOICE", "CHORDON", +"CHORDOFF", "CHORDOFFEX", "DRUMON", "DRUMOFF", +"DRONEON", "DRONEOFF", "SLUR_TIE", "TNOTE", +"LT", "GT", "DYNAMIC", "LINENUM", +"MUSICLINE", "MUSICSTOP", "WORDLINE", "WORDSTOP", +"INSTRUCTION", "NOBEAM", "CHORDNOTE", "CLEF", +"PRINTLINE", "NEWPAGE", "LEFT_TEXT", "CENTRE_TEXT", +"VSKIP", "COPYRIGHT", "COMPOSER", "ARPEGGIO", +"SPLITVOICE", "META", "PEDAL_ON", "PEDAL_OFF", "EFFECT" +}; + + + +void event_info (place) +char * place; +{ +} + +void event_gchord (chord) +char * chord; +{ +} + +void event_slur (t) +int t; +{ +} + + +void event_instruction (s) +char *s; +{ +} + + +void event_reserved (p) +char p; +{ +} + +int bar_num, bar_denom, barno, barsize; +int b_num,b_denom; + +void reduce(a, b) +/* elimate common factors in fraction a/b */ +int *a, *b; +{ + int sign; + int t, n, m; + + if (*a < 0) { + sign = -1; + *a = -*a; + } else { + sign = 1; + }; + /* find HCF using Euclid's algorithm */ + if (*a > *b) { + n = *a; + m = *b; + } else { + n = *b; + m = *a; + }; + while (m != 0) { + t = n % m; + n = m; + m = t; + }; + *a = (*a/n)*sign; + *b = *b/n; +} + +void addunits(a, b) +/* add a/b to the count of units in the bar */ +int a, b; +{ + bar_num = bar_num*(b*b_denom) + (a*b_num)*bar_denom; + bar_denom = bar_denom * (b*b_denom); + reduce(&bar_num, &bar_denom); +} + + +void set_meter(n, m) +/* set up variables associated with meter */ +int n, m; +{ + /* set up barsize */ + barsize = n; + if (barsize % 3 == 0) { + beat = 3; + } else { + if (barsize % 2 == 0) { + beat = 2; + } else { + beat = barsize; + }; + }; + /* correction factor to make sure we count in the right units */ + if (m > 4) { + b_num = m/4; + b_denom = 1; + } else { + b_num = 1; + b_denom = 4/m; + }; +} + +int dummydecorator[DECSIZE]; /* used in event_chord */ + + +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; +} + + + + +void event_text(s) +/* text found in abc file */ +char *s; +{ +} + + +void event_specific (package, s) +char *package, *s; +{ +} + + +void event_x_reserved(p) +/* reserved character H-Z found in abc file */ +char p; +{ +} + +void event_abbreviation(symbol, string, container) +/* abbreviation encountered - this is handled within the parser */ +char symbol; +char *string; +char container; +{ +} + + +void event_acciaccatura() +{ +/* does nothing here but outputs a / in abc2abc */ +return; +} + +/* [SS] 2015-03-23 */ +void event_start_extended_overlay() +{ +event_error("extended overlay not implemented in abcmatch"); +} + +void event_stop_extended_overlay() +{ +event_error("extended overlay not implemented in abcmatch"); +} + + +void event_split_voice() +{ +} + + + +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; +{ +if (noerror) return; +#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; +{ +if (nowarn) return; +#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) { + 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; +{ +} + +void event_comment(s) +/* comment found in abc */ +char *s; +{ +} + + +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 event_field(k, f) +/* Handles R: T: and any other field not handled elsewhere */ +char k; +char *f; +{ + if (dotune) { + switch (k) { + case 'R': + { + char* p; + + p = f; + skipspace(&p); +/******** if ((strncmp(p, "Hornpipe", 8) == 0) || + (strncmp(p, "hornpipe", 8) == 0)) { + hornpipe = 1; + }; +********/ + }; + break; + case 'T': + if (foundtitle == 0) strncpy(titlename,f,46); + foundtitle = 1; /* [SS] 2014-01-01 */ + break; + default: + { + }; + }; + }; +} + +void event_words(p, continuation) +/* handles a w: field in the abc */ +char* p; +int continuation; +{ +} + +/* [SS] 2014-08-16 */ +void appendfield (morewords) +char *morewords; +{ +printf("appendfield not implemented here\n"); +} + + +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(1,1); + }; + if (v->ingrace != 0) { + event_error("Previous voice has unfinished grace notes"); + v->ingrace = 0; + }; +} + + +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; +{ +} + +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_octave(int, int); + +void event_voice(n, s, vp) +/* handles a V: field in the abc */ +int n; +char *s; +struct voice_params *vp; +{ + if (pastheader || XTEN1) { + voicesused = 1; + if (pastheader) checkbreak(); + v = getvoicecontext(n); + addfeature(VOICE, v->indexno, 0, 0); + if (vp->gotoctave) { + event_octave(vp->octave,1); + }; + if (vp->gottranspose) { + addfeature(TRANSPOSE, vp->transpose, 0, 0); + }; + } else { + event_warning("V: in header ignored"); + }; +} + + +void event_length(n) +/* handles an L: field in the abc */ +int n; +{ + note_unit_length = 8; + 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; +{ +} + +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; +{ +} + + +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, local) +/* used internally by other routines when octave=N is encountered */ +/* in I: or K: fields */ +int num; +{ + if (dotune) { + if (pastheader || local) { + 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,0); + }; +} + +static void stack_broken(v) +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) +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 */ +{ +} + +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 { + 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(1,1); + } else { + event_chordon(dummydecorator); + }; +} + +void event_ignore () { }; /* [SS] 2018-12-21 */ + + +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 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; + default: + num1=num2=1; /* [SDG] 2020-06-03 */ + }; + 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(); +} + +/* just a stub to ignore 'y' */ +void event_spacing(n, m) +int n,m; +{ +} + +void event_rest(decorators,n,m,type) +/* rest of n/m in the abc */ +int n, m,type; +int decorators[DECSIZE]; +{ + 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)) { + 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,c) +/* multiple bar rest of n/m in the abc */ +/* we check for m == 1 in the parser */ +int n, m; +char c; /* [SS] 2017-04-19 to distinguish X from Z in abc2abc */ +{ + int i; + int decorators[DECSIZE]; + decorators[FERMATA]=0; +/* it is not legal to pass a fermata to a multirest */ + + for (i=0; idefault_length), time_denom,0); + if (i != n-1) { + event_bar(SINGLE_BAR, ""); + }; + }; +} + + + +void event_chordon(int chorddecorators[]) +/* handles a chord start [ in the abc */ +/* the array chorddecorators is needed in toabc.c and yapstree.c */ +/* but is not relevant here. */ + +{ + 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(int chord_n, int chord_m) +/* handles a chord close ] in the abc */ +{ + if (!v->inchord) { + event_error("Chord already finished"); + } else { + + if(chord_m == 1 && chord_n == 1) /* chord length not set outside [] */ + addfeature(CHORDOFF, 0, v->chord_num, v->chord_denom); + else + addfeature(CHORDOFFEX, 0, chord_n*4, chord_m*v->default_length); + + v->inchord = 0; + v->chordcount = 0; + marknoteend(); + if (tuplecount > 0) --tuplecount; + }; +} + + +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 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 pitch_noacc; + 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); + pitch_noacc = pitchof(note,0,0,octave,0); + if (decorators[FERMATA]) { + num = num*2; + }; + if (v->chordcount == 1) { + v->chord_num = num*4; + v->chord_denom = denom*(v->default_length); + }; + + pitchline[notes] = pitch_noacc; + addfeature(NOTE, pitch, num*4, denom*(v->default_length)); + marknote(); +} + +void event_microtone(int dir, int a, int b) +{ +} + +void event_temperament(char **line) { +} + + +void event_normal_tone() +{ +} + + + +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; +{ +} + +void event_handle_instruction(s) +/* handler for ! ! instructions */ +/* does ppp pp p mp mf f ff fff */ +/* also does !drum! and !nodrum! */ +char* s; +{ +} + +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,voiceno) +/* called in preprocessing stage to handle ties */ +/* we need the voiceno in case a tie is broken by a */ +/* voice switch. */ +int j, xinchord,voiceno; +{ + int tienote, place; + int tietodo, done; + int lastnote, lasttie; + int inchord; + int tied_num, tied_denom; + int localvoiceno; + int samechord; + + /* find note to be tied */ + samechord = 0; + if (xinchord) samechord = 1; + tienote = j; + localvoiceno = voiceno; + 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)) { + /* printf("%d %s %d %d/%d ",place,featname[feature[place]],pitch[place],num[place],denom[place]); */ + switch (feature[place]) { + case NOTE: + if(localvoiceno != voiceno) break; + lastnote = place; + if ((tied_num == 0) && (tietodo == 0)) { + done = 1; + }; + if ((pitchline[place] == pitchline[tienote]) + && (tietodo == 1) && (samechord == 0)) { + /* tie in note */ + if (tied_num != 0) { + event_error("Time mismatch at tie"); + }; + tietodo = 0; + pitch[place] = pitch[tienote]; /* in case accidentals did not + propagate */ + /* 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(localvoiceno != voiceno) break; + 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(localvoiceno != voiceno) break; + if (lastnote == -1) { + event_error("Bad tie: possibly two ties in a row"); + } else { + if (pitch[lastnote] == pitch[tienote] && samechord == 0) { + lasttie = place; + tietodo = 1; + if (inchord) samechord = 1; + }; + }; + break; + case CHORDON: + if(localvoiceno != voiceno) break; + inchord = 1; + break; + case CHORDOFF: + case CHORDOFFEX: + samechord = 0; + if(localvoiceno != voiceno) break; + inchord = 0; + /* subtract time from tied time */ + addfract(&tied_num, &tied_denom, -num[place], denom[place]); + break; + case VOICE: + localvoiceno = pitch[place]; + default: + break; + }; + /*printf("tied_num = %d done = %d inchord = %d\n",tied_num, done, inchord); */ + place = place + 1; + }; + if (tietodo == 1) { + event_error("Could not find note to be tied"); + }; + }; +/* printf("dotie finished\n"); */ +} + +static void tiefix() +/* connect up tied notes and cleans up the */ +/* note lengths in the chords (eg [ace]3 ) */ +{ + int j; + int inchord; + int chord_num=-1, chord_denom=1; /*[SDG] 2020-06-03 */ + int chord_start,chord_end; + int voiceno; + + j = 0; + inchord = 0; + voiceno = 1; + while (j 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); + }; +*/ +} + + + + +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; + time_num = 4; + time_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); + } + + +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; */ + voicesused = 0; +} + +void event_key(sharps, s, modeindex, modmap, modmul, modmicrotone, gotkey, gotclef, clefname, + octave, transpose, gotoctave, gottranspose, explict) +/* handles a K: field */ +int sharps; /* sharps is number of sharps in key signature */ +int modeindex; /* 0 major, 1,2,3 minor, 4 locrian, etc. */ +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) */ +struct fraction modmicrotone[7]; /* [SS] 2014-01-06 */ +int gotkey, gotclef; +int octave, transpose, gotoctave, gottranspose; +int explict; +char* clefname; +{ + int minor; + strncpy(keysignature,s,16); + if (modeindex >0 && modeindex <4) minor = 1; + 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,0); + }; + }; +} + + + +void print_feature_list () +{ +int i,length; +float fract; +printf("feature list \n"); +for (i=0;i -1) { + addfeature(PART, ' ', 0, 0); + }; + if (headerpartlabel == 1) { + event_error("P: field in header should go after K: field"); + }; + + tiefix(); + + + }; + for (i=0; i73) printf("illegal feature[%d] = %d\n",i,j); /* [SS] 2012-11-25 */ + else printf("%d %s %d %d %d \n",i,featname[j],pitch[i],num[i],denom[i]); + /* [SDG] 2020-06-03 removed extra %d */ + } +} + + diff -Nru abcmidi-20200706/mftext.c abcmidi-20200728/mftext.c --- abcmidi-20200706/mftext.c 2020-06-06 23:07:18.000000000 +0000 +++ abcmidi-20200728/mftext.c 2020-07-27 15:52:43.000000000 +0000 @@ -1,291 +1,291 @@ -/* - * mftext - * - * Convert a MIDI file to verbose text. - * - * modified for portability 22/6/98 - error checking on file open failure - * removed. - */ - -#include -#include -#ifdef ANSILIBS -#include -#endif -#include "midifile.h" - -void initfuncs(); -void midifile(); - -static FILE *F; -int SECONDS; /* global that tells whether to display seconds or ticks */ -int division; /* from the file header */ -long tempo = 500000; /* the default tempo is 120 beats/minute */ - -int filegetc() -{ - return(getc(F)); -} - -/* for crack */ -extern int arg_index; - -int main(argc,argv) -int argc; -char **argv; -{ - FILE *efopen(); - char *ch; - char *crack(); - - SECONDS = 0; - - while((ch = crack(argc,argv,"s",0)) != NULL){ - switch(*ch){ - case 's' : SECONDS = 1; break; - } - } - - if ((argc < 2 && !SECONDS) || (argc < 3 && SECONDS)) - F = stdin; - else - F = efopen(argv[arg_index],"rb"); - - initfuncs(); - Mf_getc = filegetc; - midifile(); - fclose(F); - exit(0); -} - -FILE * -efopen(name,mode) -char *name; -char *mode; -{ - FILE *f; - - if ( (f=fopen(name,mode)) == NULL ) { - fprintf(stderr,"Error - Cannot open file %s\n",name); - exit(0); - } - return(f); -} - -void error(s) -char *s; -{ - fprintf(stderr,"Error: %s\n",s); -} - -void prtime() -{ - if(SECONDS) - printf("Time=%f ",mf_ticks2sec(Mf_currtime,division,tempo)); - else - printf("Time=%ld ",Mf_currtime); -} - -void txt_header(format,ntrks,ldivision) -int format, ntrks, ldivision; -{ - division = ldivision; - printf("Header format=%d ntrks=%d division=%d\n",format,ntrks,division); -} - -void txt_trackstart() -{ - printf("Track start\n"); -} - -void txt_trackend() -{ - printf("Track end\n"); -} - -void txt_noteon(chan,pitch,vol) -int chan, pitch, vol; -{ - prtime(); - printf("Note on, chan=%d pitch=%d vol=%d\n",chan+1,pitch,vol); -} - -void txt_noteoff(chan,pitch,vol) -int chan, pitch, vol; -{ - prtime(); - printf("Note off, chan=%d pitch=%d vol=%d\n",chan+1,pitch,vol); -} - -void txt_pressure(chan,pitch,press) -int chan, pitch,press; -{ - prtime(); - printf("Pressure, chan=%d pitch=%d press=%d\n",chan+1,pitch,press); -} - -void txt_parameter(chan,control,value) -int chan, control, value; -{ - prtime(); - printf("Parameter, chan=%d c1=%d c2=%d\n",chan+1,control,value); -} - - -/* [SS] 2017-11-16 submitted by Jonathan Hough (msb,lsb interchanged) */ -void txt_pitchbend(chan,lsb,msb) -int chan, msb, lsb; -{ - prtime(); - printf("Pitchbend, chan=%d lsb=%d msb=%d\n",chan+1,msb,lsb); -} - -void txt_program(chan,program) -int chan, program; -{ - prtime(); - printf("Program, chan=%d program=%d\n",chan+1,program); -} - -void txt_chanpressure(chan,press) -int chan, press; -{ - prtime(); - printf("Channel pressure, chan=%d pressure=%d\n",chan+1,press); -} - -void txt_sysex(leng,mess) -int leng; -char *mess; -{ - prtime(); - printf("Sysex, leng=%d\n",leng); -} - -void txt_metamisc(type,leng,mess) -int type, leng; -char *mess; -{ - prtime(); - printf("Meta event, unrecognized, type=0x%02x leng=%d\n",type,leng); -} - -void txt_metaspecial(type,leng,mess) -int type, leng; -char *mess; -{ - prtime(); - printf("Meta event, sequencer-specific, type=0x%02x leng=%d\n",type,leng); -} - -void txt_metatext(type,leng,mess) -int type, leng; -char *mess; -{ - static char *ttype[] = { - NULL, - "Text Event", /* type=0x01 */ - "Copyright Notice", /* type=0x02 */ - "Sequence/Track Name", - "Instrument Name", /* ... */ - "Lyric", - "Marker", - "Cue Point", /* type=0x07 */ - "Unrecognized" - }; - int unrecognized = (sizeof(ttype)/sizeof(char *)) - 1; - register int n, c; - register char *p = mess; - - if ( type < 1 || type > unrecognized ) - type = unrecognized; - prtime(); - printf("Meta Text, type=0x%02x (%s) leng=%d\n",type,ttype[type],leng); - printf(" Text = <"); - for ( n=0; n\n"); -} - -void txt_metaseq(num) -int num; -{ - prtime(); - printf("Meta event, sequence number = %d\n",num); -} - -void txt_metaeot() -{ - prtime(); - printf("Meta event, end of track\n"); -} - -void txt_keysig(sf,mi) -int sf,mi; -{ - prtime(); - printf("Key signature, sharp/flats=%d minor=%d\n",sf,mi); -} - -void txt_tempo(ltempo) -long ltempo; -{ - tempo = ltempo; - prtime(); - printf("Tempo, microseconds-per-MIDI-quarter-note=%ld\n",tempo); -} - -void txt_timesig(nn,dd,cc,bb) -int nn, dd, cc, bb; -{ - int denom = 1; - while ( dd-- > 0 ) - denom *= 2; - prtime(); - printf("Time signature=%d/%d MIDI-clocks/click=%d 32nd-notes/24-MIDI-clocks=%d\n", - nn,denom,cc,bb); -} - -void txt_smpte(hr,mn,se,fr,ff) -int hr, mn, se, fr, ff; -{ - prtime(); - printf("SMPTE, hour=%d minute=%d second=%d frame=%d fract-frame=%d\n", - hr,mn,se,fr,ff); -} - -void txt_arbitrary(leng,mess) -int leng; /* [SDE] 2020-06-03 */ -char *mess; -{ - prtime(); - printf("Arbitrary bytes, leng=%d\n",leng); -} - -void initfuncs() -{ - Mf_error = error; - Mf_header = txt_header; - Mf_trackstart = txt_trackstart; - Mf_trackend = txt_trackend; - Mf_noteon = txt_noteon; - Mf_noteoff = txt_noteoff; - Mf_pressure = txt_pressure; - Mf_parameter = txt_parameter; - Mf_pitchbend = txt_pitchbend; - Mf_program = txt_program; - Mf_chanpressure = txt_chanpressure; - Mf_sysex = txt_sysex; - Mf_metamisc = txt_metamisc; - Mf_seqnum = txt_metaseq; - Mf_eot = txt_metaeot; - Mf_timesig = txt_timesig; - Mf_smpte = txt_smpte; - Mf_tempo = txt_tempo; - Mf_keysig = txt_keysig; - Mf_seqspecific = txt_metaspecial; - Mf_text = txt_metatext; - Mf_arbitrary = txt_arbitrary; -} +/* + * mftext + * + * Convert a MIDI file to verbose text. + * + * modified for portability 22/6/98 - error checking on file open failure + * removed. + */ + +#include +#include +#ifdef ANSILIBS +#include +#endif +#include "midifile.h" + +void initfuncs(); +void midifile(); + +static FILE *F; +int SECONDS; /* global that tells whether to display seconds or ticks */ +int division; /* from the file header */ +long tempo = 500000; /* the default tempo is 120 beats/minute */ + +int filegetc() +{ + return(getc(F)); +} + +/* for crack */ +extern int arg_index; + +int main(argc,argv) +int argc; +char **argv; +{ + FILE *efopen(); + char *ch; + char *crack(); + + SECONDS = 0; + + while((ch = crack(argc,argv,"s",0)) != NULL){ + switch(*ch){ + case 's' : SECONDS = 1; break; + } + } + + if ((argc < 2 && !SECONDS) || (argc < 3 && SECONDS)) + F = stdin; + else + F = efopen(argv[arg_index],"rb"); + + initfuncs(); + Mf_getc = filegetc; + midifile(); + fclose(F); + exit(0); +} + +FILE * +efopen(name,mode) +char *name; +char *mode; +{ + FILE *f; + + if ( (f=fopen(name,mode)) == NULL ) { + fprintf(stderr,"Error - Cannot open file %s\n",name); + exit(0); + } + return(f); +} + +void error(s) +char *s; +{ + fprintf(stderr,"Error: %s\n",s); +} + +void prtime() +{ + if(SECONDS) + printf("Time=%f ",mf_ticks2sec(Mf_currtime,division,tempo)); + else + printf("Time=%ld ",Mf_currtime); +} + +void txt_header(format,ntrks,ldivision) +int format, ntrks, ldivision; +{ + division = ldivision; + printf("Header format=%d ntrks=%d division=%d\n",format,ntrks,division); +} + +void txt_trackstart() +{ + printf("Track start\n"); +} + +void txt_trackend() +{ + printf("Track end\n"); +} + +void txt_noteon(chan,pitch,vol) +int chan, pitch, vol; +{ + prtime(); + printf("Note on, chan=%d pitch=%d vol=%d\n",chan+1,pitch,vol); +} + +void txt_noteoff(chan,pitch,vol) +int chan, pitch, vol; +{ + prtime(); + printf("Note off, chan=%d pitch=%d vol=%d\n",chan+1,pitch,vol); +} + +void txt_pressure(chan,pitch,press) +int chan, pitch,press; +{ + prtime(); + printf("Pressure, chan=%d pitch=%d press=%d\n",chan+1,pitch,press); +} + +void txt_parameter(chan,control,value) +int chan, control, value; +{ + prtime(); + printf("Parameter, chan=%d c1=%d c2=%d\n",chan+1,control,value); +} + + +/* [SS] 2017-11-16 submitted by Jonathan Hough (msb,lsb interchanged) */ +void txt_pitchbend(chan,lsb,msb) +int chan, msb, lsb; +{ + prtime(); + printf("Pitchbend, chan=%d lsb=%d msb=%d\n",chan+1,msb,lsb); +} + +void txt_program(chan,program) +int chan, program; +{ + prtime(); + printf("Program, chan=%d program=%d\n",chan+1,program); +} + +void txt_chanpressure(chan,press) +int chan, press; +{ + prtime(); + printf("Channel pressure, chan=%d pressure=%d\n",chan+1,press); +} + +void txt_sysex(leng,mess) +int leng; +char *mess; +{ + prtime(); + printf("Sysex, leng=%d\n",leng); +} + +void txt_metamisc(type,leng,mess) +int type, leng; +char *mess; +{ + prtime(); + printf("Meta event, unrecognized, type=0x%02x leng=%d\n",type,leng); +} + +void txt_metaspecial(type,leng,mess) +int type, leng; +char *mess; +{ + prtime(); + printf("Meta event, sequencer-specific, type=0x%02x leng=%d\n",type,leng); +} + +void txt_metatext(type,leng,mess) +int type, leng; +char *mess; +{ + static char *ttype[] = { + NULL, + "Text Event", /* type=0x01 */ + "Copyright Notice", /* type=0x02 */ + "Sequence/Track Name", + "Instrument Name", /* ... */ + "Lyric", + "Marker", + "Cue Point", /* type=0x07 */ + "Unrecognized" + }; + int unrecognized = (sizeof(ttype)/sizeof(char *)) - 1; + register int n, c; + register char *p = mess; + + if ( type < 1 || type > unrecognized ) + type = unrecognized; + prtime(); + printf("Meta Text, type=0x%02x (%s) leng=%d\n",type,ttype[type],leng); + printf(" Text = <"); + for ( n=0; n\n"); +} + +void txt_metaseq(num) +int num; +{ + prtime(); + printf("Meta event, sequence number = %d\n",num); +} + +void txt_metaeot() +{ + prtime(); + printf("Meta event, end of track\n"); +} + +void txt_keysig(sf,mi) +int sf,mi; +{ + prtime(); + printf("Key signature, sharp/flats=%d minor=%d\n",sf,mi); +} + +void txt_tempo(ltempo) +long ltempo; +{ + tempo = ltempo; + prtime(); + printf("Tempo, microseconds-per-MIDI-quarter-note=%ld\n",tempo); +} + +void txt_timesig(nn,dd,cc,bb) +int nn, dd, cc, bb; +{ + int denom = 1; + while ( dd-- > 0 ) + denom *= 2; + prtime(); + printf("Time signature=%d/%d MIDI-clocks/click=%d 32nd-notes/24-MIDI-clocks=%d\n", + nn,denom,cc,bb); +} + +void txt_smpte(hr,mn,se,fr,ff) +int hr, mn, se, fr, ff; +{ + prtime(); + printf("SMPTE, hour=%d minute=%d second=%d frame=%d fract-frame=%d\n", + hr,mn,se,fr,ff); +} + +void txt_arbitrary(leng,mess) +int leng; /* [SDE] 2020-06-03 */ +char *mess; +{ + prtime(); + printf("Arbitrary bytes, leng=%d\n",leng); +} + +void initfuncs() +{ + Mf_error = error; + Mf_header = txt_header; + Mf_trackstart = txt_trackstart; + Mf_trackend = txt_trackend; + Mf_noteon = txt_noteon; + Mf_noteoff = txt_noteoff; + Mf_pressure = txt_pressure; + Mf_parameter = txt_parameter; + Mf_pitchbend = txt_pitchbend; + Mf_program = txt_program; + Mf_chanpressure = txt_chanpressure; + Mf_sysex = txt_sysex; + Mf_metamisc = txt_metamisc; + Mf_seqnum = txt_metaseq; + Mf_eot = txt_metaeot; + Mf_timesig = txt_timesig; + Mf_smpte = txt_smpte; + Mf_tempo = txt_tempo; + Mf_keysig = txt_keysig; + Mf_seqspecific = txt_metaspecial; + Mf_text = txt_metatext; + Mf_arbitrary = txt_arbitrary; +} diff -Nru abcmidi-20200706/midi2abc.c abcmidi-20200728/midi2abc.c --- abcmidi-20200706/midi2abc.c 2020-07-06 12:03:47.000000000 +0000 +++ abcmidi-20200728/midi2abc.c 2020-07-27 15:52:43.000000000 +0000 @@ -1,4100 +1,4100 @@ -/* midi2abc - program to convert MIDI files to abc notation. - * Copyright (C) 1998 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 - */ - - -/* new midi2abc - converts MIDI file to abc format files - * - * - * re-written to use dynamic data structures - * James Allwright - * 5th June 1998 - * - * added output file option -o - * added summary option -sum - * added option -u to enter xunit directly - * fixed computation of xunit using -b option - * added -obpl (one bar per line) option - * add check for carriage return embedded inside midi text line - * Seymour Shlien 04/March/00 - * made to conform as much as possible to the official version. - * check for drum track added - * when midi program channel is command encountered, we ensure that - * we are using the correct channel number for the Voice by sending - * a %%MIDI channel message. - * - * Many more changes (see doc/CHANGES) - * - * Seymour Shlien 2005 - * - * based on public domain 'midifilelib' package. - */ - -#define VERSION "3.46 June 22 2020 midi2abc" - -/* Microsoft Visual C++ Version 6.0 or higher */ -#ifdef _MSC_VER -#define snprintf _snprintf -#define ANSILIBS -#endif - -#include -#include -#ifdef PCCFIX -#define stdout 1 -#endif - -/* define USE_INDEX if your C libraries have index() instead of strchr() */ -#ifdef USE_INDEX -#define strchr index -#endif - -#ifdef ANSILIBS -#include -#include -#include -#else -extern char* malloc(); -extern char* strchr(); -#endif -#include "midifile.h" -#define BUFFSIZE 200 -/* declare MIDDLE C */ -#define MIDDLE 72 -void initfuncs(); -void setupkey(int); -void stats_finish(); -int testtrack(int trackno, int barbeats, int anacrusis); -int open_note(int chan, int pitch, int vol); -int close_note(int chan, int pitch, int *initvol); -float histogram_entropy (int *histogram, int size); -void stats_noteoff(int chan,int pitch,int vol); -void reset_back_array (); /* [SS] 2019-05-08 */ - - -/* Global variables and structures */ - -extern long Mf_toberead; - -static FILE *F; -static FILE *outhandle; /* for producing the abc file */ - -int tracknum=0; /* track number */ -int division; /* pulses per quarter note defined in MIDI header */ -long tempo = 500000; /* the default tempo is 120 quarter notes/minute */ -int unitlen; /* abc unit length usually defined in L: field */ -int header_unitlen; /* first unitlen set */ -int unitlen_set =0; /* once unitlen is set don't allow it to change */ -int parts_per_unitlen = 2; /* specifies minimum quantization size */ -long laston = 0; /* length of MIDI track in pulses or ticks */ -char textbuff[BUFFSIZE]; /*buffer for handling text output to abc file*/ -int trans[256], back[256]; /*translation tables for MIDI pitch to abc note*/ -int barback[256]; /* to reinitialize back[] after each bar line */ -char atog[256]; /* translation tables for MIDI pitch to abc note */ -int symbol[256]; /*translation tables for MIDI pitch to abc note */ -int key[12]; -int sharps; -int trackno; -int maintrack; -int format; /* MIDI file type */ - -int karaoke, inkaraoke; -int midline; -int tempocount=0; /* number of tempo indications in MIDI file */ -int gotkeysig=0; /*set to 1 if keysignature found in MIDI file */ - -/* global parameters that may be set by command line options */ -int xunit; /* pulses per abc unit length */ -int tsig_set; /* flag - time signature already set by user */ -int ksig_set; /* flag - key signature already set by user */ -int xunit_set;/* flat - xunit already set by user */ -int extracta; /* flag - get anacrusis from strong beat */ -int guessu; /* flag - estimate xunit from note durations */ -int guessa; /* flag - get anacrusis by minimizing tied notes */ -int guessk; /* flag - guess key signature */ -int summary; /* flag - output summary info of MIDI file */ -int keep_short; /*flag - preserve short notes */ -int swallow_rests; /* flag - absorb short rests */ -int midiprint; /* flag - run midigram instead of midi2abc */ -int stats = 0; /* flag - gather and print statistics */ -int usesplits; /* flag - split measure into parts if needed */ -int restsize; /* smallest rest to absorb */ -/* [SS] 2017-01-01 */ -/*int no_triplets; flag - suppress triplets or broken rhythm */ -int allow_triplets; /* flag to allow triplets */ -int allow_broken; /* flag to allow broken rhythms > < */ -int obpl = 0; /* flag to specify one bar per abc text line */ -int nogr = 0; /* flag to put a space between every note */ -int noly = 0; /* flag to allow lyrics [SS] 2019-07-12 */ -int bars_per_line=4; /* number of bars per output line */ -int bars_per_staff=4; /* number of bars per music staff */ -int asig, bsig; /* time signature asig/bsig */ -int header_asig =0; /* first time signature encountered */ -int header_bsig =0; /* first time signature encountered */ -int header_bb; /* first ticks/quarter note encountered */ -int active_asig,active_bsig; /* last time signature declared */ -int last_asig, last_ksig; /* last time signature printed */ -int barsize; /* barsize in parts_per_unitlen units */ -int chordthreshold; /* number of maximum number of pulses separating note */ -int Qval; /* tempo - quarter notes per minute */ -int verbosity=0; /* control amount of detail messages in abcfile*/ -int debug=0; /* for debugging */ - -/* global arguments dependent on command line options or computed */ - -int anacrusis=0; -int bars; -int keysig; -int header_keysig= -50; /* header key signature */ -int active_keysig = -50; /* last key signature declared */ -int xchannel; /* channel number to be extracted. -1 means all */ -int timeunits = 1; /*tells prtime to display time in beats [SS] 2018-10-25 */ - - -/* structure for storing music notes */ -struct anote { - int pitch; /* MIDI pitch */ - int chan; /* MIDI channel */ - int vel; /* MIDI velocity */ - long time; /* MIDI onset time in pulses */ - long dtnext; /* time increment to next note in pulses */ - long tplay; /* note duration in pulses */ - int xnum; /* number of xunits to next note */ - int playnum; /* note duration in number of xunits */ - int posnum; /* note position in xunits */ - int splitnum; /* voice split number */ - /* int denom; */ -}; - - -/* linked list of notes */ -struct listx { - struct listx* next; - struct anote* note; -}; - -/* linked list of text items (strings) */ -struct tlistx { - struct tlistx* next; - char* text; - long when; /* time in pulses to output */ - int type; /* 0 - comments, other - field commands */ -}; - - -/* a MIDI track */ -struct atrack { - struct listx* head; /* first note */ - struct listx* tail; /* last note */ - struct tlistx* texthead; /* first text string */ - struct tlistx* texttail; /* last text string */ - int notes; /* number of notes in track */ - long tracklen; - long startwait; - int startunits; - int drumtrack; - /* [SS] 2019-05-29 for debugging */ - int texts; /* number of text links in track */ -}; - -/* can cope with up to 64 track MIDI files */ -struct atrack track[64]; -int trackcount = 0; -int maxbarcount = 0; -/* maxbarcount is used to return the numbers of bars created.*/ -/* obpl is a flag for one bar per line. */ - -/* double linked list of notes */ -/* used for temporary list of chords while abc is being generated */ -struct dlistx { - struct dlistx* next; - struct dlistx* last; - struct anote* note; -}; - -int notechan[2048],notechanvol[2048]; /*for linking on and off midi - channel commands */ -int last_tick[17]; /* for getting last pulse number in MIDI file */ -int last_on_tick[17]; /* for detecting chords [SS] 2019-08-02 */ - -char *title = NULL; /* for pasting title from argv[] */ -char *origin = NULL; /* for adding O: info from argv[] */ -int newline_flag = 0; /* [SS] 2019-06-14 signals new line was just issued */ - - -void remove_carriage_returns(); -int validnote(); -void printpitch(struct anote*); -void printfract(int, int); -void txt_trackend(); -void txt_noteoff(int,int,int); -/* [SS] 2019-05-29 new functions to handle type 0 midi files */ -void txt_trackstart_type0(); -void txt_noteon_type0(int,int,int); -void txt_program_type0(int,int); - -/* The following variables are used by the -stats option - * which is used by a separate application called midiexplorer.tcl. - * The channel numbers go from 1 to 16 instead of 0 to 15 - */ -struct trkstat { - int notecount[17]; - int chordcount[17]; - int notemeanpitch[17]; - int notelength[17]; - int pitchbend[17]; - int pressure[17]; - int cntlparam[17]; - int program[17]; - int tempo[17]; - int npulses[17]; - } trkdata; - -/* The trkstat references the individual channels in the midi file. - * notecount is the number of notes or bass notes in the chord. - * chordcount is the number of notes not counting the bass notes. - * notemeanpitch is the average pitch for the channel. - * notelength is the average note length. - * pitchbend is the number of pitch bends for the channel. - * pressure is the number of control pressure commands. - * cntlparam is the number of control parameter commands. - * program is number of times there is a program command for the channel. - * tempo is the number of times there is a tempo command. - * npulses is the number of pulses. - */ - -int progcolor[17]; /* used by stats_program */ -int drumhistogram[82]; /* counts drum noteons */ -int pitchhistogram[12]; /* pitch distribution for non drum notes */ -int channel2prog[17]; /* maps channel to program */ -int channel2nnotes[17]; /*maps channel to note count */ -int chnactivity[17]; /* [SS] 2018-02-02 */ -int progactivity[128]; /* [SS] 2018-02-02 */ -int pitchclass_activity[12]; /* [SS] 2018-02-02 */ - - -/* [SS] 2017-11-01 */ -static int progmapper[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 2, - 3, 3, 3, 3, 3, 3, 3, 3, - 2, 2, 4, 4, 4, 4, 4, 2, - 5, 5, 5, 5, 5, 5, 5, 5, - 6, 6, 6, 6, 6, 2, 7, 10, - 7, 7, 7, 7, 8, 8, 8, 8, - 9, 9, 9, 9, 9, 9, 9, 9, -11, 11, 11, 11, 11, 11, 11, 11, -12, 12, 12, 12, 12, 12, 12, 12, -13, 13, 13, 13, 13, 13, 13, 13, -14, 14, 14, 14, 14, 14, 14, 14, -15, 15, 15, 15, 15, 15, 15, 15, - 2, 2, 2, 2, 2, 12, 6, 12, - 1, 1, 10, 10, 10, 10, 10, 1, -16, 16, 16, 16, 16, 16, 16, 16 -}; - -/* Stage 1. Parsing MIDI file */ - -/* Functions called during the reading pass of the MIDI file */ - -/* The following C routines are required by midifilelib. */ -/* They specify the action to be taken when various items */ -/* are encountered in the MIDI. The mfread function scans*/ -/* the MIDI file and calls these functions when needed. */ - - -int filegetc() -{ - return(getc(F)); -} - - -void fatal_error(s) -char* s; -/* fatal error encounterd - abort program */ -{ - fprintf(stderr, "%s\n", s); - exit(1); -} - - -void event_error(s) -char *s; -/* problem encountered but OK to continue */ -{ - char msg[256]; - - sprintf(msg, "Error: Time=%ld Track=%d %s\n", Mf_currtime, trackno, s); - printf("%s",msg); -} - - -int* checkmalloc(bytes) -/* malloc with error checking */ -int bytes; -{ - int *p; - - p = (int*) malloc(bytes); - if (p == NULL) { - fatal_error("Out of memory error - cannot malloc!"); - }; - return (p); -} - - - -char* addstring(s) -/* create space for string and store it in memory */ -char* s; -{ - char* p; - int numbytes; - - /* p = (char*) checkmalloc(strlen(s)+1); */ - /* Integer overflow leading to heap buffer overflow in midi2abc - * Debian Bug report log #924947 [SS] 2019-08-11 - * https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=924947 - */ - numbytes = strlen(s)+2; /* [SS] 2019-08-11 */ - if (numbytes > 1024) numbytes = 1024; /* [SS] 2019-08-11 */ - p = (char*) checkmalloc(numbytes); /* [SS] 2019-04-13 2019-08-11*/ - strncpy(p, s,numbytes); /* [SS] 2017-08-30 [SDG] 2020-06-03 */ - return(p); -} - -void addtext(s, type) -/* add structure for text */ -/* used when parsing MIDI file */ -char* s; -int type; -{ - struct tlistx* newx; - - track[trackno].texts = track[trackno].texts + 1; /* [SS] 2019-05-29 */ - newx = (struct tlistx*) checkmalloc(sizeof(struct tlistx)); - newx->next = NULL; - newx->text = addstring(s); - newx->type = type; - newx->when = Mf_currtime; - if (track[trackno].texthead == NULL) { - track[trackno].texthead = newx; - track[trackno].texttail = newx; - } - else { - track[trackno].texttail->next = newx; - track[trackno].texttail = newx; - }; -} - -/* [SS] 2019-05-2019 forwards text to track[chn] */ -void addtext_type0(s, type,chn) -/* add structure for text */ -/* used when parsing MIDI file */ -char* s; -int type; -int chn; -{ - struct tlistx* newx; - - track[chn].texts = track[chn].texts + 1; - newx = (struct tlistx*) checkmalloc(sizeof(struct tlistx)); - newx->next = NULL; - newx->text = addstring(s); - newx->type = type; - newx->when = Mf_currtime; - if (track[chn].texthead == NULL) { - track[chn].texthead = newx; - track[chn].texttail = newx; - } - else { - track[chn].texttail->next = newx; - track[chn].texttail = newx; - }; -} - - - - -/* The MIDI file has separate commands for starting */ -/* and stopping a note. In order to determine the duration of */ -/* the note it is necessary to find the note_on command associated */ -/* with the note off command. We rely on the note's pitch and channel*/ -/* number to find the right note. While we are parsing the MIDI file */ -/* we maintain a list of all the notes that are currently on */ -/* head and tail of list of notes still playing. */ -/* The following doubly linked list is used for this purpose */ - -struct dlistx* playinghead; -struct dlistx* playingtail; - - -void noteplaying(p) -/* This function adds a new note to the playinghead list. */ -struct anote* p; -{ - struct dlistx* newx; - - newx = (struct dlistx*) checkmalloc(sizeof(struct dlistx)); - newx->note = p; - newx->next = NULL; - newx->last = playingtail; - if (playinghead == NULL) { - playinghead = newx; - }; - if (playingtail == NULL) { - playingtail = newx; - } - else { - playingtail->next = newx; - playingtail = newx; - }; -} - - -void addnote(p, ch, v) -/* add structure for note */ -/* used when parsing MIDI file */ -int p, ch, v; -{ - struct listx* newx; - struct anote* newnote; - - track[trackno].notes = track[trackno].notes + 1; - newx = (struct listx*) checkmalloc(sizeof(struct listx)); - newnote = (struct anote*) checkmalloc(sizeof(struct anote)); - newx->next = NULL; - newx->note = newnote; - if (track[trackno].head == NULL) { - track[trackno].head = newx; - track[trackno].tail = newx; - } - else { - track[trackno].tail->next = newx; - track[trackno].tail = newx; - }; - if (ch == 9) { - track[trackno].drumtrack = 1; - }; - newnote->pitch = p; - newnote->chan = ch; - newnote->vel = v; - newnote->time = Mf_currtime; - laston = Mf_currtime; - newnote->tplay = Mf_currtime; - noteplaying(newnote); -} - -/* [SS] 2019-05-29 forwards note to track[ch] */ -void addnote_type0(p, ch, v) -/* add structure for note */ -/* used when parsing MIDI file */ -int p, ch, v; -{ - struct listx* newx; - struct anote* newnote; - - track[ch].notes = track[ch].notes + 1; - newx = (struct listx*) checkmalloc(sizeof(struct listx)); - newnote = (struct anote*) checkmalloc(sizeof(struct anote)); - newx->next = NULL; - newx->note = newnote; - if (track[ch].head == NULL) { - track[ch].head = newx; - track[ch].tail = newx; - } - else { - track[ch].tail->next = newx; - track[ch].tail = newx; - }; - if (ch == 9) { - track[ch].drumtrack = 1; - }; - newnote->pitch = p; - newnote->chan = ch; - newnote->vel = v; - newnote->time = Mf_currtime; - laston = Mf_currtime; - newnote->tplay = Mf_currtime; - noteplaying(newnote); -} - - - -void notestop(p, ch) -/* MIDI note stops */ -/* used when parsing MIDI file */ -int p, ch; -{ - struct dlistx* i; - int found; - char msg[80]; - - i = playinghead; - found = 0; - while ((found == 0) && (i != NULL)) { - if ((i->note->pitch == p)&&(i->note->chan==ch)) { - found = 1; - } - else { - i = i->next; - }; - }; - if (found == 0) { - sprintf(msg, "Note terminated when not on - pitch %d", p); - event_error(msg); - return; - }; - /* fill in tplay field */ - i->note->tplay = Mf_currtime - (i->note->tplay); - /* remove note from list */ - if (i->last == NULL) { - playinghead = i->next; - } - else { - (i->last)->next = i->next; - }; - if (i->next == NULL) { - playingtail = i->last; - } - else { - (i->next)->last = i->last; - }; - free(i); -} - - - - -FILE * -efopen(name,mode) -char *name; -char *mode; -{ - FILE *f; - - if ( (f=fopen(name,mode)) == NULL ) { - char msg[256]; - sprintf(msg,"Error - Cannot open file %s",name); - fatal_error(msg); - } - return(f); -} - -void error(s) -char *s; -{ - fprintf(stderr,"Error: %s\n",s); -} - - -/* [SS] 2017-11-19 */ -void stats_error(s) -char *s; -{ - fprintf(stderr,"Error: %s\n",s); - fprintf(stderr,"activetrack %d\n",tracknum); - stats_finish(); -} - - - -void txt_header(xformat,ntrks,ldivision) -int xformat, ntrks, ldivision; -{ - division = ldivision; - format = xformat; - if (format != 0) { - /* fprintf(outhandle,"%% format %d file %d tracks\n", format, ntrks);*/ - if(summary>0) printf("This midi file has %d tracks\n\n",ntrks); - } - else { -/* fprintf(outhandle,"%% type 0 midi file\n"); */ -/* [SS] 2019-05-29 use these functions for type 0 midi files */ - Mf_trackstart = txt_trackstart_type0; - Mf_trackend = txt_trackend; - Mf_noteon = txt_noteon_type0; - Mf_noteoff = txt_noteoff; - Mf_program = txt_program_type0; - if(summary>0) { - printf("This is a type 0 midi file.\n"); - printf("All the channels are in one track.\n"); - printf("You may need to process the channels separately\n\n"); - } - } - -} - - -void txt_trackstart() -{ - laston = 0L; - track[trackno].notes = 0; - track[trackno].texts = 0; /* [SS] 2019-05-29 */ - track[trackno].head = NULL; - track[trackno].tail = NULL; - track[trackno].texthead = NULL; - track[trackno].texttail = NULL; - track[trackno].tracklen = Mf_currtime; - track[trackno].drumtrack = 0; -} - - -/* [SS] 2019-05-29 new function. We need all 16 tracks - * to handle type 0 midi file. Each track links to one - * of the 16 midi channels */ -void txt_trackstart_type0() -{ - int i; - for (i=0;i<16;i++) { - track[i].notes = 0; - track[i].texts = 0; /* [SS] 2019-05-29 */ - track[i].head = NULL; - track[i].tail = NULL; - track[i].texthead = NULL; - track[i].texttail = NULL; - track[i].tracklen = Mf_currtime; - track[i].drumtrack = 0; - } -} - - -void txt_trackend() -{ - /* check for unfinished notes */ - if (playinghead != NULL) { - printf("Error in MIDI file - notes still on at end of track!\n"); - }; - track[trackno].tracklen = Mf_currtime - track[trackno].tracklen; - trackno = trackno + 1; - trackcount = trackcount + 1; -} - -void txt_noteon(chan,pitch,vol) -int chan, pitch, vol; -{ - if ((xchannel == -1) || (chan == xchannel)) { - if (vol != 0) { - addnote(pitch, chan, vol); - } - else { - notestop(pitch, chan); - }; - }; -} - -/* [SS] 2019-05-29 new. Calls addnote_type0() function */ -void txt_noteon_type0(chan,pitch,vol) -int chan, pitch, vol; -{ - if ((xchannel == -1) || (chan == xchannel)) { - if (vol != 0) { - addnote_type0(pitch, chan, vol); - } - else { - notestop(pitch, chan); - }; - }; -} - -void txt_noteoff(chan,pitch,vol) -int chan, pitch, vol; -{ - if ((xchannel == -1) || (chan == xchannel)) { - notestop(pitch, chan); - }; -} - -void txt_pressure(chan,pitch,press) -int chan, pitch, press; -{ -} - -void stats_pressure(chan,press) -int chan, press; -{ -trkdata.pressure[0]++; -} - -void txt_parameter(chan,control,value) -int chan, control, value; -{ -} - -void txt_pitchbend(chan,lsb,msb) -int chan, msb, lsb; -{ -} - -void txt_program(chan,program) -int chan, program; -{ -/* suppress the %%MIDI program for channel 10 */ - if (chan == 9) return; /* [SS] 2020-02-17 */ - sprintf(textbuff, "%%%%MIDI program %d", program); - addtext(textbuff,0); -/* abc2midi does not use the same channel number as specified in - the original midi file, so we should not specify that channel - number in the %%MIDI program. If we leave it out the program - will refer to the current channel assigned to this voice. -*/ -} - -/* [SS] 2019-05-29 new. Calls addtext_type0 function. */ -void txt_program_type0(chan,program) -int chan, program; -{ - if (chan == 9) return; /* [SS] 2020-02-17 */ - sprintf(textbuff, "%%%%MIDI program %d", program); - addtext_type0(textbuff,0,chan); -/* abc2midi does not use the same channel number as specified in - the original midi file, so we should not specify that channel - number in the %%MIDI program. If we leave it out the program - will refer to the current channel assigned to this voice. -*/ -} - -void txt_sysex(leng,mess) -int leng; -char *mess; -{ -} - -void txt_metamisc(type,leng,mess) -int type, leng; -char *mess; -{ -} - -void txt_metaspecial(type,leng,mess) -int type, leng; -char *mess; -{ -} - -void txt_metatext(type,leng,mess) -int type, leng; -char *mess; -{ - char *ttype[] = { - NULL, - "Text Event", /* type=0x01 */ - "Copyright Notice", /* type=0x02 */ - "Sequence/Track Name", - "Instrument Name", /* ... */ - "Lyric", - "Marker", - "Cue Point", /* type=0x07 */ - "Unrecognized" - }; - int unrecognized = (sizeof(ttype)/sizeof(char *)) - 1; - unsigned char c; - int n; - char *p = mess; - char *buff; - char buffer2[BUFFSIZE]; - - if ((type < 1)||(type > unrecognized)) - type = unrecognized; - buff = textbuff; - for (n=0; n0) return; /* ignore other tempo indications */ - tempo = ltempo; - tempocount++; -} - - -void setup_timesig(nn, denom, bb) -int nn,denom,bb; -{ - asig = nn; - bsig = denom; -/* we must keep unitlen and xunit fixed for the entire tune */ - if (unitlen_set == 0) { - unitlen_set = 1; - if ((asig*4)/bsig >= 3) { - unitlen =8; - } - else { - unitlen = 16; - }; - } -/* set xunit for this unitlen */ - if(!xunit_set) xunit = (division*bb*4)/(8*unitlen); - barsize = parts_per_unitlen*asig*unitlen/bsig; -/* printf("setup_timesig: unitlen=%d xunit=%d barsize=%d\n",unitlen,xunit,barsize); */ - if (header_asig ==0) {header_asig = asig; - header_bsig = bsig; - header_unitlen = unitlen; - header_bb = bb; - } -} - - -void txt_timesig(nn,dd,cc,bb) -int nn, dd, cc, bb; -{ - int denom = 1; - while ( dd-- > 0 ) - denom *= 2; - sprintf(textbuff, - "%% Time signature=%d/%d MIDI-clocks/click=%d 32nd-notes/24-MIDI-clocks=%d", - nn,denom,cc,bb); - if (verbosity) addtext(textbuff,0); - sprintf(textbuff,"%d %d %d\n",nn,denom,bb); - if (!tsig_set) { - addtext(textbuff,2); - setup_timesig(nn, denom,bb); - } - if (summary>0) { - if(tsig_set) printf("Time signature = %d/%d suppressed\n",nn,denom); - else printf("Time signature = %d/%d\n",nn,denom); - } -} - - -void txt_smpte(hr,mn,se,fr,ff) -int hr, mn, se, fr, ff; -{ -} - -void txt_arbitrary(leng,mess) -char *mess; -int leng; -{ -} - - - -/* Dummy functions for handling MIDI messages. - * */ - void no_op0() {} - void no_op1(int dummy1) {} - void no_op2(int dummy1, int dummy2) {} - void no_op3(int dummy1, int dummy2, int dummy3) { } - void no_op4(int dummy1, int dummy2, int dummy3, int dummy4) { } - void no_op5(int dummy1, int dummy2, int dummy3, int dummy4, int dummy5) { } - - -void print_txt_header(xformat,ntrks,ldivision) -int xformat, ntrks, ldivision; -{ - division = ldivision; - format = xformat; - printf("Header %d %d %d\n",format,ntrks,ldivision); /*[SS] 2019-11-13 */ -} - - -void print_txt_noteon(chan, pitch, vol) -int chan, pitch, vol; -{ -int start_time; -int initvol; -if (vol > 0) -open_note(chan, pitch, vol); -else { - start_time = close_note(chan, pitch,&initvol); - if (start_time >= 0) - /* printf("%8.4f %8.4f %d %d %d %d\n", - (double) start_time/(double) division, - (double) Mf_currtime/(double) division, - trackno+1, chan +1, pitch,initvol); - */ - printf("%d %ld %d %d %d %d\n", - start_time, Mf_currtime, trackno+1, chan +1, pitch,initvol); - - if(Mf_currtime > last_tick[chan+1]) last_tick[chan+1] = Mf_currtime; - } -} - - - -void print_txt_noteoff(chan, pitch, vol) -int chan, pitch, vol; -{ -int start_time,initvol; - -start_time = close_note(chan, pitch, &initvol); -if (start_time >= 0) -/* - printf("%8.4f %8.4f %d %d %d %d\n", - (double) start_time/(double) division, - (double) Mf_currtime/(double) division, - trackno+1, chan+1, pitch,initvol); -*/ - printf("%d %ld %d %d %d %d\n", - start_time, Mf_currtime, trackno+1, chan +1, pitch,initvol); - if(Mf_currtime > last_tick[chan+1]) last_tick[chan+1] = Mf_currtime; -} - - - -/* In order to associate a channel note off message with its - * corresponding note on message, we maintain the information - * the notechan array. When a midi pitch (0-127) is switched - * on for a particular channel, we record the time that it - * was turned on in the notechan array. As there are 16 channels - * and 128 pitches, we initialize an array 128*16 = 2048 elements - * long. -**/ -void init_notechan() -{ -/* signal that there are no active notes */ - int i; - for (i = 0; i < 2048; i++) notechan[i] = -1; -} - - -/* The next two functions update notechan when a channel note on - or note off is encountered. The second function close_note, - returns the time when the note was turned on. -*/ -int open_note(int chan, int pitch, int vol) -{ - notechan[128 * chan + pitch] = Mf_currtime; - notechanvol[128 * chan + pitch] = vol; - return 0; -} - - -int close_note(int chan, int pitch, int *initvol) -{ - int index, start_tick; - index = 128 * chan + pitch; - if (notechan[index] < 0) - return -1; - start_tick = notechan[index]; - *initvol = notechanvol[index]; - notechan[index] = -1; - return start_tick; -} - -void print_txt_program(int chan,int program) { - /* [SS] 2019-11-06 */ - printf("%ld Program %2d %d \n",Mf_currtime,chan+1, program); - /*printf("Program %2d %d \n",chan+1, program);*/ - } - -/* mftext mode */ -int prtime(int units) -{ -/* if(Mf_currtime >= pulses) ignore=0; - if (ignore) return 1; - linecount++; - if(linecount > maxlines) {fclose(F); exit(0);} -*/ - if(units==1) - /*seconds*/ - printf("%6.2f ",mf_ticks2sec(Mf_currtime,division,tempo)); - else if (units==2) - /*beats*/ - printf("%6.2f ",(float) Mf_currtime/(float) division); - else - /*pulses*/ - printf("%6ld ",Mf_currtime); - return 0; -} - -char * pitch2key(int note) -{ -static char name[16]; /* [SDG] 2020-06-03 */ -char* s = name; - switch(note % 12) - { - case 0: *s++ = 'c'; break; - case 1: *s++ = 'c'; *s++ = '#'; break; - case 2: *s++ = 'd'; break; - case 3: *s++ = 'd'; *s++ = '#'; break; - case 4: *s++ = 'e'; break; - case 5: *s++ = 'f'; break; - case 6: *s++ = 'f'; *s++ = '#'; break; - case 7: *s++ = 'g'; break; - case 8: *s++ = 'g'; *s++ = '#'; break; - case 9: *s++ = 'a'; break; - case 10: *s++ = 'a'; *s++ = '#'; break; - case 11: *s++ = 'b'; break; - } - sprintf(s, "%d", (note / 12)-1); /* octave (assuming Piano C4 is 60)*/ - return name; -} - - -void pitch2drum(midipitch) -int midipitch; -{ -static char *drumpatches[] = { - "Acoustic Bass Drum", "Bass Drum 1", "Side Stick", "Acoustic Snare", - "Hand Clap", "Electric Snare", "Low Floor Tom", "Closed Hi Hat", - "High Floor Tom", "Pedal Hi-Hat", "Low Tom", "Open Hi-Hat", - "Low-Mid Tom", "Hi Mid Tom", "Crash Cymbal 1", "High Tom", - "Ride Cymbal 1", "Chinese Cymbal", "Ride Bell", "Tambourine", - "Splash Cymbal", "Cowbell", "Crash Cymbal 2", "Vibraslap", - "Ride Cymbal 2", "Hi Bongo", "Low Bongo", "Mute Hi Conga", - "Open Hi Conga", "Low Conga", "High Timbale", "Low Timbale", - "High Agogo", "Low Agogo", "Cabasa", "Maracas", - "Short Whistle", "Long Whistle", "Short Guiro", "Long Guiro", - "Claves", "Hi Wood Block", "Low Wood Block", "Mute Cuica", - "Open Cuica", "Mute Triangle", "Open Triangle" }; -if (midipitch >= 35 && midipitch <= 81) { - printf(" (%s)",drumpatches[midipitch-35]); - } -} - - -void mftxt_header (int format, int ntrks, int ldivision) -{ - division = ldivision; - printf("Header format=%d ntrks=%d division=%d\n",format,ntrks,division); -} - - -void stats_header (int format, int ntrks, int ldivision) -{ - int i; - division = ldivision; - printf("ntrks %d\n",ntrks); - printf("ppqn %d\n",ldivision); - chordthreshold = ldivision/16; /* [SS] 2018-01-21 */ - trkdata.tempo[0] = 0; - trkdata.pressure[0] = 0; - trkdata.program[0] = 0; - for (i=0;i<17;i++) { - trkdata.npulses[i] = 0; - trkdata.pitchbend[i] = 0; - progcolor[i] = 0; - channel2prog[i] = -1; - channel2nnotes[i] = 0; - chnactivity[i] = 0; /* [SS] 2018-02-02 */ - } - for (i=0;i<82;i++) drumhistogram[i] = 0; - for (i=0;i<12;i++) pitchhistogram[i] = 0; /* [SS] 2017-11-01 */ - for (i=0;i<12;i++) pitchclass_activity[i] = 0; /* [SS] 2018-02-02 */ - for (i=0;i<128;i++) progactivity[i] = 0; /* [SS] 2018-02-02 */ -} - -void determine_progcolor () -{ -int i; -for (i=0;i<17;i++) progcolor[i] =0; -for (i=0;i<128;i++) { - progcolor[progmapper[i]] += progactivity[i]; - } -} - - -/* [SS] 2018-04-24 */ -void output_progs_data () { -int i; -/* check that there is valid progactivity */ - - printf("progs "); - for (i=0;i<128;i++) - if(progactivity[i] > 0) printf(" %d",i); - printf("\nprogsact "); - for (i=0;i<128;i++) - if(progactivity[i] > 0) printf(" %d",progactivity[i]); - } - - - -void stats_finish() -{ -int i; /* [SDG] 2020-06-03 */ -int npulses; -int nprogs; - -npulses = trkdata.npulses[0]; -printf("npulses %d\n",trkdata.npulses[0]); -printf("tempocmds %d\n",trkdata.tempo[0]); -printf("pitchbends %d\n",trkdata.pitchbend[0]); -for (i=1;i<17;i++) { - if (trkdata.pitchbend[i] > 0) { - printf("pitchbendin %d %d\n",i,trkdata.pitchbend[i]); } - } - -if (trkdata.pressure[0] > 0) - printf("pressure %d\n",trkdata.pressure[0]); -printf("programcmd %d\n",trkdata.program[0]); - -nprogs = 0; /* [SS] 2018-04-24 */ -for (i=1;i<128;i++) - if(progactivity[i] >0) nprogs++; -if (nprogs > 0) output_progs_data(); -else { - for (i=0;i<17;i++) - if(chnactivity[i] > 0) - progactivity[channel2prog[i]] = chnactivity[i]; - output_progs_data(); - } - -determine_progcolor(); -printf("\nprogcolor "); -if (npulses > 0) - for (i=0;i<17;i++) printf("%5.2f ",progcolor[i]/(double) npulses); -else - for (i=0;i<17;i++) printf("%5.2f ",(double) progcolor[i]); -printf("\n"); - -printf("drums "); -for (i=35;i<82;i++) { - if (drumhistogram[i] > 0) printf("%d ",i); - } -printf("\ndrumhits "); -for (i=35;i<82;i++) { - if (drumhistogram[i] > 0) printf("%d ",drumhistogram[i]); - } - -printf("\npitches "); /* [SS] 2017-11-01 */ -for (i=0;i<12;i++) printf("%d ",pitchhistogram[i]); -printf("\npitchact "); /* [SS] 2018-02-02 */ -if (npulses > 0) - for (i=0;i<12;i++) printf("%5.2f ",pitchclass_activity[i]/(double) npulses); -else - for (i=0;i<12;i++) printf("%5.2f ",(double) pitchclass_activity[i]); -printf("\nchnact "); /* [SS] 2018-02-08 */ -if (npulses > 0) - for (i=1;i<17;i++) printf("%5.2f ",chnactivity[i]/(double) trkdata.npulses[0]); -else - for (i=0;i<17;i++) printf("%5.2f ",(double) chnactivity[i]); -printf("\npitchentropy %f\n",histogram_entropy(pitchclass_activity,12)); -printf("\n"); -} - - - -float histogram_entropy (int *histogram, int size) - { - int i; - int total; - float entropy; - float e,p; - total = 0; - entropy = 0.0; - for (i=0;i0) { - printf("%s ",name); - printf("%d %d ",i,data_array[i]); - printf("\n"); - } - } -} - - - -void output_track_summary () { -int i; -/* find first channel containing data */ -for (i=0;i<17;i++) { - if(trkdata.notecount[i] == 0 && trkdata.chordcount[i] == 0) continue; - printf("trkinfo "); - printf("%d %d ",i,trkdata.program[i]); /* channel number and program*/ - printf("%d %d ",trkdata.notecount[i],trkdata.chordcount[i]); - printf("%d %d",trkdata.notemeanpitch[i], trkdata.notelength[i]); - printf("\n"); - - channel2nnotes[i] += trkdata.notecount[i] + trkdata.chordcount[i]; - } -} - - - - -void stats_trackstart() -{ - int i; - tracknum++; - for (i=0;i<17;i++) { - trkdata.notecount[i] = 0; - trkdata.notemeanpitch[i] = 0; - trkdata.notelength[i] = 0; - trkdata.chordcount[i] = 0; - trkdata.cntlparam[i] = 0; - last_tick[i] = -1; - last_on_tick[i] = -1; - } - printf("trk %d \n",tracknum); -} - -void stats_trackend() -{ - trkdata.npulses[tracknum] = Mf_currtime; - if (trkdata.npulses[0] < Mf_currtime) trkdata.npulses[0] = Mf_currtime; - output_track_summary(); -} - - -void mftxt_noteon(chan,pitch,vol) -int chan, pitch, vol; -{ - char *key; -/* - if (onlychan >=0 && chan != onlychan) return; -*/ - if (prtime(timeunits)) return; - key = pitch2key(pitch); - printf("Note on %2d %2d (%3s) %3d",chan+1, pitch, key,vol); - if (chan == 9) pitch2drum(pitch); - printf("\n"); -} - -void stats_noteon(chan,pitch,vol) -int chan, pitch, vol; -{ - if (vol == 0) { - /* treat as noteoff */ - stats_noteoff(chan,pitch,vol); - return; - } - trkdata.notemeanpitch[chan+1] += pitch; - if (abs(Mf_currtime - last_on_tick[chan+1]) < chordthreshold) trkdata.chordcount[chan+1]++; - else trkdata.notecount[chan+1]++; /* [SS] 2019-08-02 */ - last_tick[chan+1] = Mf_currtime; - last_on_tick[chan+1] = Mf_currtime; /* [SS] 2019-08-02 */ - /* last_on_tick not updated by stats_noteoff */ - if (chan == 9) { - if (pitch < 0 || pitch > 81) - printf("****illegal drum value %d\n",pitch); - else drumhistogram[pitch]++; - } - else pitchhistogram[pitch % 12]++; /* [SS] 2017-11-01 */ -} - - -void mftxt_noteoff(chan,pitch,vol) -int chan, pitch, vol; -{ - char *key; -/* - if (onlychan >=0 && chan != onlychan) return; -*/ - if (prtime(timeunits)) return; - key = pitch2key(pitch); - printf("Note off %2d %2d (%3s) %3d\n",chan+1,pitch, key,vol); -} - - -void stats_noteoff(int chan,int pitch,int vol) -{ - int length; - int program; - /* ignore if there was no noteon */ - if (last_tick[chan+1] == -1) return; - length = Mf_currtime - last_tick[chan+1]; - trkdata.notelength[chan+1] += length; - chnactivity[chan+1] += length; - if (chan == 9) return; /* drum channel */ - pitchclass_activity[pitch % 12] += length; - program = trkdata.program[chan+1]; - progactivity[program] += length; - /* [SS] 2018-04-18 */ - if(Mf_currtime > last_tick[chan+1]) last_tick[chan+1] = Mf_currtime; -} - -void mftxt_pressure(chan,pitch,press) -int chan, pitch, press; -{ - char *key; - if (prtime(timeunits)) return; - key = pitch2key(pitch); - printf("Pressure %2d %3s %3d\n",chan+1,key,press); -} - - -void mftxt_pitchbend(chan,lsb,msb) -int chan, lsb, msb; -{ - float bend; - int pitchbend; -/* - if (onlychan >=0 && chan != onlychan) return; -*/ - if (prtime(timeunits)) return; - /* [SS] 2014-01-05 2015-08-04*/ - pitchbend = (msb*128 + lsb); - bend = (float) (pitchbend - 8192); - bend = bend/40.96; - printf("Pitchbend %2d %d bend = %6.3f (cents)\n",chan+1,pitchbend,bend); -} - -void stats_pitchbend(chan,lsb,msb) -int chan, lsb, msb; -{ -trkdata.pitchbend[0]++; -trkdata.pitchbend[chan+1]++; -} - -void mftxt_program(chan,program) -int chan, program; -{ -static char *patches[] = { - "Acoustic Grand","Bright Acoustic","Electric Grand","Honky-Tonk", - "Electric Piano 1","Electric Piano 2","Harpsichord","Clav", - "Celesta", "Glockenspiel", "Music Box", "Vibraphone", - "Marimba", "Xylophone", "Tubular Bells", "Dulcimer", - "Drawbar Organ", "Percussive Organ", "Rock Organ", "Church Organ", - "Reed Organ", "Accordian", "Harmonica", "Tango Accordian", - "Acoustic Guitar (nylon)", "Acoustic Guitar (steel)", - "Electric Guitar (jazz)", "Electric Guitar (clean)", - "Electric Guitar (muted)", "Overdriven Guitar", - "Distortion Guitar", "Guitar Harmonics", - "Acoustic Bass", "Electric Bass (finger)", - "Electric Bass (pick)", "Fretless Bass", - "Slap Bass 1", "Slap Bass 2", "Synth Bass 1", "Synth Bass 2", - "Violin", "Viola", "Cello", "Contrabass", - "Tremolo Strings", "Pizzicato Strings", - "Orchestral Strings", "Timpani", - "String Ensemble 1", "String Ensemble 2", - "SynthStrings 1", "SynthStrings 2", - "Choir Aahs", "Voice Oohs", "Synth Voice", "Orchestra Hit", - "Trumpet", "Trombone", "Tuba", "Muted Trumpet", - "French Horn", "Brass Section", "SynthBrass 1", "SynthBrass 2", - "Soprano Sax", "Alto Sax", "Tenor Sax", "Baritone Sax", - "Oboe", "English Horn", "Bassoon", "Clarinet", - "Piccolo", "Flute", "Recorder", "Pan Flute", - "Blown Bottle", "Skakuhachi", "Whistle", "Ocarina", - "Lead 1 (square)", "Lead 2 (sawtooth)", - "Lead 3 (calliope)", "Lead 4 (chiff)", - "Lead 5 (charang)", "Lead 6 (voice)", - "Lead 7 (fifths)", "Lead 8 (bass+lead)", - "Pad 1 (new age)", "Pad 2 (warm)", - "Pad 3 (polysynth)", "Pad 4 (choir)", - "Pad 5 (bowed)", "Pad 6 (metallic)", - "Pad 7 (halo)", "Pad 8 (sweep)", - "FX 1 (rain)", "(soundtrack)", - "FX 3 (crystal)", "FX 4 (atmosphere)", - "FX 5 (brightness)", "FX 6 (goblins)", - "FX 7 (echoes)", "FX 8 (sci-fi)", - "Sitar", "Banjo", "Shamisen", "Koto", - "Kalimba", "Bagpipe", "Fiddle", "Shanai", - "Tinkle Bell", "Agogo", "Steel Drums", "Woodblock", - "Taiko Drum", "Melodic Tom", "Synth Drum", "Reverse Cymbal", - "Guitar Fret Noise", "Breath Noise", "Seashore", "Bird Tweet", - "Telephone ring", "Helicopter", "Applause", "Gunshot"}; -/* - if (onlychan >=0 && chan != onlychan) return; -*/ - if (prtime(timeunits)) return; - printf("Program %2d %d (%s)\n",chan+1, program,patches[program]); - } - - -void stats_program(chan,program) -int chan, program; -{ -int beatnumber; -if (program <0 || program > 127) return; /* [SS] 2018-03-06 */ -if (trkdata.program[chan+1] != 0) { - beatnumber = Mf_currtime/division; - printf("cprogram %d %d %d\n",chan+1,program,beatnumber); - /* count number of times the program was modified for a channel */ - trkdata.program[0] = trkdata.program[0]+1; - } else { - printf("program %d %d\n",chan+1,program); - trkdata.program[chan+1] = program; - } - if (channel2prog[chan+1]== -1) channel2prog[chan+1] = program; -} - - -void mftxt_parameter(chan,control,value) -int chan, control, value; -{ - static char *ctype[] = { - "Bank Select", "Modulation Wheel", /*1*/ - "Breath controller", "unknown", /*3*/ - "Foot Pedal", "Portamento Time", /*5*/ - "Data Entry", "Volume", /*7*/ - "Balance", "unknown", /*9*/ - "Pan position", "Expression", /*11*/ - "Effect Control 1", "Effect Control 2", /*13*/ - "unknown", "unknown", /*15*/ - "Slider 1", "Slider 2", /*17*/ - "Slider 3", "Slider 4", /*19*/ - "unknown", "unknown", /*21*/ - "unknown", "unknown", /*23*/ - "unknown", "unknown", /*25*/ - "unknown", "unknown", /*27*/ - "unknown", "unknown", /*29*/ - "unknown", "unknown", /*31*/ - "Bank Select (fine)", "Modulation Wheel (fine)", /*33*/ - "Breath controller (fine)", "unknown", /*35*/ - "Foot Pedal (fine)", "Portamento Time (fine)", /*37*/ - "Data Entry (fine)", "Volume (fine)", /*39*/ - "Balance (fine)", "unknown", /*41*/ - "Pan position (fine)", "Expression (fine)", /*43*/ - "Effect Control 1 (fine)", "Effect Control 2 (fine)", /*45*/ - "unknown", "unknown", /*47*/ - "unknown", "unknown", /*49*/ - "unknown", "unknown", /*51*/ - "unknown", "unknown", /*53*/ - "unknown", "unknown", /*55*/ - "unknown", "unknown", /*57*/ - "unknown", "unknown", /*59*/ -"unknown", "unknown", /*61*/ - "unknown", "unknown", /*63*/ - "Hold Pedal", "Portamento", /*65*/ - "Susteno Pedal", "Soft Pedal", /*67*/ - "Legato Pedal", "Hold 2 Pedal", /*69*/ - "Sound Variation", "Sound Timbre", /*71*/ - "Sound Release Time", "Sound Attack Time", /*73*/ - "Sound Brightness", "Sound Control 6", /*75*/ - "Sound Control 7", "Sound Control 8", /*77*/ - "Sound Control 9", "Sound Control 10", /*79*/ - "GP Button 1", "GP Button 2", /*81*/ - "GP Button 3", "GP Button 4", /*83*/ - "unknown", "unknown", /*85*/ - "unknown", "unknown", /*87*/ - "unknown", "unknown", /*89*/ - "unknown", "Effects Level", /*91*/ - "Tremolo Level", "Chorus Level", /*93*/ - "Celeste Level", "Phaser Level", /*95*/ - "Data button increment", "Data button decrement", /*97*/ - "NRP (fine)", "NRP (coarse)", /*99*/ - "Registered parameter (fine)", "Registered parameter (coarse)", /*101*/ - "unknown", "unknown", /*103*/ - "unknown", "unknown", /*105*/ - "unknown", "unknown", /*107*/ - "unknown", "unknown", /*109*/ - "unknown", "unknown", /*111*/ - "unknown", "unknown", /*113*/ - "unknown", "unknown", /*115*/ - "unknown", "unknown", /*117*/ - "unknown", "unknown", /*119*/ - "All Sound Off", "All Controllers Off", /*121*/ - "Local Keyboard (on/off)","All Notes Off", /*123*/ - "Omni Mode Off", "Omni Mode On", /*125*/ - "Mono Operation", "Poly Operation"}; - -/* if (onlychan >=0 && chan != onlychan) return; */ - if (prtime(timeunits)) return; - - printf("CntlParm %2d %s = %d\n",chan+1, ctype[control],value); -} - -void stats_parameter(chan,control,value) -int chan, control, value; -{ -/*if (control == 7) { - printf("cntrlvolume %d %d \n",chan+1,value); - } -*/ -trkdata.cntlparam[chan+1]++; -} - -void mftxt_metatext(type,leng,mess) -int type, leng; -char *mess; -{ - static char *ttype[] = { - NULL, - "Text Event", /* type=0x01 */ - "Copyright Notice", /* type=0x02 */ - "Seqnce/Track Name", - "Instrument Name", /* ... */ - "Lyric", - "Marker", - "Cue Point", /* type=0x07 */ - "Unrecognized" - }; - int unrecognized = (sizeof(ttype)/sizeof(char *)) - 1; - int len; - register int n, c; - register char *p = mess; - - if ( type < 1 || type > unrecognized ) - type = unrecognized; - if (prtime(timeunits)) return; - printf("Metatext (%s) ",ttype[type]); - len = leng; - if (len > 15) len = 15; - for ( n=0; n */ - printf( (isprint(c)||isspace(c)) ? "%c" : "\\0x%02x" , c); - } - if (leng>15) printf("..."); - printf("\n"); -} - - -void stats_metatext(type,leng,mess) -int type, leng; -char *mess; -{ -int i; -if (type != 3) return; -printf("metatext %d ",type); -for (i=0;i12) return; - if (mi) - printf("keysig %s %d %d %6.2f\n",minor[index],sf,mi,beatnumber); - else - printf("keysig %s %d %d %6.2f\n",major[index],sf,mi,beatnumber); -} - - -void mftxt_tempo(ltempo) -long ltempo; -{ - tempo = ltempo; - if (prtime(timeunits)) return; - printf("Metatext tempo = %6.2f bpm\n",60000000.0/tempo); -} - -/* [SS] 2018-01-02 */ -void stats_tempo(ltempo) -long ltempo; -{ - float beatnumber; - tempo = ltempo; - beatnumber = Mf_currtime/division; - if (trkdata.tempo[0] == 0) printf("tempo %6.2f bpm\n",60000000.0/tempo); - else if (trkdata.tempo[0] < 10) printf("ctempo %6.2f %6.2f\n",60000000.0/tempo,beatnumber); - trkdata.tempo[0]++; -} - -void mftxt_timesig(nn,dd,cc,bb) -int nn, dd, cc, bb; -{ - int denom = 1; - while ( dd-- > 0 ) - denom *= 2; - if (prtime(timeunits)) return; - printf("Metatext time signature=%d/%d\n",nn,denom); -/* printf("Time signature=%d/%d MIDI-clocks/click=%d \ - 32nd-notes/24-MIDI-clocks=%d\n", nn,denom,cc,bb); */ -} - -void stats_timesig(nn,dd,cc,bb) -int nn, dd, cc, bb; -{ - float beatnumber; - int denom = 1; - beatnumber = Mf_currtime/division; - while ( dd-- > 0 ) - denom *= 2; - printf("timesig %d/%d %6.2f\n",nn,denom,beatnumber); -} - -void mftxt_smpte(hr,mn,se,fr,ff) -int hr, mn, se, fr, ff; -{ - if (prtime(timeunits)) return; - printf("Metatext SMPTE, %d:%d:%d %d=%d\n", hr,mn,se,fr,ff); -} - -void mftxt_metaeot() -{ - if (prtime(timeunits)) return; - printf("Meta event, end of track\n"); -} - - -void initfunc_for_midinotes() -{ - Mf_error = error; - Mf_header = print_txt_header; - Mf_trackstart = no_op0; - Mf_trackend = txt_trackend; - Mf_noteon = print_txt_noteon; - Mf_noteoff = print_txt_noteoff; - Mf_pressure = no_op3; - Mf_parameter = no_op3; - Mf_pitchbend = no_op3; - Mf_program = print_txt_program; - Mf_chanpressure = no_op3; - Mf_sysex = no_op2; - Mf_metamisc = no_op3; - Mf_seqnum = no_op1; - Mf_eot = no_op0; - Mf_timesig = no_op4; - Mf_smpte = no_op5; - Mf_tempo = no_op1; - Mf_keysig = no_op2; - Mf_seqspecific = no_op3; - Mf_text = no_op3; - Mf_arbitrary = no_op2; -} - - -void initfunc_for_mftext() -{ - Mf_error = error; - Mf_header = mftxt_header; - Mf_trackstart = mftxt_trackstart; - Mf_trackend = txt_trackend; - Mf_noteon = mftxt_noteon; - Mf_noteoff = mftxt_noteoff; - Mf_pressure =mftxt_pressure; - Mf_parameter = mftxt_parameter; - Mf_pitchbend = mftxt_pitchbend; - Mf_program = mftxt_program; - Mf_chanpressure = mftxt_pressure; - Mf_sysex = no_op2; - Mf_metamisc = no_op3; - Mf_seqnum = no_op1; - Mf_eot = mftxt_metaeot; - Mf_timesig = mftxt_timesig; - Mf_smpte = mftxt_smpte; - Mf_tempo = mftxt_tempo; - Mf_keysig = mftxt_keysig; - Mf_seqspecific = no_op3; - Mf_text = mftxt_metatext; - Mf_arbitrary = no_op2; -} - -void initfunc_for_stats() -{ - Mf_error = stats_error; /* [SS] 2017-11-19 */ - Mf_header = stats_header; - Mf_trackstart = stats_trackstart; - Mf_trackend = stats_trackend; - Mf_noteon = stats_noteon; - Mf_noteoff = stats_noteoff; - Mf_pressure = no_op3; - Mf_parameter = stats_parameter; - Mf_pitchbend = stats_pitchbend; - Mf_program = stats_program; - Mf_chanpressure = stats_pressure; - Mf_sysex = no_op2; - Mf_metamisc = no_op3; - Mf_seqnum = no_op1; - Mf_eot = no_op0; - Mf_timesig = stats_timesig; - Mf_smpte = no_op5; - Mf_tempo = stats_tempo; - Mf_keysig = stats_keysig; - Mf_seqspecific = no_op3; - Mf_text = stats_metatext; - Mf_arbitrary = no_op2; -} - - - -void initfuncs() -{ - Mf_error = error; - Mf_header = txt_header; - Mf_trackstart = txt_trackstart; - Mf_trackend = txt_trackend; - Mf_noteon = txt_noteon; - Mf_noteoff = txt_noteoff; - Mf_pressure = txt_pressure; - Mf_parameter = txt_parameter; - Mf_pitchbend = txt_pitchbend; - Mf_program = txt_program; - Mf_chanpressure = txt_pressure; - Mf_sysex = txt_sysex; - Mf_metamisc = txt_metamisc; - Mf_seqnum = txt_metaseq; - Mf_eot = txt_metaeot; - Mf_timesig = txt_timesig; - Mf_smpte = txt_smpte; - Mf_tempo = txt_tempo; - Mf_keysig = txt_keysig; - Mf_seqspecific = txt_metaspecial; - Mf_text = txt_metatext; - Mf_arbitrary = txt_arbitrary; -} - - -/* Stage 2 Quantize MIDI tracks. Get key signature, time signature... */ - - -void postprocess(trackno) -/* This routine calculates the time interval before the next note */ -/* called after the MIDI file has been read in */ -int trackno; -{ - struct listx* i; - - i = track[trackno].head; - if (i != NULL) { - track[trackno].startwait = i->note->time; - } - else { - track[trackno].startwait = 0; - }; - while (i != NULL) { - if (i->next != NULL) { - i->note->dtnext = i->next->note->time - i->note->time; - } - else { - i->note->dtnext = i->note->tplay; - }; - i = i->next; - }; -} - -void scannotes(trackno) -int trackno; -/* diagnostic routine to output notes in a track */ -{ - struct listx* i; - - i = track[trackno].head; - while (i != NULL) { - printf("Pitch %d chan %d vel %d time %ld %ld xnum %d playnum %d\n", - i->note->pitch, i->note->chan, - i->note->vel, i->note->dtnext, i->note->tplay, - i->note->xnum, i->note->playnum); - i = i->next; - }; -} - - -int xnum_to_next_nonchordal_note(fromitem,spare,quantum) -struct listx* fromitem; -int spare,quantum; -{ -struct anote* jnote; -struct listx* nextitem; -int i,xxnum; -jnote = fromitem->note; -if (jnote->xnum > 0) return jnote->xnum; -i = 0; -nextitem = fromitem->next; -while (nextitem != NULL && i < 5) { - jnote = nextitem->note; - xxnum = (2*(jnote->dtnext + spare + (quantum/4)))/quantum; - if (xxnum > 0) return xxnum; - i++; - nextitem = nextitem->next; - } -return 0; -} - -int quantize(trackno, xunit) -/* Work out how long each note is in musical time units. - * The results are placed in note.playnum */ -int trackno, xunit; -{ - struct listx* j; - struct anote* this; - int spare; - int toterror; - int quantum; - int posnum,xxnum; - - /* fix to avoid division by zero errors in strange MIDI */ - if (xunit == 0) { - return(10000); - }; - quantum = (int) (2.*xunit/parts_per_unitlen); /* xunit assume 2 parts_per_unit */ - track[trackno].startunits = (2*(track[trackno].startwait + (quantum/4)))/quantum; - spare = 0; - toterror = 0; - j = track[trackno].head; - posnum = 0; - while (j != NULL) { - this = j->note; - /* this->xnum is the quantized inter onset time */ - /* this->playnum is the quantized note length */ - this->xnum = (2*(this->dtnext + spare + (quantum/4)))/quantum; - this->playnum = (2*(this->tplay + (quantum/4)))/quantum; - if ((this->playnum == 0) && (keep_short)) { - this->playnum = 1; - }; - /* In the event of short rests, the inter onset time - * will be larger than the note length. However, for - * chords the inter onset time can be zero. */ - xxnum = xnum_to_next_nonchordal_note(j,spare,quantum); - if ((swallow_rests>=0) && (xxnum - this->playnum <= restsize) - && xxnum > 0) { - this->playnum = xxnum; - }; - /* this->denom = parts_per_unitlen; this variable is never used ! */ - spare = spare + this->dtnext - (this->xnum*xunit/parts_per_unitlen); - if (spare > 0) { - toterror = toterror + spare; - } - else { - toterror = toterror - spare; - }; - /* gradually forget old errors so that if xunit is slightly off, - errors don't accumulate over several bars */ - spare = (spare * 96)/100; - this->posnum = posnum; - posnum += this->xnum; - j = j->next; - }; - return(toterror); -} - - -void guesslengths(trackno) -/* work out most appropriate value for a unit of musical time */ -int trackno; -{ - int i; - int trial[100]; - float avlen, factor, tryx; - long min; - - min = track[trackno].tracklen; - if (track[trackno].notes == 0) { - return; - }; - avlen = ((float)(min))/((float)(track[trackno].notes)); - tryx = avlen * (float) 0.75; - factor = tryx/100; - for (i=0; i<100; i++) { - trial[i] = quantize(trackno, (int) tryx); - if ((long) trial[i] < min) { - min = (long) trial[i]; - xunit = (int) tryx; - }; - tryx = tryx + factor; - }; -xunit_set = 1; -} - - -int findana(maintrack, barsize) -/* work out anacrusis from MIDI */ -/* look for a strong beat marking the start of a bar */ -int maintrack; -int barsize; -{ - int min, mincount; - int place; - struct listx* p; - - min = 0; - mincount = 0; - place = 0; - p = track[maintrack].head; - while ((p != NULL) && (place < barsize)) { - if ((p->note->vel > min) && (place > 0)) { - min = p->note->vel; - mincount = place; - }; - place = place + (p->note->xnum); - p = p->next; - }; - return(mincount); -} - - - -int guessana(barbeats) -int barbeats; -/* try to guess length of anacrusis */ -{ - int score[64]; - int min, minplace; - int i,j; - - if (barbeats > 64) { - fatal_error("Bar size exceeds static limit of 64 units!"); - }; - for (j=0; jnote->pitch; - max = min; - totalnotes = 0; - for (j=0; jnote->pitch; - if (thispitch > max) { - max = thispitch; - } - else { - if (thispitch < min) { - min = thispitch; - }; - }; - n[thispitch % 12] = n[thispitch % 12] + 1; - p = p->next; - }; - }; - /* count black notes for each key */ - /* assume pitch = 0 is C */ - minkey = 0; - minblacks = totalnotes; - for (j=0; j<12; j++) { - key_score[j] = n[(j+1)%12] + n[(j+3)%12] + n[(j+6)%12] + - n[(j+8)%12] + n[(j+10)%12]; - /* printf("Score for key %d is %d\n", j, key_score[j]); */ - if (key_score[j] < minblacks) { - minkey = j; - minblacks = key_score[j]; - }; - }; - /* do conversion to abc pitches */ - /* Code changed to use absolute rather than */ - /* relative choice of pitch for 'c' */ - /* MIDDLE = (min + (max - min)/2 + 6)/12 * 12; */ - /* Do last note analysis */ - lastpitch = track[maintrack].tail->note->pitch; - if (minkey != (lastpitch%12)) { - fprintf(outhandle,"%% Last note suggests "); - switch((lastpitch+12-minkey)%12) { - case(2): - fprintf(outhandle,"Dorian "); - break; - case(4): - fprintf(outhandle,"Phrygian "); - break; - case(5): - fprintf(outhandle,"Lydian "); - break; - case(7): - fprintf(outhandle,"Mixolydian "); - break; - case(9): - fprintf(outhandle,"minor "); - break; - case(11): - fprintf(outhandle,"Locrian "); - break; - default: - fprintf(outhandle,"unknown "); - break; - }; - fprintf(outhandle,"mode tune\n"); - }; - /* switch to minor mode if it gives same number of accidentals */ - if ((minkey != ((lastpitch+3)%12)) && - (key_score[minkey] == key_score[(lastpitch+3)%12])) { - minkey = (lastpitch+3)%12; - }; - /* switch to major mode if it gives same number of accidentals */ - if ((minkey != (lastpitch%12)) && - (key_score[minkey] == key_score[lastpitch%12])) { - minkey = lastpitch%12; - }; - sharps = keysharps[minkey]; - return(sharps); -} - - - - -/* Stage 3 output MIDI tracks in abc format */ - - -/* head and tail of list of notes in current chord playing */ -/* used while abc is being generated */ -struct dlistx* chordhead; -struct dlistx* chordtail; - - -void printchordlist() -/* diagnostic routine */ -{ - struct dlistx* i; - - i = chordhead; - printf("----CHORD LIST------\n"); - while(i != NULL) { - printf("pitch %d len %d\n", i->note->pitch, i->note->playnum); - if (i->next == i) { - fatal_error("Loopback problem!"); - }; - i = i->next; - }; -} - -void checkchordlist() -/* diagnostic routine */ -/* validates data structure */ -{ - struct dlistx* i; - int n; - - if ((chordhead == NULL) && (chordtail == NULL)) { - return; - }; - if ((chordhead == NULL) && (chordtail != NULL)) { - fatal_error("chordhead == NULL and chordtail != NULL"); - }; - if ((chordhead != NULL) && (chordtail == NULL)) { - fatal_error("chordhead != NULL and chordtail == NULL"); - }; - if (chordhead->last != NULL) { - fatal_error("chordhead->last != NULL"); - }; - if (chordtail->next != NULL) { - fatal_error("chordtail->next != NULL"); - }; - i = chordhead; - n = 0; - while((i != NULL) && (i->next != NULL)) { - if (i->next->last != i) { - char msg[80]; - - sprintf(msg, "chordlist item %d : i->next->last!", n); - fatal_error(msg); - }; - i = i->next; - n = n + 1; - }; - /* checkchordlist(); */ -} - -void addtochord(p) -/* used when printing out abc */ -struct anote* p; -{ - struct dlistx* newx; - struct dlistx* place; - - if (debug > 3) printf("addtochord %d\n",p->pitch); - - newx = (struct dlistx*) checkmalloc(sizeof(struct dlistx)); - newx->note = p; - newx->next = NULL; - newx->last = NULL; - - if (chordhead == NULL) { - chordhead = newx; - chordtail = newx; - checkchordlist(); - return; - }; - place = chordhead; - while ((place != NULL) && (place->note->pitch > p->pitch)) { - place = place->next; - }; - if (place == chordhead) { - newx->next = chordhead; - chordhead->last = newx; - chordhead = newx; - checkchordlist(); - return; - }; - if (place == NULL) { - newx->last = chordtail; - chordtail->next = newx; - chordtail = newx; - checkchordlist(); - return; - }; - newx->next = place; - newx->last = place->last; - place->last = newx; - newx->last->next = newx; - checkchordlist(); -} - -struct dlistx* removefromchord(i) -/* used when printing out abc */ -struct dlistx* i; -{ - struct dlistx* newi; - - /* remove note from list */ - if (i->last == NULL) { - chordhead = i->next; - } - else { - (i->last)->next = i->next; - }; - if (i->next == NULL) { - chordtail = i->last; - } - else { - (i->next)->last = i->last; - }; - newi = i->next; - free(i); - checkchordlist(); - return(newi); -} - -int findshortest(gap) -/* find the first note in the chord to terminate */ -int gap; -{ - int min, v; - struct dlistx* p; - - p = chordhead; - min = gap; - while (p != NULL) { - v = p->note->playnum; - if (v < min) { - min = v; - }; - p = p->next; - }; - if (debug >3) printf("find_shortest %d --> %d\n",gap,min); - return(min); -} - -void advancechord(len) -/* adjust note lengths for all notes in the chord */ -int len; -{ - struct dlistx* p; - - if (debug > 3 && len >0) printf("\nadvancechord %d\n",len); - p = chordhead; - while (p != NULL) { - if (p->note->playnum <= len) { - if (p->note->playnum < len) { - fatal_error("Error - note too short!"); - }; - /* remove note */ - checkchordlist(); - p = removefromchord(p); - } - else { - /* shorten note */ - p->note->playnum = p->note->playnum - len; - p = p->next; - }; - }; -} - -void freshline() -/* if the current line of abc or text is non-empty, start a new line */ -{ - if (newline_flag) return; /* [SS] 2019-06-14 */ - if (midline == 1) { - fprintf(outhandle,"\n"); - midline = 0; - }; - newline_flag = 1; /* [SS] 2019-06-14 */ -} - - -void printnote (struct listx *i) -{ - printf("%ld ",i->note->time); - printpitch(i->note); - printfract(i->note->playnum, parts_per_unitlen); - printf(" %d %d %d %d\n",i->note->xnum, i->note->playnum, - i->note->posnum,i->note->splitnum); -} - -void listnotes(int trackno, int start, int end) -/* A diagnostic like scannotes. I usually call it when - I am in the debugger (for example in printtrack). -*/ -{ -struct listx* i; -int k; -i = track[trackno].head; -k = 0; -printf("ticks pitch xnum,playnum,posnum,splitnum\n"); -while (i != NULL && k < end) - { - if (k >= start) - printnote(i); - k++; - i = i->next; - } -} - -/* [SS] 2019-05-29 new. To see what is going on. I usually - * call it from the debugger just before printtrack(). - */ -void listtrack_sizes () -{ -int i; -for (i =0;i<16;i++) { - printf("track %d = %d notes %d texts\n",i,track[i].notes,track[i].texts); - } -} - - -void pitch_percentiles (int trackno, int * ten, int * ninety) -{ -int histogram[128]; -struct listx* i; -struct anote* n; -int j,p; -float qprob,total; -for (j=0;j<127;j++) histogram[j] = 0; -i = track[trackno].head; -while (i != NULL) - { - n = i->note; - p = (int) n->pitch; - if (p >-1 && p < 127) histogram[p]++; - i = i->next; - } -/*printf("pitch histogram "); -for (j=0;j<127;j++) printf(" %d",histogram[j]); -printf("\n"); -*/ -for (j=0;j<127;j++) - histogram[j+1] = histogram[j+1] + histogram[j]; -*ten = 0; -*ninety = 0; -if (histogram[126] == 0) return; -total = (float) histogram[126]; -for (j=0;j<127;j++) { - qprob = (float) histogram[j]/total; - if (qprob < 0.1) *ten = j; - if (qprob < 0.9) *ninety = j; - } -/* printf("10 and 90 percentiles = %d %d\n",*ten,*ninety); */ -} - - - -int testtrack(trackno, barbeats, anacrusis) -/* print out one track as abc */ -int trackno, barbeats, anacrusis; -{ - struct listx* i; - int step, gap; - int barnotes; - int barcount; - int breakcount; - - breakcount = 0; - chordhead = NULL; - chordtail = NULL; - i = track[trackno].head; - gap = 0; - if (anacrusis > 0) { - barnotes = anacrusis; - } - else { - barnotes = barbeats; - }; - barcount = 0; - while((i != NULL)||(gap != 0)) { - if (gap == 0) { - /* add notes to chord */ - addtochord(i->note); - gap = i->note->xnum; - i = i->next; - advancechord(0); /* get rid of any zero length notes */ - } - else { - step = findshortest(gap); - if (step > barnotes) { - step = barnotes; - }; - if (step == 0) { - fatal_error("Advancing by 0 in testtrack!"); - }; - advancechord(step); - gap = gap - step; - barnotes = barnotes - step; - if (barnotes == 0) { - if (chordhead != NULL) { - breakcount = breakcount + 1; - }; - barnotes = barbeats; - barcount = barcount + 1; - if (barcount>0 && barcount%4 ==0) { - /* can't zero barcount because I use it for computing maxbarcount */ - freshline(); - barcount = 0; - }; - }; - }; - }; - return(breakcount); -} - -void printpitch(j) -/* convert numerical value to abc pitch */ -struct anote* j; -{ - int p, po,i; - - p = j->pitch; - if (p == -1) { - fprintf(outhandle,"z"); - } - else { - po = p % 12; - /* if ((back[trans[p]] != p) || (key[po] == 1)) { [SS] 2010-05-08 */ - if (back[trans[p]] != p ) { /* [SS] 2019-05-08 */ - fprintf(outhandle,"%c%c", symbol[po], atog[p]); - for (i=p%12; i<256; i += 12) /* apply accidental to all octaves */ - back[trans[i]] = i; - } - else { - fprintf(outhandle,"%c", atog[p]); - }; - while (p >= MIDDLE + 12) { - fprintf(outhandle,"'"); - p = p - 12; - }; - while (p < MIDDLE - 12) { - fprintf(outhandle,","); - p = p + 12; - }; - }; -} - -static void reduce(a, b) -int *a, *b; -{ - int t, n, m; - - /* find HCF using Euclid's algorithm */ - if (*a > *b) { - n = *a; - m = *b; - } - else { - n = *b; - m = *a; - }; -while (m != 0) { - t = n % m; - n = m; - m = t; - }; -*a = *a/n; -*b = *b/n; -} - - - -void printfract(a, b) -/* print fraction */ -/* used when printing abc */ -int a, b; -{ - int c, d; - - c = a; - d = b; - reduce(&c,&d); - /* print out length */ - if (c != 1) { - fprintf(outhandle,"%d", c); - }; - if (d != 1) { - fprintf(outhandle,"/%d", d); - }; -} - -void printchord(len) -/* Print out the current chord. Any notes that haven't */ -/* finished at the end of the chord are tied into the next chord. */ -int len; -{ - struct dlistx* i; - - i = chordhead; - if (i == NULL) { - /* no notes in chord */ -#ifdef zSPLITCODE - fprintf(outhandle,"x"); -#else - fprintf(outhandle,"z"); -#endif - printfract(len, parts_per_unitlen); - midline = 1; - } - else { - if (i->next == NULL) { - /* only one note in chord */ - printpitch(i->note); - printfract(len, parts_per_unitlen); - midline = 1; - if (len < i->note->playnum) { - fprintf(outhandle,"-"); - }; - } - else { - fprintf(outhandle,"["); - while (i != NULL) { - printpitch(i->note); - /* printfract(len, parts_per_unitlen); [SS] 2020-01-06 */ - if (len < i->note->playnum) { - fprintf(outhandle,"-"); - }; - if (nogr && i->next != NULL) fprintf(outhandle," "); - i = i->next; - }; - fprintf(outhandle,"]"); - printfract(len, parts_per_unitlen); /* [SS] 2020-01-06 */ - midline = 1; - }; - }; - newline_flag = 0; /* [SS] 2019-06-14 */ -} - -char dospecial(i, barnotes, featurecount,allow_broken,allow_triplets) -/* identify and print out triplets and broken rhythm */ -struct listx* i; -int* barnotes; -int* featurecount; -int allow_broken,allow_triplets; -{ - int v1, v2, v3, vt; - int xa, xb; - int pnum; - long total, t1, t2, t3; - - - if ((chordhead != NULL) || (i == NULL) || (i->next == NULL) - /* || (asig%3 == 0) || (asig%2 != 0) 2004/may/09 SS*/) { - return(' '); - }; - t1 = i->note->dtnext; - v1 = i->note->xnum; - pnum = i->note->playnum; - if ((v1 < pnum) || (v1 > 1 + pnum) || (pnum == 0)) { - return(' '); - }; - t2 = i->next->note->dtnext; - v2 = i->next->note->xnum; - pnum = i->next->note->playnum; - if (/*(v2 < pnum) ||*/ (v2 > 1 + pnum) || (pnum == 0) || (v1+v2 > *barnotes)) { - return(' '); - }; - /* look for broken rhythm */ - total = t1 + t2; - if (total == 0L) { - /* shouldn't happen, but avoids possible divide by zero */ - return(' '); - }; - /* [SS] 2017-01-01 */ - if (allow_broken == 1 && ((v1+v2)%2 == 0) && ((v1+v2)%3 != 0)) { - vt = (v1+v2)/2; - if (vt == validnote(vt)) { - /* do not try to break a note which cannot be legally expressed */ - switch ((int) ((t1*6+(total/2))/total)) { - case 2: - *featurecount = 2; - i->note->xnum = vt; - i->note->playnum = vt; - i->next->note->xnum = vt; - i->next->note->playnum = vt; - return('<'); - break; - case 4: - *featurecount = 2; - i->note->xnum = vt; - i->note->playnum = vt; - i->next->note->xnum = vt; - i->next->note->playnum = vt; - return('>'); - break; - default: - break; - }; - }; - }; - if (allow_triplets == 0) return(' '); /* [SS] 2017-01-01 */ - /* look for triplet */ - if (i->next->next != NULL) { - t3 = i->next->next->note->dtnext; - v3 = i->next->next->note->xnum; - pnum = i->next->next->note->playnum; - if ((v3 < pnum) || (v3 > 1 + pnum) || (pnum == 0) || - (v1+v2+v3 > *barnotes)) { - return(' '); - }; - if ((v1+v2+v3)%2 != 0) { - return(' '); - }; - vt = (v1+v2+v3)/2; - if ((vt%2 == 1) && (vt > 1)) { - /* don't want strange fractions in triplet */ - return(' '); - }; - total = t1+t2+t3; - xa = (int) ((t1*6+(total/2))/total); - xb = (int) (((t1+t2)*6+(total/2))/total); - if ((xa == 2) && (xb == 4) && (vt%3 != 0) ) { - *featurecount = 3; - *barnotes = *barnotes + vt; - i->note->xnum = vt; - i->note->playnum = vt; - i->next->note->xnum = vt; - i->next->note->playnum = vt; - i->next->next->note->xnum = vt; - i->next->next->note->playnum = vt; - }; - }; - return(' '); -} - -int validnote(n) -int n; -/* work out a step which can be expressed as a musical time */ -{ - int v; - - if (n <= 4) { - v = n; - } - else { - v = 4; - while (v*2 <= n) { - v = v*2; - }; - if (v + v/2 <= n) { - v = v + v/2; - }; - }; - return(v); -} - -void handletext(t, textplace, trackno) -/* print out text occuring in the body of the track */ -/* The text is printed out at the appropriate place within the track */ -/* In addition the function handles key signature and time */ -/* signature changes that can occur in the middle of the tune. */ -long t; -struct tlistx** textplace; -int trackno; -{ - char* str; - char ch; - int type,sf,mi,nn,denom,bb; - - while (((*textplace) != NULL) && ((*textplace)->when <= t)) { - str = (*textplace)->text; - ch = *str; - type = (*textplace)->type; - if (type == 5 && noly == 1) { /* [SS] 2019-07-12 */ - *textplace = (*textplace)->next; - return; - } - - remove_carriage_returns(str); - if (((int)ch == '\\') || ((int)ch == '/')) { - inkaraoke = 1; - }; - if ((inkaraoke == 1) && (karaoke == 1)) { - switch(ch) { - case ' ': - fprintf(outhandle,"%s", str); - midline = 1; - break; - case '\\': - freshline(); - fprintf(outhandle,"w:%s", str + 1); - midline = 1; - break; - case '/': - freshline(); - fprintf(outhandle,"w:%s", str + 1); - midline = 1; - break; - default : - if (midline == 0) { - fprintf(outhandle,"%%%s", str); - } - else { - fprintf(outhandle,"-%s", str); - }; - break; - }; - newline_flag = 0; /* [SS] 2019-06-14 */ - } - else { - freshline(); - ch=*(str+1); - switch (type) { - case 0: - if (ch != '%') - fprintf(outhandle,"%%%s\n", str); - else - fprintf(outhandle,"%s\n", str); - break; - case 1: /* key signature change */ - sscanf(str,"%d %d",&sf,&mi); - if((trackno != 0 || trackcount==1) && - (active_keysig != sf)) { - setupkey(sf); - active_keysig=sf; - } - break; - case 2: /* time signature change */ - sscanf(str,"%d %d %d",&nn,&denom,&bb); - if ((trackno != 0 || trackcount ==1) && - (active_asig != nn || active_bsig != denom)) - { - setup_timesig(nn,denom,bb); - fprintf(outhandle,"M: %d/%d\n",nn,denom); - fprintf(outhandle,"L: 1/%d\n",unitlen); - active_asig=nn; - active_bsig=denom; - } - break; - case 5: /* lyric [SS] 2019-07-12*/ - if (ch != '%') - fprintf(outhandle,"%%%s\n", str); - else - fprintf(outhandle,"%s\n", str); - break; - default: - break; - } - newline_flag = 1; /* 2019-06-14 */ - } - *textplace = (*textplace)->next; - } -} - - - -/* This function identifies irregular chords, (notes which - do not exactly overlap in time). The notes in the - chords are split into separate lines (split numbers). - The xnum (delay) to next note is updated. -*/ - -int splitstart[10],splitend[10]; /* used in forming chords from notes*/ -int lastposnum[10]; /* posnum of previous note in linked list */ -int endposnum; /* posnum at last note in linked list */ -struct anote* prevnote[10]; /*previous note in linked list */ -struct listx* last_i[10]; /*note after finishing processing bar*/ -int existingsplits[10]; /* existing splits in active bar */ -struct dlistx* splitchordhead[10]; /* chordhead list for splitnum */ -struct dlistx* splitchordtail[10]; /* chordtail list for splitnum */ -int splitgap[10]; /* gap to next note at end of split measure */ - -void label_split(struct anote *note, int activesplit) -{ -/* The function assigns a split number (activesplit), to - a specific note, (*note). We also update splitstart - and splitend which specifies the region in time where - the another note must occur if it forms a proper chord. - After assigning a split number to the note we need to - update note->xnum as this indicates the gap to the - next note in the same split number. The function uses - a greedy algorithm. It assigns a note to the first - splitnumber code which satisfies the above constraint. - If it cannot find a splitnumber, a new one (voice or track) - is created. It would be nice if the voices kept the - high and low notes (in pitch) separate. -*/ - note->splitnum = activesplit; - splitstart[activesplit] = note->posnum; - splitend[activesplit] = splitstart[activesplit] + note->playnum; - if (prevnote[activesplit]) - prevnote[activesplit]->xnum = note->posnum - lastposnum[activesplit]; - lastposnum[activesplit] = note->posnum; - prevnote[activesplit] = note; - /* in case this is the last activesplit note make sure it - xnum points to end of track. Otherwise it will be changed - when the next activesplit note is labeled. - */ - note->xnum = endposnum - note->posnum; - existingsplits[activesplit]++; -} - - -int label_split_voices (int trackno) -{ -/* This function sorts all the notes in the track into - separate split part. A note is placed into a separate - part if it forms a chord in the current part but - does not have the same onset time and same end time. - If this occurs, we search for another part where - this does not happen. If we can't find such a part - a new part (split) is created. - The heuristic used needs to be improved, so - that split number 0 always contains notes and so - that notes in the same pitch range or duration are - given the same split number. -*/ -int activesplit,nsplits; -int done; -struct listx* i; -int k; -int firstposnum = 0; /* [SDG] 2020-06-03 */ -/* initializations */ -activesplit = 0; -nsplits = 0; -if (debug > 4) printf("label_split_voices:\n"); -for (k=0;k<10;k++) { - splitstart[k]=splitend[k]=lastposnum[k]=0; - prevnote[k] = NULL; - existingsplits[k] = 0; - splitgap[k]=0; - } -i = track[trackno].head; -if (track[trackno].tail == 0x0) {return 0;} -endposnum =track[trackno].tail->note->posnum + - track[trackno].tail->note->playnum; - -if (i != NULL) label_split(i->note, activesplit); -/* now label all the notes in the track */ -while (i != NULL) - { - done =0; - if (nsplits == 0) { /*no splits exist, create split number 0 */ - activesplit = 0; - nsplits++; - i->note->splitnum = activesplit; - splitstart[activesplit] = i->note->posnum; - splitend[activesplit] = splitstart[activesplit] + i->note->playnum; - firstposnum = splitstart[activesplit]; - } else { /* do a compatibility check with the last split number */ - if ( ( i->note->posnum == splitstart[activesplit] - && i->note->playnum == (splitend[activesplit] - splitstart[activesplit])) - || i->note->posnum >= splitend[activesplit]) - { - if (existingsplits[activesplit] == 0) { - last_i[activesplit] = i; - splitgap[activesplit] = i->note->posnum - firstposnum; - } - label_split(i->note, activesplit); - done = 1; - } -/* need to search for any other compatible split numbers */ - if (done == 0) for (activesplit=0;activesplitnote->posnum == splitstart[activesplit] - && i->note->playnum == splitend[activesplit] - splitstart[activesplit]) - || i->note->posnum >= splitend[activesplit]) - { - if (existingsplits[activesplit] == 0) { - last_i[activesplit] = i; - splitgap[activesplit] = i->note->posnum - firstposnum; - } - label_split(i->note,activesplit); - done = 1; - break; - } - } - -/* No compatible split number found. Create new split */ - if (done == 0) { - if(nsplits < 10) {nsplits++; activesplit = nsplits-1;} - if (existingsplits[activesplit] == 0) { - last_i[activesplit] = i; - splitgap[activesplit] = i->note->posnum - firstposnum; - } - label_split(i->note,activesplit); - } - } -if (debug>2) printf("note %d links to %d %d (%d %d)\n",i->note->pitch,activesplit, -i->note->posnum,splitstart[activesplit],splitend[activesplit]); - - i = i->next; - } /* end while loop */ -return nsplits; -} - - -int nextsplitnum(int splitnum) - { - while (splitnum < 9) { - splitnum++; - if (existingsplits[splitnum]) return splitnum; - } - return -1; -} - - -int count_splits() -{ -int i,n; -n = 0; -for (i=0;i<10;i++) - if (existingsplits[i]) n++; -return n; -} - - -/* [SS] 2019-06-26 */ -int firstgap[10]; - -void set_first_gaps (int trackno) { -struct listx* i; -int j; -int start; -int splitnumber; -start = track[trackno].startunits; -for (j=0;j<10;j++) firstgap[j] = -1; -i = track[trackno].head; -while((i != NULL)) { - splitnumber = i->note->splitnum; - if (firstgap[splitnumber] < 0) { - firstgap[splitnumber] = start + i->note->posnum; - /* printf("split = %d gap = %d\n",splitnumber,firstgap[splitnumber]);*/ - } - i = i->next; - } -} - - -/* [SS] 2019-06-13 */ -void printtrack_split (int trackno, int splitnum, int anacrusis) - { - struct listx* i; - struct tlistx* textplace; - struct tlistx* textplace0; /* track 0 text storage */ - int step, gap; - int featurecount; - int lastnote_in_split; - char broken; - int barnotes; - int barcount; - int last_barsize,barnotes_correction; - long now; - int nlines; - int bars_on_line; - - featurecount = 0; - lastnote_in_split = 0; - broken = ' '; - now = 0L; - nlines= 0; - bars_on_line = 0; - chordhead = NULL; /* [SS] 2019-06-17 */ - chordtail = NULL; - i = track[trackno].head; - /* gap = track[trackno].startunits; */ - gap = firstgap[splitnum]; /* [SS] 2019-06-26 */ - if (anacrusis > 0) { - barnotes = anacrusis; - barcount = -1; - } - else { - barnotes = barsize; - barcount = 0; - }; - last_barsize = barsize; - - textplace = track[trackno].texthead; - textplace0 = track[0].texthead; - fprintf(outhandle,"V: split%d%c\n",trackno+1,'A'+splitnum); - newline_flag = 1; /* [SS] 2019-06-14 */ - - while((i != NULL)||(gap != 0)) { - if (gap == 0) { - if (i->note->posnum + i->note->xnum == endposnum) lastnote_in_split = 1; - /* do triplet here */ - if (featurecount == 0) { - if (allow_triplets || allow_broken) { /* [SS] 2017-01-01 */ - broken = dospecial(i, &barnotes, &featurecount,allow_broken,allow_triplets); - }; - }; -/* ignore any notes that are not in the current splitnum */ - if (i->note->splitnum == splitnum) { - /*printf("\nadding "); - printnote(i); */ - addtochord(i->note); - gap = i->note->xnum; - now = i->note->time; - } - i = i->next; - advancechord(0); /* get rid of any zero length notes */ - if (trackcount > 1 && trackno !=0 && splitnum == 0) - handletext(now, &textplace0, trackno); - /* handletext(now, &textplace,trackno); 2019-06-26 */ - barnotes_correction = barsize - last_barsize; - barnotes += barnotes_correction; - last_barsize = barsize; - } - else { - step = findshortest(gap); - if (step > barnotes) { - step = barnotes; - }; - step = validnote(step); - if (step == 0) { - fatal_error("Advancing by 0 in printtrack!"); - }; - if (featurecount == 3) - { - fprintf(outhandle," (3"); - }; - printchord(step); - if ( featurecount > 0) { - featurecount = featurecount - 1; - }; - if ((featurecount == 1) && (broken != ' ')) { - fprintf(outhandle,"%c", broken); - }; - advancechord(step); - gap = gap - step; - barnotes = barnotes - step; - if (barnotes == 0) { - nlines++; - if (nlines > 5000) { - printf("\nProbably infinite loop: aborting\n"); - fprintf(outhandle,"\n\nProbably infinite loop: aborting\n"); - return; - } - fprintf(outhandle,"|"); - reset_back_array(); - barnotes = barsize; - barcount = barcount + 1; - bars_on_line++; - if (barcount >0 && barcount%bars_per_staff == 0) { - freshline(); - bars_on_line=0; - } - /* can't zero barcount because I use it for computing maxbarcount */ - else if(bars_on_line >= bars_per_line && i != NULL) { - if (!lastnote_in_split) fprintf(outhandle," \\"); - freshline(); - bars_on_line=0;} - } - else if (featurecount == 0) { - /* note grouping algorithm */ - /* [SS] 2016-07-20 waltz is a special case */ - if ((barsize/parts_per_unitlen) % 3 == 0 && asig != 3) { - if ( (barnotes/parts_per_unitlen) % 3 == 0 - &&(barnotes%parts_per_unitlen) == 0) { - fprintf(outhandle," "); - newline_flag = 0; /* [SS] 2019-06-14 */ - }; - } - else { - if (((barsize/parts_per_unitlen) % 2 == 0) - && (barnotes % parts_per_unitlen) == 0 - && ((barnotes/parts_per_unitlen) % 2 == 0)) { - fprintf(outhandle," "); - newline_flag = 0; /* [SS] 2019-06-14 */ - }; - }; - } - if (nogr) {fprintf(outhandle," "); newline_flag = 0;} - }; - if (barcount > maxbarcount) maxbarcount = barcount; - } - freshline(); /* [SS] 2019-06-17 */ -} - -void printtrack_split_voice(trackno, anacrusis) -/* print out one track as abc */ -int trackno, anacrusis; -{ - int splitnum; - long now; - int nsplits; - int i; - struct tlistx* textplace; - nsplits = label_split_voices (trackno); - /*printf("%d splits were detected\n",nsplits);*/ - set_first_gaps (trackno); /* [SS] 2019-06-26 */ - textplace = track[trackno].texthead; - midline = 0; - inkaraoke = 0; - now = 0L; - active_asig = header_asig; - active_bsig = header_bsig; - setup_timesig(header_asig,header_bsig,header_bb); - active_keysig = header_keysig; - handletext(now, &textplace, trackno); - splitnum = 0; - /* gap = splitgap[splitnum]; [SS] 2019-05-12 */ - /* [SS] 2019-06-13 */ - for (i=0;i 0) { - barnotes = anacrusis; - barcount = -1; - } - else { - barnotes = barsize; - barcount = 0; - }; - bars_on_line = 0; - last_barsize = barsize; - active_asig = header_asig; - active_bsig = header_bsig; - setup_timesig(header_asig,header_bsig,header_bb); - active_keysig = header_keysig; - handletext(now, &textplace, trackno); - - while((i != NULL)||(gap != 0)) { - if (debug > 4) printf("gap = %d\n",gap); - if (gap == 0) { - /* do triplet here */ - if (featurecount == 0) { - if (allow_triplets || allow_broken) { /* [SS] 2017-01-01 */ - broken = dospecial(i, &barnotes, &featurecount,allow_broken,allow_triplets); - }; - }; - /* add notes to chord */ - addtochord(i->note); - gap = i->note->xnum; - now = i->note->time; - i = i->next; - advancechord(0); /* get rid of any zero length notes */ - - /*if (trackcount > 1 && trackno !=0) - handletext(now, &textplace0, trackno); - [SS] 2019-12-30 */ - - handletext(now, &textplace,trackno); - barnotes_correction = barsize - last_barsize; - barnotes += barnotes_correction; - last_barsize = barsize; - } - else { - step = findshortest(gap); - if (step > barnotes) { - step = barnotes; - }; - step = validnote(step); - if (step == 0) { - fatal_error("Advancing by 0 in printtrack!"); - }; - if (featurecount == 3) - { - fprintf(outhandle," (3"); - }; - printchord(step); - if ( featurecount > 0) { - featurecount = featurecount - 1; - }; - if ((featurecount == 1) && (broken != ' ')) { - fprintf(outhandle,"%c", broken); - }; - advancechord(step); - gap = gap - step; - barnotes = barnotes - step; - if (barnotes == 0) { - fprintf(outhandle,"|"); - reset_back_array(); - barnotes = barsize; - barcount = barcount + 1; - bars_on_line++; - if (barcount >0 && barcount%bars_per_staff == 0) { - freshline(); - bars_on_line=0; - } - /* can't zero barcount because I use it for computing maxbarcount */ - else if(bars_on_line >= bars_per_line && i != NULL) { - fprintf(outhandle," \\"); - freshline(); - bars_on_line=0;} - } - else if (featurecount == 0) { - /* note grouping algorithm */ - /* [SS] 2016-07-20 waltz is a special case */ - if ((barsize/parts_per_unitlen) % 3 == 0 && asig != 3) { - if ( (barnotes/parts_per_unitlen) % 3 == 0 - &&(barnotes%parts_per_unitlen) == 0) { - fprintf(outhandle," "); - newline_flag = 0; /* [SS] 2019-06-14 */ - }; - } - else { - if (((barsize/parts_per_unitlen) % 2 == 0) - && (barnotes % parts_per_unitlen) == 0 - && ((barnotes/parts_per_unitlen) % 2 == 0)) { - fprintf(outhandle," "); - newline_flag = 0; /* [SS] 2019-06-14 */ - }; - }; - } - if (nogr) {fprintf(outhandle," "); - newline_flag = 0; - } - }; - }; - /* print out all extra text */ - while (textplace != NULL) { - handletext(textplace->when, &textplace, trackno); - }; - freshline(); - if (barcount > maxbarcount) maxbarcount = barcount; -} - - - - -void remove_carriage_returns(char *str) -{ -/* a carriage return might be embedded in a midi text meta-event. - do not output this in the abc file or this would make a nonsyntactic - abc file. -*/ -char * loc; -while (loc = (char *) strchr(str,'\r')) *loc = ' '; -while (loc = (char *) strchr(str,'\n')) *loc = ' '; -} - - - -void printQ() -/* print out tempo for abc */ -{ - float Tnote, freq; - Tnote = mf_ticks2sec((long)((xunit*unitlen)/4), division, tempo); - freq = (float) 60.0/Tnote; - fprintf(outhandle,"Q:1/4=%d\n", (int) (freq+0.5)); - if (summary>0) printf("Tempo: %d quarter notes per minute\n", - (int) (freq + 0.5)); -} - - - -/* [SS] 2019-05-08 */ -void reset_back_array () -/* reset back[] after each bar line */ -{ - int i; - for (i=0;i<256;i++) back[i] = barback[i]; -} - -void setupkey(sharps) -int sharps; -/* set up variables related to key signature */ -{ - char sharp[13], flat[13], shsymbol[13], flsymbol[13]; - int j, t, issharp; - int minkey; - - for (j=0; j<12; j++) - key[j] = 0; - minkey = (sharps+12)%12; - if (minkey%2 != 0) { - minkey = (minkey+6)%12; - }; - /* [SS] 2017-12-20 changed strncpy to memcpy and limit to 12 */ - /*strncpy(sharp, "ccddeffggaab",16); [SS] 2017-08-30 */ - memcpy(sharp, "ccddeffggaab",12); /* [SS] 2017-12-20 */ - memcpy(shsymbol, "=^=^==^=^=^=",12); /* [SS] 2017-12-20 */ - if (sharps == 6) { - sharp[6] = 'e'; - shsymbol[6] = '^'; - }; - memcpy(flat, "cddeefggaabb",12); /* [SS] 2017-12-20 */ - memcpy(flsymbol, "=_=_==_=_=_=",12); /* [SS] 2017-12-20 */ - /* Print out key */ - - if (sharps >= 0) { - if (sharps == 6) { - fprintf(outhandle,"K:F#"); - } - else { - fprintf(outhandle,"K:%c", sharp[minkey] + 'A' - 'a'); - }; - issharp = 1; - } - else { - if (sharps == -1) { - fprintf(outhandle,"K:%c", flat[minkey] + 'A' - 'a'); - } - else { - fprintf(outhandle,"K:%cb", flat[minkey] + 'A' - 'a'); - }; - issharp = 0; - }; - if (sharps >= 0) { - fprintf(outhandle," %% %d sharps\n", sharps); - } - else { - fprintf(outhandle," %% %d flats\n", -sharps); - }; - key[(minkey+1)%12] = 1; - key[(minkey+3)%12] = 1; - key[(minkey+6)%12] = 1; - key[(minkey+8)%12] = 1; - key[(minkey+10)%12] = 1; - for (j=0; j<256; j++) { - t = j%12; - if (issharp) { - atog[j] = sharp[t]; - symbol[j] = shsymbol[t]; - } - else { - atog[j] = flat[t]; - symbol[j] = flsymbol[t]; - }; - trans[j] = 7*(j/12)+((int) atog[j] - 'a'); - if (j < MIDDLE) { - atog[j] = (char) (int) atog[j] + 'A' - 'a'; - }; - if (key[t] == 0) { - barback[trans[j]] = j; /* [SS] 2019-05-08 */ - }; - }; -reset_back_array(); /* [SS] 2019-05-08 */ -} - - - - -/* Functions for supporting the command line user interface to midi2abc. */ - - -int readnum(num) -/* read a number from a string */ -/* used for processing command line */ -char *num; -{ - int t; - char *p; - int neg; - - t = 0; - neg = 1; - p = num; - if (*p == '-') { - p = p + 1; - neg = -1; - }; - while (((int)*p >= '0') && ((int)*p <= '9')) { - t = t * 10 + (int) *p - '0'; - p = p + 1; - }; - return neg*t; -} - - -int readnump(p) -/* read a number from a string (subtly different) */ -/* used for processing command line */ -char **p; -{ - int t; - - t = 0; - while (((int)**p >= '0') && ((int)**p <= '9')) { - t = t * 10 + (int) **p - '0'; - *p = *p + 1; - }; - return t; -} - - -void readsig(a, b, sig) -/* read time signature */ -/* used for processing command line */ -int *a, *b; -char *sig; -{ - char *p; - int t; - - p = sig; - if ((int)*p == 'C') { - *a = 4; - *b = 4; - return; - }; - *a = readnump(&p); - if ((int)*p != '/') { - char msg[80]; - - sprintf(msg, "Expecting / in time signature found %c!", *p); - fatal_error(msg); - }; - p = p + 1; - *b = readnump(&p); - if ((*a == 0) || (*b == 0)) { - char msg[80]; - - sprintf(msg, "%d/%d is not a valid time signature!", *a, *b); - fatal_error(msg); - }; - t = *b; - while (t > 1) { - if (t%2 != 0) { - fatal_error("Bad key signature, divisor must be a power of 2!"); - } - else { - t = t/2; - }; - }; -} - -int is_power_of_two(int numb) -/* checks whether numb is a power of 2 less than 256 */ -{ -int i,k; -k = 1; -for (i= 0;i<8;i++) { - if(numb == k) return(1); - k *= 2; - } -return(0); -} - -int getarg(option, argc, argv) -/* extract arguments from command line */ -char *option; -char *argv[]; -int argc; -{ - int j, place; - - place = -1; - for (j=0; j= argc+1) { /* [SS] 2015-02-22 */ - Qval = readnum(argv[arg]); - } - else { - Qval = 0; - }; - arg = getarg("-u", argc,argv); - if (arg != -1) { - xunit = readnum(argv[arg]); - xunit_set = 1; - } - else { - xunit = 0; - xunit_set = 0; - }; - arg = getarg("-ppu",argc,argv); - if (arg != -1) { - val = readnum(argv[arg]); - if (is_power_of_two(val)) parts_per_unitlen = val; - else { - printf("*error* -ppu parameter must be a power of 2\n"); - parts_per_unitlen = 2; - } - } - else - parts_per_unitlen = 2; - arg = getarg("-aul",argc,argv); - if (arg != -1) { - val = readnum(argv[arg]); - if (is_power_of_two(val)) { - unitlen = val; - unitlen_set = 1;} - else - printf("*error* -aul parameter must be a power of 2\n"); - } - arg = getarg("-bps",argc,argv); - if (arg != -1) - bars_per_staff = readnum(argv[arg]); - else { - bars_per_staff=4; - }; - arg = getarg("-bpl",argc,argv); - if (arg != -1) - bars_per_line = readnum(argv[arg]); - else { - bars_per_line=1; - }; - - - extracta = (getarg("-xa", argc, argv) != -1); - guessa = (getarg("-ga", argc, argv) != -1); - guessu = (getarg("-gu", argc, argv) != -1); - guessk = (getarg("-gk", argc, argv) != -1); - keep_short = (getarg("-s", argc, argv) != -1); - summary = getarg("-sum",argc,argv); - swallow_rests = getarg("-sr",argc,argv); - if (swallow_rests != -1) { - restsize = readnum(argv[swallow_rests]); - if(restsize <1) restsize=1; - } - obpl = getarg("-obpl",argc,argv); - if (obpl>= 0) bars_per_line=1; - if (!unitlen_set) { - if ((asig*4)/bsig >= 3) { - unitlen =8; - } - else { - unitlen = 16; - }; - } - arg = getarg("-b", argc, argv); - if ((arg != -1) && (arg < argc)) { - bars = readnum(argv[arg]); - } - else { - bars = 0; - }; - arg = getarg("-c", argc, argv); - if ((arg != -1) && (arg < argc)) { - xchannel = readnum(argv[arg]) - 1; - } - else { - xchannel = -1; - }; - arg = getarg("-k", argc, argv); - if ((arg != -1) && (arg < argc)) { - keysig = readnum(argv[arg]); - if (keysig<-6) keysig = 12 - ((-keysig)%12); - if (keysig>6) keysig = keysig%12; - if (keysig>6) keysig = keysig - 12; - ksig_set = 1; - } - else { - keysig = -50; - ksig_set = 0; - }; - - if(guessk) ksig_set=1; - - arg = getarg("-o",argc,argv); - if ((arg != -1) && (arg < argc)) { - outhandle = efopen(argv[arg],"w"); /* open output abc file */ - } - else { - outhandle = stdout; - }; - arg = getarg("-nt", argc, argv); - if (arg == -1) { /* 2017-01-01 */ - allow_triplets = 1; - } - else { - allow_triplets = 0; - }; - arg = getarg("-nb", argc, argv); - if (arg == -1) { /* 2017-01-01 */ - allow_broken = 1; - } - else { - allow_broken = 0; - }; - arg = getarg("-nogr",argc,argv); - if (arg != -1) - nogr=1; - else nogr = 0; - - arg = getarg("-noly",argc,argv); /* [SS] 2019-07-12 */ - if (arg != -1) - noly=1; - else noly = 0; - - arg = getarg("-title",argc,argv); - if (arg != -1) { - title = addstring(argv[arg]); - } - - arg = getarg("-origin",argc,argv); - if (arg != -1) { - origin = addstring(argv[arg]); - } - - - arg = getarg("-f", argc, argv); - if (arg == -1) { - arg = huntfilename(argc, argv); - }; - - if ((arg != -1) && (arg < argc)) { - F = efopen(argv[arg],"rb"); -/* fprintf(outhandle,"%% input file %s\n", argv[arg]); */ - } - else { - printf("midi2abc version %s\n usage :\n",VERSION); - printf("midi2abc filename \n"); - printf(" -a \n"); - printf(" -xa Extract anacrusis from file "); - printf("(find first strong note)\n"); - printf(" -ga Guess anacrusis (minimize ties across bars)\n"); - printf(" -gk Guess key signature \n"); - printf(" -gu Guess xunit from note duration statistics\n"); - printf(" -m