Comments on the coding of VAMP     
    Stan Swanson, May 15, 2004

The code of VAMP is grouped into six files plus globals.c and globals.h.

The liberal use of global variables is partly because the original code was
split into six files from one, and partly due to my programming style,
which prefers simple argument lists.  There will be comments on some of
the more important global variables below.

In Linux there is a Makefile, and Windows requires only that the spawn of timidity
be suppressed (see spawn_play() in vamp_midi.c).

The six files are

vamp_main.c  -- parses input except for harmony and loops.

vamp_harm.c  -- parses harmony command and does analysis

vamp_loops.c -- parses loop command and makes a midi file from it

vamp_abc.c   -- parses input "ABC" syntax for melody and vamp, deals with repeats
                  ( see nextnote() and repeat_frags() )

vamp_midi.c  -- generates MIDI output file
                  ( most of the work is done in gen_midi_music_track() )

vamp_utils.c -- utilities for sorting, character input, token matching, file stacking,
                extracting notes in measures for harmony analysis...

List of function declarations as of May 15, 2004

vamp_abc.c:9:void set_meter(void) /* look at line[] for meter info */
vamp_abc.c:23:void set_default_note(void) /* look for default note length */
vamp_abc.c:40:void set_key(void) /* look at line[] for key signature */
vamp_abc.c:115:int kromatic( int delta, int octave, char accidental)
vamp_abc.c:135:int nextnote(int *tone, int *duration, int* vol)
vamp_abc.c:230:void read_music(void)
vamp_abc.c:292: int read_abc(int act, char* filnam)
vamp_abc.c:311:void repeat_frags(void)  

vamp_harm.c:55:void print_all_chords(int tonic, int nchords)
vamp_harm.c:98:int chord_number(char *gc)  /* convert guitar chord string to internal code */
vamp_harm.c:107:void save_chord_scores(int meas, int *jot, int n)
vamp_harm.c:115:void analyse_harmony(char *line)
vamp_harm.c:270:int consonances(int nstart, int nend)
vamp_harm.c:293:int find_center(int nstart, int nend, int meas, int tonic, int period, int verbose)
vamp_harm.c:314:void set_chord_prefs(int tn)
vamp_harm.c:387:int analyse_notes( int tonic,  int verbose, int mode)
vamp_harm.c:439:int print_chord_matches(int n, int *jot, float *dwells, int dur, 
vamp_harm.c:464:int calc_wt_sums(int *jot, float *dwells, int *diver, int *durat)
vamp_harm.c:539:int test_all_chords(int tonic, int verbose, int mode)
vamp_harm.c:569:int halfmeas(int ki, int kf)
vamp_harm.c:691:int other_alg(int ki, int kf)
vamp_harm.c:823:void print_stats(FILE *fo)

vamp_loops.c:8:void print_help(void)   /* print contents of help[] */
vamp_loops.c:15:void generate_loops(char *midiout)
vamp_loops.c:126:void session_prefix(void)
vamp_loops.c:151:int loop_in(void)

vamp_main.c:7:void read_line(void)  /* next_line handling indirect @ and EOF */
vamp_main.c:24:void parse_phrasing(char* id)
vamp_main.c:115:int get_velocities(int evtype)  /* determine velocity source for generated midi */
vamp_main.c:142:void parse_accent(char *id)
vamp_main.c:197:int main(void)  /* parses input and calls other routines */


vamp_midi.c:8:void lowendian(int n, char *in, char *out)
vamp_midi.c:12:void intout(int i)
vamp_midi.c:20:void shout(short sh)
vamp_midi.c:28:void strout(char *str, int n)
vamp_midi.c:34:void byteout(int v) /* positive byte output 0-127 */
vamp_midi.c:39:void delta(int v) /* MIDI variable count, eventually up to 128^4 - 1 */
vamp_midi.c:46:void command(int cmd, int chan, int note, int veloc)
vamp_midi.c:53:void change(int cmd, int chan, int prog)
vamp_midi.c:59:void midi_file_header(int org, int tracks, int delta_time, char *name)
vamp_midi.c:78:void microseconds_in_beat(float bpm)
vamp_midi.c:90:void midi_info(char *title)
vamp_midi.c:152:int spawn_play(char *midifile)  /* suppress system call for WINDOWS */
vamp_midi.c:175:void pack_event( int *kx, int ticks, int cmd, int chan, int d1, int d2)
vamp_midi.c:185:int sort_gen_events(int *kx, int *previous, int now)
vamp_midi.c:214:void set_dither(int blur)  /* scaling parameters for dither */
vamp_midi.c:233:int dither(int n)  /* (n==1) probably "melody", otherwise "chord" */
vamp_midi.c:249:void make_notes(int n, int *tones, int now, int dur, int vol, int chan)
vamp_midi.c:264:int start_midi_music_track(int instrum, int chan, int chords)
     /* vamp_midi.c:318:int gen_midi_music_track(int npi, int npf, int chan, int chords)  */
vamp_midi.c:441:int gen_midi_music_track(int npi, int npf, int chan, int chords)
vamp_midi.c:547:int write_midi_music_track(void)
vamp_midi.c:572:void gen_midi_frags(int kchunk, int chan, int chords, int coda)  

vamp_utils.c:8:void sort_i(int *v, int m)   /* single array of integers */
vamp_utils.c:18:void sort_ii(int *v, int *u, int m) 
vamp_utils.c:26:void sort_dd(double *v, double *u, int m)
vamp_utils.c:34:void print_help(void)   /* print contents of help[] */
vamp_utils.c:39:void scan_list(int *list, char *buf)  /* recover enumerated and for lists */
vamp_utils.c:53:int finstring(char *ch, int max, FILE *f)
vamp_utils.c:64:int next_line(void)
vamp_utils.c:71:int next_chr(void)  /* next character in line, but don't over run */
vamp_utils.c:75:void deblank(void)
vamp_utils.c:78:int get_integer(int *ival) /* convert digit string into decimal integer */
vamp_utils.c:85:void copy_line(void)  /* make copy of line so find_token can put '\0' to end strings */
vamp_utils.c:88:void lower_the_case(char *s, char *d, int n)
vamp_utils.c:99:char *find_token(char **start) 
vamp_utils.c:118:int match_token(char *token, char **list, int nlist, int exact)
vamp_utils.c:138:int save_token(char *token, char **list, int *nlist)
vamp_utils.c:151:char *make_id(char *token, char *prefix, int kount)
vamp_utils.c:168:int stack_files(FILE* fi) 
vamp_utils.c:178:int pop_file(void)
vamp_utils.c:184:int find_measures(int mel, int verbose)
vamp_utils.c:271:int select_notes(int nstart, int nend, int period, int mode, int barbreak)
vamp_utils.c:301:void select_accented(int beats) /* prune selected notes to accented ones */
vamp_utils.c:318:int abc_ticks(int ip, int lp)  /* total abc duration tics[ip] ... tics[lp-1] */
vamp_utils.c:334:int pedometer(int dur) /* which step (beat) in phrase */

--------------------------------------------

Comments on globals.c, globals.h:

/* limits for array sizes: most of these are explicitly checked when elements are added */

# define MAXOUT 50000    /* size of MIDI out buffer bout[] */
# define MXNOTE  4096    /* decoded abc stuff (melody, vamp, chords)  */
# define MXLIST    23    /* maximum list elements +3 */
# define MAXIN    136    /* length of input buffers */
# define MXCHUNK  100    /* number of abc chunks input */
# define MXCHAR  1024    /* characters for string storage */
# define MXGC    2048    /* maximum characters in guitar chords */
# define MXACC   1024    /* accent entries for phrasing */
# define MXFRAG  1024    /* fragment storage (repeats, chord limits) */
# define MXVAR    100    /* events for program-variation */
# define MXPT     512    /* selected notes stone[], stick[] */

/* flags for syntax analysis and execution control: modified in main() */

int Q_META=1, Q_ABC=0, Q_PHRASE=0, Q_CHORD=0, Q_VAMP=0, Q_LOOP=0, Q_EVENT=0,
    Q_ACCENT=0, Q_EXPERT=0, V_CLASS=0,
    Q_VERBOSE=1, Q_PLAY=1, Q_BLUR=0, Q_TESTA=0, Q_TESTB=0, EXPAND_CHORDS=0;

/* pseudo enums for abc syntactic constructs (non-notes) cf. nextnote() */
const int E_REST=128, E_KEY=-2,
   E_START_CHORD=-4, E_END_CHORD=-5, E_GUITAR_CHORD = -6,
   E_TIE = -7, E_BAR=-9, E_REP=-10, E_REP_1=-11, E_REP_2=-12, E_REP_last=-13;

/* for chunk[].type */
const int T_MEL=1, T_VAMP=0, T_CHORD=2,
             T_ACCENT=-1, T_PHRASE=-2, T_SPECIAL=-4;
 

char bout[MAXOUT]; /* MIDI output buffer (holds entire generated file) */

/* remember symbolic identifiers used in input */
char chunkname[MXCHUNK][8], sav_strings[MXCHAR], guitar_ch[MXGC],
   *tokens[MXCHUNK] = { "tune","chords"};

/* pitch[] and tics[] hold the decoded "ABC" input for melody and vamps */
int pitch[MXNOTE], tics[MXNOTE], scale[128]; 
int epoch[MXNOTE], event[MXNOTE], efirst[MXCHUNK],elast[MXCHUNK];
 
/* 031.29: a structure to handle named melodies, vamps, chord sequences and accents */
/* .ix and .lx may be offsets into data arrays, depending on .type */
struct {char* name; int type, count, ix, lx; } chunk[MXCHUNK];

/* 031.29 start:(stop+1) pairs for complicated chunks(count>0) */
/* melody fragments (if repeats), vamps referenced by chord sequence */
int kfrag=0, kev, frags[MXFRAG], jot[100];

/* pseudo enum for metacommand ID (== index postion of string) */
/* these are used in the top level parse switch in main() */

# define  MC_QUIT     1
# define  MC_MELODY   2
# define  MC_VAMP     3
# define  MC_CHORDS   4
# define  MC_ACCENT   5
# define  MC_VARIATION 6
# define  MC_LOOP     7
# define  MC_INSTRUM  8
# define  MC_TEMPO    9
# define  MC_GEN     10
# define  MC_HARMONY 11
# define  MC_REPEAT  12
# define  MC_MIDI    13
# define  MC_PARAM   14
# define  MC_EVENT   15
# define  MC_XTRA    16
# define  MC_PHRASE  17
                                                                                

/* information from the accent and phrase commands goes into phrasing[] */
int phrasing[MXACC] = { 6,8,  0,0,  0,0,  8,8,     /* [0:7] are default phrase chunk */
                        120,1060,80,1060,  90,1060,80,1060,
                        100,1060,80,1060,  90,1060,80,1060 };

/*  data arrays for the list command */
int accent_list[MXLIST] = {0,1,0,0};   /* {1,3,0,0,41,5} */
int key_list[MXLIST] =    {0,1,0,0};   /*  {0,1,0,60} */
int loop_list[MXLIST] =    {0,1,0,1,2,3,4};
int instrument_list[MXLIST] = {0,5,0,111,1,25,72,106};
                /* 031.09  fiddle,piano,guitar,clarinet,banjo */
/* int instrument_list[MXLIST] = {0,5,0,1,1,1,1,1};  */
                /*  02x.06 all piano  */
int veloc_list[MXLIST] = {0,4,0,2040,40,1040,40,20,10};
    /* vel+2000 is major accent, vel+1000 is minor accent */
int rhythm_list[MXLIST] = {0,4,0,120,120,120,120};
int tone_list[MXLIST] = {0,4,0,0,4,7,5,12};
int controls[10][MXLIST];  /* control byte sequences */
int order_list[MXLIST] = {0,4,0,  0, 1, 2, 3 }; /* default r l a i */
 

/* stone[0..snotes], stick[] are used by the harmony analysis routines
   to hold notes and other ABC information for the current measure/half measure */
int nmeas, snotes, shalf, smeas, stone[MXPT], stick[MXPT];

/* bars holds indices and information about measure positions in the current melody */
struct { int s,h,e,t,m; } bars[MXPT];

/* flags for bars[].m */
# define B_LEN   1   /* nonstandard length != ticks_in_meas  */
# define B_SPLIT 2   /* bad half measure split != duration_in_meas/2 */
# define B_REP   4   /* has repeat sign in measure |: or :| */
# define B_END   8   /* has alternate ending [1 [2 [3 */
# define B_KEY  16   /* key defined, redefined */