/*
 * abc2midi - program to convert abc files to MIDI files.
 * Copyright (C) 1999 James Allwright
 * e-mail: J.R.Allwright@westminster.ac.uk
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 *
 */

/* store.c  
 * This file handles all event_X() calls from parseabc.c and stores them 
 * in various arrays and data structures. The midi is then generated by
 * calling mfwrite() which in turn uses the routine writetrack().
 * This file is part of abc2midi.
 *
 * James Allwright
 *
 * Macintosh Port 30th July 1996
 * Wil Macaulay (wil@syndesis.com)
 */

#define VERSION "3.27 April 10 2014"
/* enables reading V: indication in header */
#define XTEN1 1
/*#define INFO_OCTAVE_DISABLED 1*/

/* for Microsoft Visual C++ 6.0 and higher */
#ifdef _MSC_VER
#define ANSILIBS
#define strcasecmp stricmp
#endif


#ifdef WIN32
#define snprintf _snprintf
#endif

#include "abc.h"
#include "parseabc.h"
#include "parser2.h"
#include "midifile.h"
#include "genmidi.h"
#include <stdio.h>
#include <math.h>

#ifdef __MWERKS__
#define __MACINTOSH__ 1
#endif /* __MWERKS__ */

#ifdef __MACINTOSH__
int setOutFileCreator(char *fileName,unsigned long theType,
                      unsigned long theCreator);
#endif /* __MACINTOSH__ */
/* define USE_INDEX if your C libraries have index() instead of strchr() */
#ifdef USE_INDEX
#define strchr index
#endif

#ifdef ANSILIBS
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#else
extern char* strchr();
extern void reduce();
#endif
/*int snprintf(char *str, size_t size, const char *format, ...);*/
void load_stress_parameters(char *);

#define MAXLINE 500
#define INITTEXTS 20
#define INITWORDS 20
#define MAXCHANS 16

/* global variables grouped roughly by function */

FILE *fp;

/*#define MAKAM*/
#ifdef MAKAM
FILE *fc53; /* for debugging */
#endif

programname fileprogram = ABC2MIDI;
extern int oldchordconvention; /* for handling +..+ chords */

/* 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 velocitychange = 15;
int chordstart=0;
int nopropagate_accidentals = 0;
/* microtonal support and scale temperament */
int active_pitchbend;
extern struct fraction setmicrotone; /* [SS] 2014-01-07 */
extern int microtone;
int temperament = 0;
#define SEMISIZE 4096
int octave_size = 12*SEMISIZE;
int fifth_size = 7*SEMISIZE; /* default to 12-edo */
int started_parsing=0;
int v1index= -1;
int ignore_fermata = 0; /* [SS] 2010-01-06 */
int ignore_gracenotes = 0; /* [SS] 2010-01-08 */
int separate_tracks_for_words = 0; /* [SS] 2010-02-02 */
int bodystarted =0;
int harpmode=0;  /* [JS] 2011-04-29 */
int easyabcmode = 1; /* [SS] 2011-07-18 */
int barflymode = 1; /* [SS] 2011-08-19 */
char rhythmdesignator[32]; /* [SS] 2011-08-19 */
int retuning = 0; /* [SS] 2012-04-01 */
int bend = 8192; /* [SS] 2012-04-01 */
int comma53 = 0; /* [SS] 2014-01-12 */
void init_p48toc53 (); /* [SS] 2014-01-12 */ 
void convert_to_comma53 (char acc, int *midipitch, int* midibend);  

struct voicecontext {
/* not to be confused with struct voice defined in struct.h and
   used only by yaps.
*/
  /* maps of accidentals for each stave line */
  char basemap[7], workmap[7][10];
  int  basemul[7], workmul[7][10];
  struct fraction basemic[7],workmic[7][10];
  int  keyset; /* flag to indicate whether key signature set */
  int default_length;
  int active_meter_num; /* [SS] 2012-11-08 */
  int active_meter_denom; /* [SS] 2012-11-08 */
  int voiceno; /* voice number referenced by V: command. To avoid
                  conflicts with split voices, all split voices
                  begin from 32. */
  int indexno; /* voice index number in the feat array. It just
                  increments by one and depends on the order the
                  voices are created -- including split voices.
               */
  int topvoiceno,topindexno; /* links to original voice in the split */
  int hasgchords;
  int haswords;
  int hasdrums;
  int hasdrone;
  int inslur;
  int ingrace;
  int octaveshift;
  int lastbarloc;  /* position of last bar line parsed */
  int tosplitno,fromsplitno; /* links to split voices and source voice*/ 
  int last_resync_point;
  /* 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; /* for handling broken rhythms*/
  /* broken rhythm handling */
  int brokentype, brokenmult, brokenpending;
  int broken_stack[7];
  struct voicecontext* next;
  int drumchannel;
};
struct voicecontext global;
struct voicecontext* v;
struct voicecontext* head;
struct voicecontext* vaddr[64]; /* address of all voices (by v->indexno) */
/* vaddr is only a convenience for debugging */


/* [SS] 2012-06-30 */
/* structure for expanding a note into a TRILL, ROLL, or ORNAMENT */
struct notestruct {
  int note;
  int index;
  int notetype;
  int pitch;
  int pitchup;
  int bendup;
  int benddown;
  int pitchdown;
  int default_length;
  };

struct notestruct* noteaddr[1000];
int notesdefined = 1;



struct trackstruct trackdescriptor[40]; /* trackstruct defined in genmidi.h*/
 

int dependent_voice[64]; /* flag to indicate type of voice */
int voicecount;
int numsplits=0;
int splitdepth = 0;

/* storage structure for strings */
int maxtexts = INITTEXTS;
char** atext;
int ntexts = 0;

/* Named guitar chords */
char chordname[MAXCHORDNAMES][8];
/* int chordnotes[MAXCHORDNAMES][6]; */
int chordnotes[MAXCHORDNAMES][10]; /* [SS] 2012-01-29 */
int chordlen[MAXCHORDNAMES];
int chordsnamed = 0;

/* general purpose storage structure */
int maxnotes;
int *pitch, *num, *denom;
int *bentpitch; /* needed for handling microtones */
featuretype *feature;
int *stressvelocity;  /* [SS] 2011-08-17 for Phil's stress model*/
int *pitchline; /* introduced for handling ties */
int *decotype; /* [SS] 2012-06-29 for handling ROLLS, TRILLS, etc. */
int notes;

int verbose = 0;
int titlenames = 0;
int got_titlename;
int namelimit;
int xmatch;
int sf, mi;
int gchordvoice, wordvoice, drumvoice, dronevoice;
int ratio_standard = -1; /* flag corresponding to -RS parameter */
/* when ratio_standard != -1 the ratio for a>b is 3:1 instead of 2:1 */
int quiet = -1; /* if not -1 many common warnings and error messages */
                /* are suppressed.                                   */
int fermata_fixed = 0; /* flag on how to process fermata */
int apply_fermata_to_chord = 0; /* [SS] 2012-03-26 */

/* Part handling */
struct vstring part;
extern int parts, partno, partlabel;
extern int part_start[26], part_count[26];

int voicesused;

/* Tempo handling (Q: field) */
int time_num, time_denom;
int mtime_num, mtime_denom;
long tempo;
int tempo_num, tempo_denom;
int relative_tempo, Qtempo;
extern int division;
extern int div_factor;
int default_tempo = 120; /* quarter notes per minutes */

/* for get_tempo_from_name  [SS] 2010-12-07 */
char *temponame[19] = {"larghissimo" , "adagissimo", "lentissimo",
   "largo", "adagio", "lento", "larghetto", "adagietto", "andante",
   "andantino", "moderato", "allegretto", "allegro", "vivace",
   "vivo", "presto", "allegrissimo", "vivacissimo", "prestissimo"};
int temporate[19] = {40,            44,             48,
    56,      59,       62,       66,        76,        88,
    96,          104,       112,          120,       168,
   180,     192,      208,            220,          240}; 


/* output file generation */
int userfilename = 0;
char *outname = NULL;
char *outbase = NULL;
int check;
int nofnop; /* for suppressing dynamics (ff, pp etc) */
int nocom;  /* for suppressing comments in MIDI file */
int ntracks;

/* bar length checking */
extern int bar_num, bar_denom;
int barchecking;

/* generating MIDI output */
int middle_c;
extern int channels[MAXCHANS + 3];
extern int additive;
int gfact_num, gfact_denom, gfact_method;  /* for handling grace notes */

/* karaoke handling */
int karaoke, wcount;
char** words;
int maxwords = INITWORDS;

extern int decorators_passback[DECSIZE]; /* a kludge for passing
information from the event_handle_instruction to parsenote
in parseabc.c */


extern int inchordflag; /* [SS] 2012-03-30 */
/* for reseting decorators_passback in parseabc.c */

/* time signature after header processed */
int header_time_num,header_time_denom;

int dummydecorator[DECSIZE]; /* used in event_chord */
extern char* featname[];

char *csmfilename = NULL;  /* [SS] 2013-04-10 */


void addfract(int *xnum, int *xdenom, int a, int b);
static void zerobar();
static void addfeature(int f,int p,int n,int d);
static void replacefeature(int f, int p, int n, int d, int loc);
void insertfeature(int f, int p, int n, int d, int loc);
static void textfeature(int type, char *s);
extern long writetrack();
void init_drum_map();
static void fix_enclosed_note_lengths(int from, int end);
static int patchup_chordtie(int chordstart,int chordend);
static void copymap(struct voicecontext* v);
void init_stresspat();
void beat_modifier(int);
void readstressfile (char * filename);
int parse_stress_params();
void calculate_stress_parameters();
extern int inbody; /* from parseabc.c [SS] 2009-12-18 */
extern int lineposition; /* from parseabc.c [SS] 2011-07-18 */
extern int beatmodel; /* from genmidi.c [SS] 2011-08-26 */
int stressmodel;


static struct voicecontext* newvoice(n)
/* allocate and initialize the data for a new voice */
int n;
{
  struct voicecontext *s;
  int i,j;

  s = (struct voicecontext*) checkmalloc(sizeof(struct voicecontext));
  voicecount = voicecount + 1;
  s->voiceno = n;
  s->indexno = voicecount;
  s->topvoiceno = n;
  s->topindexno = voicecount;
  s->default_length = global.default_length;
  s->active_meter_num = time_num;     /* [SS] 2012-11-08 */
  s->active_meter_denom = time_denom; /* [SS] 2012-11-08 */
  mtime_num = time_num;     /* [SS] 2012-11-08 */
  mtime_denom = time_denom; /* [SS] 2012-11-08 */
  s->hasgchords = 0;
  s->haswords = 0;
  s->hasdrums = 0;
  s->hasdrone = 0;
  s->inslur = 0;
  s->ingrace = 0;
  s->inchord = 0;
  s->chordcount = 0;
  s->lastbarloc = -1;
  s->laststart = -1;
  s->lastend = -1;
  s->thisstart = -1;
  s->thisend = -1;
  s->brokenpending = -1;
  s->tosplitno = -1;
  s->fromsplitno = -1;  
  s->last_resync_point=0;
  s->next = NULL;
    for (i=0; i<7; i++) {
      s->basemap[i] = global.basemap[i];
      s->basemul[i] = global.basemul[i];
      s->basemic[i].num = global.basemic[i].num; /* [SS] 2014-01-08 */
      s->basemic[i].denom = global.basemic[i].denom;
      for (j=0;j<10;j++) {
        s->workmap[i][j] = global.workmap[i][j];
        s->workmul[i][j] = global.workmul[i][j];
        s->workmic[i][j].num =   global.workmic[i][j].num; /* [SS] 2014-01-26 */
        s->workmic[i][j].denom = global.workmic[i][j].denom;
        };
    }
  s->keyset = global.keyset;
  s->octaveshift = global.octaveshift;
  s->drumchannel = 0;
  if (voicecount < 0 || voicecount >63)
     printf("illegal voicecount = %d\n",voicecount); /* [SS] 2012-11-25 */
  vaddr[voicecount] = s;
  return(s);
}

static struct voicecontext* getvoicecontext(n)
/* find the data structure for a given voice number */
int n;
{
  struct voicecontext *p;
  struct voicecontext *q;
  int i,j;

  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;
  };
/* check that key signature mapping is set if global
 * key signature set.                             */
  if (p->keyset == 0 && global.keyset)
    {
    p->keyset = 1;
    for (i=0; i<7; i++) {
      p->basemap[i] = global.basemap[i];
      p->basemul[i] = global.basemul[i];
      for (j=0;j<10;j++) {
        p->workmap[i][j] = global.workmap[i][j];
        p->workmul[i][j] = global.workmul[i][j];
        }
      };
    }
  mtime_num = p->active_meter_num; /* [SS] 2012-11-08 */
  mtime_denom = p->active_meter_denom; /* [SS] 2012-11-08 */
  return(p);
}

void dump_voicecontexts() {
/* called while debugging */
  struct voicecontext *p;
  struct voicecontext *q;

  p = head;
  printf("dump_voicecontexts()\n");
  while (p != NULL) {
    printf("num %d index %d gchords %d words %d drums %d drone %d tosplit %d fromsplit %d\n",
 p->voiceno,p->indexno,p->hasgchords,p->haswords,p->hasdrums,p->hasdrone,p->tosplitno,p->fromsplitno);
    q = p->next;
    p = q;
  };
}


void dump_trackdescriptor() {
 int i;
 for (i=0;i<ntracks;i++) {
    printf("%d %d %d\n",i,trackdescriptor[i].tracktype,trackdescriptor[i].voicenum);
 }
}


void setup_trackstructure () {
  struct voicecontext *p;
  struct voicecontext *q;


  trackdescriptor[0].tracktype=NOTES;
  trackdescriptor[0].voicenum=1;

  p = head;
  ntracks = 1;
  while (p != NULL) {
    if (verbose) {
    printf("num %d index %d gchords %d words %d drums %d drone %d tosplit %d fromsplit %d\n",
 p->voiceno,p->indexno,p->hasgchords,p->haswords,p->hasdrums,p->hasdrone,p->tosplitno,p->fromsplitno);}
    trackdescriptor[ntracks].tracktype = NOTES;
    trackdescriptor[ntracks].voicenum = p->indexno;
    if (p->haswords) {
       if (!separate_tracks_for_words) {
       trackdescriptor[ntracks].tracktype = NOTEWORDS;
       trackdescriptor[ntracks].voicenum = p->indexno;
       } else {
       ntracks++;
       trackdescriptor[ntracks].tracktype = WORDS;
       trackdescriptor[ntracks].voicenum = trackdescriptor[ntracks-1].voicenum;
       }
    }
    if (p->hasgchords) {
       ntracks++;
       trackdescriptor[ntracks].tracktype = GCHORDS;
       trackdescriptor[ntracks].voicenum = p->indexno;
       }
    if (p->hasdrums) {
        ntracks++;  
        trackdescriptor[ntracks].tracktype = DRUMS;
        trackdescriptor[ntracks].voicenum = p->indexno;
      };
    if (p->hasdrone) {
        ntracks++;  
        trackdescriptor[ntracks].tracktype = DRONE;
        trackdescriptor[ntracks].voicenum = p->indexno;
      };
    ntracks++;
    q = p->next;
    p = q;
    }

/* does the tune need any gchord, drum, drone or word track */
    if ((voicesused == 0) && (!karaoke) && (gchordvoice == 0) && 
        (drumvoice == 0) && (dronevoice==0)) {
      ntracks = 1;
      } 

/*dump_trackdescriptor();*/
}





static void clearvoicecontexts()
/* free up all the memory allocated to voices */
{
  struct voicecontext *p;
  struct voicecontext *q;

  p = head;
  while (p != NULL) {
    q = p->next;
    free(p);
    p = q;
  };
  head = NULL;
}



static int getchordnumber(s)
/* looks through list of known chords for chord name given in s */
char *s;
{
  int i;
  int chordnumber;

  chordnumber = 0;
  i = 1;
  while ((i <= chordsnamed) && (chordnumber == 0)) {
    if (strcmp(s, chordname[i]) == 0) {
      chordnumber = i;
    } else {
      i = i + 1;
    };
  };
  return(chordnumber);
}

static void addchordname(s, len, notes)
/* adds chord name and note set to list of known chords */
char *s;
int notes[];
int len;
{
  int i, j, done;

  if (strlen(s) > 7) {
    event_error("Chord name cannot exceed 7 characters");
    return;
  };  
  if (len > 10) {
    event_error("Named chord cannot have more than 10 notes");
    return;
  };
  i = 0;
  done = 0;
  while ((i<chordsnamed) && (!done)) {
    if (strcmp(s, chordname[i]) == 0) {
      /* change chord */
      chordlen[i] = len;
      for (j=0; j<len; j++) {
        chordnotes[i][j] = notes[j];
      };
      done = 1;
    } else {
      i = i + 1;
    };
  };
  if (!done) {
    if (chordsnamed >= MAXCHORDNAMES-1) {
      event_error("Too many Guitar Chord Names used");
    } else {
      chordsnamed = chordsnamed + 1;
      strcpy(chordname[chordsnamed], s);
      chordlen[chordsnamed] = len;
      for (j=0; j<len; j++) {
        chordnotes[chordsnamed][j] = notes[j];
      };
    };
  };
}

static void setup_chordnames()
/* set up named guitar chords */
{
  static int list_Maj[3] = {0, 4, 7};
  static int list_m[3] = {0, 3, 7};
  static int list_7[4] = {0, 4, 7, 10};
  static int list_m7[4] = {0, 3, 7, 10};
  static int list_m7b5[4] = {0, 3, 6, 10};
  static int list_maj7[4] = {0, 4, 7, 11};
  static int list_M7[4] = {0, 4, 7, 11};
  static int list_6[4] = {0, 4, 7, 9};
  static int list_m6[4] = {0, 3, 7, 9};
  static int list_aug[3] = {0, 4, 8};
  static int list_plus[3] = {0, 4, 8};
  static int list_aug7[4] = {0, 4, 8, 10};
  static int list_dim[3] = {0, 3, 6};
  static int list_dim7[4] = {0, 3, 6, 9};
  static int list_9[5] = {0, 4, 7, 10, 2};
  static int list_m9[5] = {0, 3, 7, 10, 2};
  static int list_maj9[5] = {0, 4, 7, 11, 2};
  static int list_M9[5] = {0, 4, 7, 11, 2};
  static int list_11[6] = {0, 4, 7, 10, 2, 5};
  static int list_dim9[5] = {0, 4, 7, 10, 13};
  static int list_sus[3] = {0, 5, 7};
  static int list_sus9[3] = {0, 2, 7};
  static int list_7sus4[4] = {0, 5, 7, 10};
  static int list_7sus9[4] = {0, 2, 7, 10};
  static int list_5[2] = {0, 7};
  
  addchordname("", 3, list_Maj);
  addchordname("m", 3, list_m);
  addchordname("7", 4, list_7);
  addchordname("m7", 4, list_m7);
  addchordname("m7b5", 4, list_m7b5);
  addchordname("maj7", 4, list_maj7);
  addchordname("M7", 4, list_M7);
  addchordname("6", 4, list_6);
  addchordname("m6", 4, list_m6);
  addchordname("aug", 3, list_aug);
  addchordname("+", 3, list_plus);
  addchordname("aug7", 4, list_aug7);
  addchordname("dim", 3, list_dim);
  addchordname("dim7", 4, list_dim7);
  addchordname("9", 5, list_9);
  addchordname("m9", 5, list_m9);
  addchordname("maj9", 5, list_maj9);
  addchordname("M9", 5, list_M9);
  addchordname("11", 6, list_11);
  addchordname("dim9", 5, list_dim9);
  addchordname("sus", 3, list_sus);
  addchordname("sus9", 3, list_sus9);
  addchordname("7sus4", 4, list_7sus4);
  addchordname("7sus9", 4, list_7sus9);
  addchordname("5", 2, list_5);
}

void event_init(argc, argv, filename)
/* this routine is called first by parseabc.c */
char* argv[];
char **filename;
{
  int j;
  int arg,m,n;
  float afreq,semitone_shift; /* [SS] 2012-04-01 */
  double log10();

  /* look for code checking option */
  if (getarg("-c", argc, argv) != -1) {
    check = 1;
  } else {
    check = 0;
  };
  /* look for filename-from-tune-titles option */
  namelimit = 252;
  titlenames = 0;
  if (getarg("-t", argc, argv) != -1) {
    titlenames = 1;
    namelimit = 8;
  };
  /* look for verbose option */
  arg = getarg("-v", argc, argv);
  if (arg != -1) {  /* [SS] 2011-08-26 */
    if (argc > arg) {
      n = sscanf(argv[arg],"%d",&m);
      if (n > 0) verbose = m; }
      else verbose = 1; /* arg != -1 but arg == argc */
    } else {  /* arg =  -1 */
    verbose = 0;
  };
  if (getarg("-ver",argc, argv) != -1) {
     printf("%s\n",VERSION);
     exit(0);
  }
/* look for "no forte no piano" option */
  if (getarg("-NFNP", argc, argv) != -1) {
    nofnop = 1;
  } else {
    nofnop = 0;
  }

  if (getarg("-NFER",argc, argv) != -1) {
     ignore_fermata = 1;
  } else {
     ignore_fermata = 0;
    }

  if (getarg("-NGRA",argc, argv) != -1) {
     ignore_gracenotes = 1;
  } else {
     ignore_gracenotes = 0;
    }

  
  if (getarg("-NCOM", argc, argv) != -1) {
    nocom = 1;
  } else {
    nocom = 0;
  }

  if (getarg("-STFW",argc,argv) != -1) {
    separate_tracks_for_words = 1;
  } else {
    separate_tracks_for_words = 0;
    }

  if (getarg("-HARP",argc,argv) != -1) {  /* [JS] 2011-04-29 */
    harpmode = 1;
  } else {
    harpmode = 0;
    }

  if (getarg("-EA",argc,argv) != -1) { /* [SS] 2011-07-18 */
    easyabcmode = 1;
  } else {
    easyabcmode = 0;
  }

  arg = getarg("-BF",argc,argv);
  if (arg != -1)  {  /* [SS] 2011-08-26 */
    barflymode = 1;
    if (argc > arg) {
      n = sscanf(argv[arg],"%d",&m);
      if (n > 0) stressmodel = m;
      } else stressmodel = 2;
    } else {
    barflymode = 0;
    stressmodel = 0;
  }

  arg = getarg("-TT",argc,argv); /* [SS] 2012-04-01 */
  if (arg != -1) {
    n =0;
    if (argc > arg) {
       n = sscanf(argv[arg],"%f",&afreq);
       }
    if (n < 1) {printf("expecting float between 415.30 and 466.16 after -TT\n");
               } else {
               retuning = 1;
               semitone_shift = 12.0 * log10(afreq/440.0f)/log10(2.0f);
               printf("afreq = %f semitone_shift = %f\n",afreq,semitone_shift);         
               if (semitone_shift >= 1.001) {printf("frequency %f must be less than 466.16\n",afreq);
                   retuning = 0;
                  }
               if (semitone_shift <= -1.015) {printf("frequency %f must be greater than 415.0\n",afreq);
                  retuning = 0;
                  }             
               if (retuning) {bend = (int) (8192.0 * semitone_shift) + 8192;
                              if (bend > 16383) bend=16383;
                              if (bend < 0) bend = 0;
                              printf("bend = %d\n",bend);
                              }
               }
    } 

  if (getarg("-OCC",argc,argv) != -1) oldchordconvention=1;

  maxnotes = 500;
  /* allocate space for notes */
  pitch = checkmalloc(maxnotes*sizeof(int));
  num = checkmalloc(maxnotes*sizeof(int));
  denom = checkmalloc(maxnotes*sizeof(int));
  stressvelocity = checkmalloc(maxnotes*sizeof(int)); /* [SS] 2011-08-17 */
  bentpitch = checkmalloc(maxnotes*sizeof(int));
  decotype  = checkmalloc(maxnotes*sizeof(int)); /* [SS] 2012-06-29 */
  feature = (featuretype*) checkmalloc(maxnotes*sizeof(featuretype));
  pitchline = checkmalloc(maxnotes*sizeof(int));
  for (j=0; j<maxnotes; j++)           /* [SS] 2012-11-25 */
       bentpitch[j] = decotype[j] = 0; /* [SS] 2012-11-25 */
  for (j=0;j<DECSIZE;j++)  dummydecorator[j] = 0;

  /* and for text */
  atext = (char**) checkmalloc(maxtexts*sizeof(char*));
  words = (char**) checkmalloc(maxwords*sizeof(char*));
  if ((getarg("-h", argc, argv) != -1) || (argc < 2)) {
    printf("abc2midi version %s\n",VERSION);
    printf("Usage : abc2midi <abc file> [reference number] [-c] [-v] ");
    printf("[-o filename]\n");
    printf("        [-t] [-n <value>] [-RS] [-NFNP] [-NCOM] [-NFER] [-NGRA] [-HARP]\n");
    printf("        [reference number] selects a tune\n");
    printf("        -c  selects checking only\n");
    printf("        -v  selects verbose option\n");
    printf("        -ver prints version number and exits\n");
    printf("        -o <filename>  selects output filename\n");
    printf("        -t selects filenames derived from tune titles\n");
    printf("        -n <limit> set limit for length of filename stem\n");
    printf("        -RS use 3:1 instead of 2:1 for broken rhythms\n");
    printf("        -quiet suppress some common warnings\n");
    printf("        -Q default tempo (quarter notes/minute)\n");
    printf("        -NFNP don't process !p! or !f!-like fields\n");
    printf("        -NCOM suppress comments in output MIDI file\n");
    printf("        -NFER ignore all fermata markings\n");
    printf("        -NGRA ignore grace notes\n");
    printf("        -STFW separate tracks for words (lyrics)\n");
    printf("        -HARP ornaments=roll for harpist (same pitch)\n"); /* [JS] 2011-04-29 */
    printf("        -BF Barfly mode: invokes a stress model if possible\n");
    printf("        -OCC old chord convention (eg. +CE+)\n");
    printf("        -TT tune to A =  <frequency>\n");
    printf("        -CSM <filename> load custom stress models from file\n");
    printf(" The default action is to write a MIDI file for each abc tune\n");
    printf(" with the filename <stem>N.mid, where <stem> is the filestem\n");
    printf(" of the abc file and N is the tune reference number. If the -o\n");
    printf(" option is used, only one file is written. This is the tune\n");
    printf(" specified by the reference number or, if no reference number\n");
    printf(" is given, the first tune in the file.\n");
    exit(0);
  } else {
    xmatch = 0;
    if ((argc >= 3) && (isdigit(*argv[2]))) {
      xmatch = readnumf(argv[2]);
    };
    *filename = argv[1];
/*    outbase = addstring(argv[1]); [RM] 2010-11-21 
    for (j = 0; j< (int) strlen(outbase); j++) {
      if (outbase[j] == '.') outbase[j] = '\0';
    };
  };
*/
outbase = addstring(argv[1]); /* [RM] 2010-11-21 */
 for (j = (int) strlen(outbase); j>0 ; j--)  {
    if (outbase[j] == '.') {
     outbase[j] = '\0';
     break;
    }
  };
 }

  /* look for filename stem limit */
  j = getarg("-n", argc, argv);
  if (j != -1) {
    if (argc >= j+1) {
      namelimit = 0;
      sscanf(argv[j], "%d", &namelimit);
      if ((namelimit < 3) || (namelimit > 252)) {
        event_fatal_error("filename stem limit must be in the range 3 - 252");
      };
    } else {
      event_error("No number given, ignoring -n option");
    };
  };
  /* look for default tempo */
  j = getarg("-Q", argc,argv);
  if (j != -1) {
    if (argc >= j+1) {
      sscanf(argv[j], "%d", &default_tempo);
      if (default_tempo < 3) {
        event_fatal_error("Q parameter is too small\nEnter -Q 240 not -Q 1/4=240");
       };
    } else {
      event_error("No number given, ignoring -Q option");
    };
  };
       
  /* look for user-supplied output filename */
  j = getarg("-o", argc, argv);
  if (j != -1) {
    if (argc >= j+1) {
      outname = addstring(argv[j]);
      userfilename = 1;
      if (xmatch == 0) {
        xmatch = -1;
      };
      if (titlenames == 1) {
        event_warning("-o option over-rides -t option");
        titlenames = 0;
      };
    } else {
      event_error("No filename given, ignoring -o option");
    };
  }

/* [SS] 2013-04-10 */
  j = getarg("-CSM", argc, argv);
  if (j != -1) {
    if (argc >= j+1) {
      csmfilename = addstring(argv[j]);
      if (*csmfilename == '-') {
        event_error("csmfilename confused with options");
        csmfilename = NULL;
       }
  } else {
     event_error("Filename required after -CSM option");
     } 
 }

  ratio_standard = getarg("-RS", argc, argv);
  quiet  = getarg("-quiet", argc, argv);
  dotune = 0;
  parseroff();
  setup_chordnames();
  
  if (barflymode) init_stresspat();  /* [SS] 2011-08-18 */
}

void event_text(s)
/* text found in abc file */
char *s;
{
  char msg[200];

  /* to prevent possible buffer overflow in sprintf, which is */
  /* a security risk,  we truncate s */
#ifdef NO_SNPRINTF
  sprintf(msg,  "Ignoring text: %s", s); /* SS 2005-01-09 */
#else
  snprintf(msg, sizeof(msg), "Ignoring text: %s", s); /* AL 2005-01-04 */
#endif
  event_warning(msg);
}

void event_x_reserved(p)
/* reserved character H-Z found in abc file */
char p;
{
  char msg[200];

  sprintf(msg, "Ignoring reserved character %c", p);
  event_warning(msg);
}

void event_abbreviation(symbol, string, container)
/* abbreviation encountered - this is handled within the parser */
char symbol;
char *string;
char container;
{
}

void event_acciaccatura()
{
/* does nothing here but outputs a / in abc2abc */
return;
}


/* support functions for split voices */

static int locate_voice(int start, int indexno)
/* This function finds the beginning of voice:indexno
   in the feature array. It starts looking from j = start
*/
{
int j;
j = start;
while (j < notes) {
   if (feature[j] == VOICE && pitch[j] == indexno) {
       return j;
       }
  j++;
  }
return j;
}

static void sync_voice (struct voicecontext *vv, int sync_to, int ignorecurrentbar)
{
/* The function scans the contents of the feature[] array between,
   the last resync point and sync_to (or end of the feature array)
   depending on the ignorecurrentbar flag. It counts the time
   of all notes and rests in the topvoice voice belonging to
   voice vv and adds rests each time it encounters a bar line,
   to synchronize this voice with its topvoice. It also copies
   all repeat signs and dynamic markings in the topvoice and
   inserts them in the current voice. 

   This function is called by event_split_voice, complete_all_split_voices
   and event_bar (if split voices are present).

   The last_resync_point keeps track of what has been scanned,
   so we only scan the topvoice once.

*/
int j;
char *p;
char command[40];
int maxnotes,begin;
char message[80];
int snum,sdenom;
int insidechord;
int voiceno,indexno;
voiceno = vv->topvoiceno;
indexno = vv->topindexno;
snum = 0;
sdenom = 1;
begin =0; /* to bypass double bar or single bar at beginning */
/* we have already synced to last_resync_point so we continue
   from here.
*/
j = vv->last_resync_point;
if (voiceno != 1 || j>2)
 j = locate_voice(j,indexno);
else j++; /* bypass bar */
if (ignorecurrentbar) maxnotes = sync_to;
else maxnotes = notes-1; /*ignore last voice change */
insidechord = 0;
/*printf("syncing voice %d to %d from %d to %d \n",vv->indexno,indexno,j,maxnotes);*/
while (j<=maxnotes) {
/*  dumpfeat(j,j); */
  switch (feature[j]) {
    case VOICE:
       if (pitch[j] != indexno) 
          j = locate_voice(j,indexno);
          break;
       break;
    case CHORDON:
       insidechord = 1;
       break;
    case CHORDOFF:
    case CHORDOFFEX:
       insidechord = 0;
       break;
    case SINGLE_BAR:
    case DOUBLE_BAR:
    case BAR_REP:
    case REP_BAR:
    case DOUBLE_REP:
       if (snum>0) {  /* bypass REST if no notes processed */
         addfeature(REST,0,snum,sdenom);
         /*printf("  added %d/%d to voice %d\n",snum,sdenom,vv->indexno);*/
         snum = 0;
         sdenom =1;
         }
       addfeature(feature[j], 0, 0, denom[j]); /* copy feature */
       break;
    case PLAY_ON_REP:
        if (feature[j-1] == SINGLE_BAR || feature[j-1] == REP_BAR
           || feature[j-1] == VOICE) 
              addfeature(feature[j],0,0,denom[j]);
        else {
            sprintf(message,"expecting SINGLE_BAR or REP_BAR preceding"
            " PLAY_ON_REP instead found %s at %d\n",featname[feature[j-1]],j-1);
            event_error(message);
            }
        break;
    case DYNAMIC:
       p = atext[pitch[j]];
       skipspace(&p);
       readstr(command, &p, 40);
       if (strcmp(command, "program") == 0) {
          textfeature(DYNAMIC, atext[pitch[j]]);
         }
       break;
    case CHANNEL:
       addfeature(feature[j], pitch[j], 0, 0); /* copy feature */
       break;

    case TIME:
       addfeature(feature[j], pitch[j], num[j], denom[j]); /* copy feature */
       break; /* [SS] 2008-07-17 */

    case SETTRIM:
       addfeature(feature[j], pitch[j], num[j], denom[j]); /* copy feature */
       break; /* [SS] 2008-08-12 */

    case GRACEON:	/*[SS] 2012-03-08 */
       gracenotes = 1;
       break;

    case GRACEOFF:      /*[SS] 2012-03-08 */
       gracenotes = 0;
       break;

    case NOTE:
    case TNOTE:
    case REST:
       /*if (insidechord < 2) addfract(&snum,&sdenom,num[j],denom[j]);*/
       if (insidechord < 2 && !gracenotes) addfract(&snum,&sdenom,num[j],denom[j]); /* [SS] 2012-03-08 */
       if (insidechord) insidechord++;
       begin = 1;
       break;
    default:
       break;
   }
   j++;
  }
/* There are no more indexno notes between maxnotes and notes-1,
   so set sync point at the end.
*/
vv->last_resync_point = notes-1;
}


int search_backwards_for_last_bar_line (int from)
{
int found,j;
found = 0;
j = from;
while (!found && j>0)
  {
  if (feature[j] ==  SINGLE_BAR || 
      feature[j] ==  DOUBLE_BAR ||
      feature[j] ==  BAR_REP    || 
      feature[j] ==  REP_BAR    ||
      feature[j] ==  PLAY_ON_REP ||
      feature[j] ==  DOUBLE_REP)  {found = 1; break;}
      j--;
  }
return j;
}



/* When a split voice is encountered for the first time, we
   must create a new voice and insert the proper delay (rests)
   so that it remains in sync with the starting voice. If
   the split voice already exists, we still sync it to the
   source voice (there may have been intervening bars with
   no splits) by adding rests. 
*/

int sync_to;

void event_split_voice()
{
int splitno;
int voiceno,indexno;
int topvoiceno,topindexno;
int program;
int octaveshift;
int default_length; /* [SS] 2010-08-28 */
int abasemap[7],abasemul[7]; /* active basemap  [SS] 2013-10-30*/
int i;
if (!voicesused) insertfeature(VOICE,1,0,0,v1index+1); /* [SS] 2009-12-21 */
voicesused = 1; /* multivoice file */
splitno = v->tosplitno;
program = 0;

/* a voice split in bar is just like a bar line */
zerobar();
v->lastbarloc = notes;  /* in case we need to change it */

voiceno = v->voiceno;
indexno = v->indexno;
topvoiceno = v->topvoiceno;
topindexno = v->topindexno;
octaveshift = v->octaveshift;
default_length = v->default_length; 
if (topvoiceno == voiceno) sync_to = search_backwards_for_last_bar_line(notes-1);
addfeature(SINGLE_BAR,0,0,0);


if (splitno == -1) {splitno = 32+numsplits++;
                   v->tosplitno = splitno;
                   }
/* save basemap and basemul in case it was just changed. We
   need to send it to the split voice. [SS] 2013-10-30
*/
for (i=0;i<7;i++) {
     abasemap[i] = v->basemap[i];
     abasemul[i] = v->basemul[i];
     }

v = getvoicecontext(splitno);
/* propagate the active basemap to the split voice */
for (i=0;i<7;i++) {
     v->basemap[i] = abasemap[i];
     v->basemul[i] = abasemul[i];
     }
copymap(v);
/* end of [SS] 2013-10-30 patch */

splitdepth++;
addfeature(VOICE, v->indexno, 0, 0);
if (v->fromsplitno == -1) {
  v->fromsplitno = voiceno;
  v->topvoiceno = topvoiceno;
  v->topindexno = topindexno;
  v->octaveshift = octaveshift;
  v->default_length = default_length; /* [SS] 2010-08-28 */
 }
dependent_voice[v->indexno] = 1;
/* when syncing the split voice we want to be sure that
   we do not include the notes in the last bar in the source
   voice the notes in the split voice take their place.
*/
sync_voice (v,sync_to,1);
}




void recurse_back_to_original_voice ()
{
int previous_voice;
previous_voice = v->fromsplitno;
while (previous_voice >-1 && splitdepth > 0) {
  v = getvoicecontext(previous_voice);
  previous_voice = v->fromsplitno;
  splitdepth--;
  }
addfeature(VOICE, v->indexno, 0, 0);
copymap(v);
}

/* This function is not used any more [SS] 2013-12-02 */
void recurse_back_and_change_bar (int type)
{
int previous_voice;
previous_voice = v->fromsplitno;
while (previous_voice >-1 && splitdepth > 0) {
  v = getvoicecontext(previous_voice);
/* [SS] 2013-12-1 */
/*  if (v->lastbarloc > -1) replacefeature(type, 0,0,0, v->lastbarloc);*/
  previous_voice = v->fromsplitno;
  splitdepth--;
  }
addfeature(VOICE, v->indexno, 0, 0);
copymap(v);
/* [SS] 2013-12-1 */
if (v->lastbarloc > -1) replacefeature(type, 0,0,0, v->lastbarloc);
}


static void
complete_all_split_voices ()
{
int splitno;
struct voicecontext *p;
int voiceno,indexno;

v = head;
while (v != NULL) {
    splitno = v->tosplitno;
    if (splitno > -1) {
      voiceno = v->voiceno;
      indexno = v->indexno;
      p = getvoicecontext(splitno);
      addfeature(VOICE, p->indexno, 0, 0);
      sync_voice (p,0,0);
      /* complete fraction of bar */
      if(bar_num >0) addfeature(REST, 0, 4*bar_num, bar_denom);
      }
    v = v->next;
  };
}

/* end of code for split voices */


void event_tex(s)
/* TeX command found - ignore it */
char *s;
{
}

void event_fatal_error(s)
/* print error message and halt */
char *s;
{
  event_error(s);
  exit(1);
}

void event_error(s)
/* generic error handler */
char *s;
{
#ifdef NOFTELL
  extern int nullpass;

  if (nullpass != 1) {
    printf("Error in line %d : %s\n", lineno, s);
  };
#else
  printf("Error in line %d : %s\n", lineno, s);
#endif
}

void event_warning(s)
/* generic warning handler - for flagging possible errors */
char *s;
{
#ifdef NOFTELL
  extern int nullpass;

  if (nullpass != 1) {
    printf("Warning in line %d : %s\n", lineno, s);
  };
#else
  printf("Warning in line %d : %s\n", lineno, s);
#endif
}

static int autoextend(maxnotes)
/* increase the number of abc elements the program can cope with */
int maxnotes;
{
  int newlimit;
  int *ptr,*ptr2,*ptr3, *ptr4;
  featuretype *fptr;
  int i;

  if (verbose > 2) {
    event_warning("Extending note capacity");
  };
  newlimit = maxnotes*2;
  fptr = (featuretype*) checkmalloc(newlimit*sizeof(featuretype));
  for(i=0;i<maxnotes;i++){
    fptr[i] = feature[i];
  };
  free(feature);
  feature = fptr;
  ptr = checkmalloc(newlimit*sizeof(int));
  ptr2 = checkmalloc(newlimit*sizeof(int));
  ptr3 = checkmalloc(newlimit*sizeof(int));
  ptr4 = checkmalloc(newlimit*sizeof(int)); /* [SS] 2012-06-29 */
  for (i=0; i<newlimit; i++) {
      ptr3[i] = 0;
      ptr4[i] =0;
      }      
  for(i=0;i<maxnotes;i++){
    ptr[i] = pitch[i];
    ptr2[i] = pitchline[i];
    ptr3[i] = bentpitch[i];
    ptr4[i] = decotype[i];
  };
  free(pitch);
  free(pitchline);
  free(bentpitch);
  free(decotype);
  pitch = ptr;
  pitchline = ptr2;
  bentpitch = ptr3;
  decotype = ptr4; /* [SS] 2012-06-29 */
  ptr = checkmalloc(newlimit*sizeof(int));
  for(i=0;i<maxnotes;i++){
    ptr[i] = num[i];
  };
  free(num);
  num = ptr;

  ptr = checkmalloc(newlimit*sizeof(int));
  for(i=0;i<maxnotes;i++){
    ptr[i] = denom[i];
  };
  free(denom);
  denom = ptr;

/* [SS] 2011-08-17 */
  ptr = checkmalloc(newlimit*sizeof(int));
  for(i=0;i<maxnotes;i++){
    ptr[i] = stressvelocity[i];
  };
  free(stressvelocity);
  stressvelocity = ptr;
  return(newlimit);
}

static int textextend(maxstrings, stringarray)
/* resize an array of pointers to strings */
/* used with arrays words and atext */
int maxstrings;
char*** stringarray;
{
  int i, newlimit;
  char** ptr;

  newlimit = maxstrings*2;
  if (verbose > 2) {
    event_warning("Extending text capacity");
  };
  ptr = (char**) checkmalloc(newlimit*sizeof(char*));
  for(i=0;i<maxstrings;i++){
    ptr[i] = (*stringarray)[i];
  };
  free(*stringarray);
  *stringarray = ptr;
  return(newlimit);
}

static void addfeature(f, p, n, d)
/* place feature in internal table */
int f, p, n, d;
{
  feature[notes] = f;
  pitch[notes] = p;
  num[notes] = n;
  denom[notes] = d;
  if ((f == NOTE) || (f == REST) || (f == CHORDOFF)) {
    reduce(&num[notes], &denom[notes]);
  };
  notes = notes + 1;
  if (notes >= maxnotes) {
    maxnotes = autoextend(maxnotes);
  };
}

static void replacefeature(f, p, n, d, loc)
int f, p, n, d, loc;
{
  feature[loc] = f;
  pitch[loc] = p;
  num[loc] = n;
  denom[loc] = d;
}


void insertfeature(f, p, n, d, loc)
/* insert feature in internal table */
int f,p,n,d,loc;
{ int i;
  notes = notes + 1;
  if (notes >= maxnotes) {
    maxnotes = autoextend(maxnotes);
  };
  for (i=notes;i>loc;i--) {
    feature[i] = feature[i-1];
    pitch[i] = pitch[i-1];
    pitchline[i] = pitchline[i-1];
    bentpitch[i] = bentpitch[i-1];
    decotype[i] = decotype[i-1]; /* [SS] 2012-06-29 */
    num[i] = num[i-1];
    denom[i] = denom[i-1];
    };
  feature[loc] = f;
  pitch[loc]   = p;
  num[i]       = n;
  denom[i]     = d;
  pitchline[i] = 0;
  bentpitch[i] = 0;
  decotype[i] = 0;
}

static void removefeature(loc)
int loc;
{
  int i;
  for (i=loc;i<notes;i++)
    {
    feature[i] = feature[i+1];
    pitch[i]   = pitch[i+1];
    num[i]     = num[i+1];
    denom[i]   = denom[i+1];
    pitchline[i] = pitchline[i+1];
    bentpitch[i] = bentpitch[i+1];
    decotype[i] = decotype[i+1]; /* [SS] 2012-06-29 */
    }
  notes--;
}

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(int type, char *s)
/* called while parsing abc - stores an item which requires an */
/* associared string */
{
  atext[ntexts] = addstring(s);
  addfeature(type, ntexts, 0, 0);
  ntexts = ntexts + 1;
  if (ntexts >= maxtexts) {
    maxtexts = textextend(maxtexts, &atext);
  };
}

void event_comment(s)
/* comment found in abc */
char *s;
{
  if (nocom) return;
  if (dotune) {
    if (pastheader) {
      textfeature(TEXT, s);
    } else {
      textfeature(TEXT, s);
    };
  };
}



void event_specific(package, s)
/* package-specific command found i.e. %%NAME */
/* only %%MIDI commands are actually handled */
char *package, *s;
{
  char msg[200], command[40];
  char *p;
  int done;

  if (started_parsing == 0) {
        event_specific_in_header(package,s);
        return;
        }
 
  if (strcmp(package, "MIDI") == 0) {
    int ch;
    int trans, rtrans;

    p = s;
    done = 0;
    skipspace(&p);
    readstr(command, &p, 40);
    if (strcmp(command, "channel") == 0) {
      skipspace(&p);
      ch = readnump(&p) - 1;
      if (v != NULL) {
        if (ch == 9) v->drumchannel = 1;
        else v->drumchannel = 0;
        }
      addfeature(CHANNEL, ch, 0, 0);
      done = 1;
    };
    trans = strcmp(command, "transpose");
    rtrans = strcmp(command, "rtranspose");
    if ((trans == 0) || (rtrans == 0)) {
      int neg, val;

      skipspace(&p);
      neg = 0;
      if (*p == '+') p = p + 1;
      if (*p == '-') {
        p = p + 1;
        neg = 1;
      };
      skipspace(&p);
      val = readnump(&p);
      if (neg) val = - val;

        if (trans == 0) {
          addfeature(GTRANSPOSE, val, 0, 0);
        } else {
          addfeature(RTRANSPOSE, val, 0, 0);
        };
      done = 1;
    };
    if (strcmp(command, "C") == 0) {
      int val;

      skipspace(&p);
      val = readnump(&p);
      middle_c = val;
      done = 1;
    };
    if (strcmp(command, "nobarlines") == 0) {
      retain_accidentals = 0;
      done = 1;
    };
    if (strcmp(command, "barlines") == 0) {
      retain_accidentals = 1;
      done = 1;
    };
    if (strcmp(command, "fermatafixed") == 0) {
      fermata_fixed = 1;
      done = 1;
    };
    if (strcmp(command, "fermataproportional") == 0) {
      fermata_fixed = 0;
      done = 1;
    };

    /* [SS] 2014-01-12 */
    if (strcmp(command, "tuningsystem") == 0) {
      skipspace(&p);
      if (strcmp(p,"comma53") == 0) {
        printf("%s\n",p);
        comma53 = 1;
        init_p48toc53();
        done = 1;
#ifdef MAKAM
        fc53 = fopen("abcmid.txt","w");
#endif
        }
    };

    if (strcmp(command, "ratio") == 0) {
      int a, b;

      skipspace(&p);
      b = readnump(&p);
      skipspace(&p);
      a = readnump(&p);
      if ((a > 0) && (b > 0)) {
        ratio_a = a;
        ratio_b = b;
        if (ratio_a + ratio_b % 2 == 1) {
          ratio_a = 2 * a;
          ratio_b = 2 * b;
        };
      } else {
        event_error("Invalid ratio");
      };
      done = 1;
    };

    if (strcmp(command, "grace") == 0) {
      int a, b;
      char msg[200];

      skipspace(&p);
      a = readnump(&p);
      if (*p != '/') {
        event_error("Need / in MIDI grace command");
      } else {
        p = p + 1;
      };
      b = readnump(&p);
      if ((a < 1) || (b < 1) || (a >= b)) {
        sprintf(msg, "%d/%d is not a suitable fraction", a, b);
        event_error(msg);
      } else {
        if (pastheader) {
          addfeature(SETGRACE, 1, a, b);
        } else {
          gfact_num = a;
          gfact_denom = b;
        };
      };
      done = 1;
    };

    if(strcmp(command,"gracedivider") == 0) {
      int b;
      char msg[200];
      skipspace(&p);
      b = -1;
      b = readnump(&p);
     if (b < 2)
       {
       sprintf(msg, "a number 2 or larger should follow MIDI gracedivider");
      event_error(msg);
      }
      if (pastheader)
         addfeature(SETGRACE, 0, 1, b);
      else {gfact_denom = b; gfact_method = 0;}
      done = 1;
    }


    if (strcmp(command, "trim") == 0) {
      int a, b;
      skipspace(&p);
      a = readnump(&p);
      if (*p != '/') {
        event_error("Need / in MIDI trim command (eg trim 1/4)");
      } else {
        p = p + 1;
        b = readnump(&p);
       if (v != NULL) {
        addfeature(SETTRIM, 1, 4*a, b*v->default_length);
        } else {
        if (global.default_length == -1) 
          event_error("Need to define L: before trim command: trim command ignored.");
        else 
          addfeature(SETTRIM,1,4*a, b*global.default_length);
       };
      };
      done = 1;
    };

    if (strcmp(command, "gchordon") == 0) {
      addfeature(GCHORDON, 0, 0, 0);
      done = 1;
    };
    if (strcmp(command, "gchordoff") == 0) {
      addfeature(GCHORDOFF, 0, 0, 0);
      done = 1;
    };
    if (strcmp(command, "chordname") == 0) {
      char name[20];
      int i, notes[10]; /* [SS] 2012-01-29 */

      skipspace(&p);
      i = 0;
      while ((i<19) && (*p != ' ') && (*p != '\0')) {
        name[i] = *p;
        p = p + 1;
        i = i + 1;
      };
      name[i] = '\0';
      if (*p != ' ') {
        event_error("Bad format for chordname command");
      } else {
        i = 0;
        while ((i<=10) && (*p == ' ')) { /* [SS] 2012-01-29 */
          skipspace(&p);
          notes[i] = readsnump(&p); 
          i = i + 1;
        };
        addchordname(name, i, notes);
      };
      done = 1;
    };


  if (strcmp(command, "temperamentlinear") == 0) {
      double octave_cents=0.0;
      double fifth_cents=0.0;
      temperament = 1;
      middle_c = 60*SEMISIZE;

      if (sscanf(p," %lf %lf ",&octave_cents,&fifth_cents) == 2) {
        octave_size = (int)(octave_cents*SEMISIZE/100+0.5);
        fifth_size = (int)(fifth_cents*SEMISIZE/100+0.5);
      }
      else {
        event_error("Bad format for lineartemperament command");
      }
      done = 1;
    }

  if (strcmp(command, "temperamentnormal") == 0) {
      temperament = 0;
      event_normal_tone();
      done = 1;
      middle_c = 60;
      }


    if (strcmp(command,"drumon") == 0 && dotune) {  /* [SS] 2010-05-26 */
      addfeature(DRUMON, 0, 0, 0);
      v->hasdrums = 1;
      drumvoice = v->indexno; /* [SS] 2010-02-09 */
      done = 1;
      if (v == NULL) event_error("%%MIDI drumon must occur after the first K: header");
    }
    if (strcmp(command,"drumoff") == 0) {
       addfeature(DRUMOFF, 0, 0, 0);
       done = 1;
    }

    if (strcmp(command,"droneon") == 0 && dotune) {
      addfeature(DRONEON, 0, 0, 0);
      v->hasdrone = 1;
      if ((dronevoice != 0) && (dronevoice != v->indexno)) {
        event_warning("Implementation limit: drones only supported in one voice");
       };
      if (v == NULL) event_error("%%MIDI droneon must occur after the first K: header");
      else {
           dronevoice = v->indexno;
           done = 1;
           }
    }
    if (strcmp(command,"droneoff") == 0) {
       addfeature(DRONEOFF, 0, 0, 0);
       done = 1;
    }

    if (strcmp(command, "deltaloudness") == 0) {
      skipspace(&p);
      velocitychange = readnump(&p);
      done = 1;
      }

	if (strcmp(command, "harpmode") == 0) {  /* [JS] 2011-04-29 */
      skipspace(&p);
      harpmode = readnump(&p);
      done = 1;
    };


    if (done == 0) {
      /* add as a command to be interpreted later */
      textfeature(DYNAMIC, s);
    };
  } else
  {
/* Parse %%abc directive */
      done = 0;
      if (strcmp(package, "abc") == 0)
      {
         p = s;
         skipspace(&p);
/* skip '-' character after abc */          
         p++;
         readstr(command, &p, 40);
/* Parse %%abc-copyright */
         if (strcmp(command, "copyright") == 0)
         {            
            int lnth;
            int n;
            char *ptr;

            done = 1;            
            skipspace(&p);
/* Copy string to parse.
 * Convert \n, \t, \r, \x and \\ in string to C style character constant defaults.
 * (Handles carriage return, linefeed and tab characters and hex codes.)
 */
            lnth = 0;
            ptr = &msg[0];
            while ((*p != '\0') && (lnth < 199))
            { 
               if (*p == '\\')
               {
                  p++;
                  switch (*p)
                  {
                     case 'n':
                        *ptr = '\n';
                        break;
                     case 'r':  
                        *ptr = '\r';
                        break;
                     case 't':  
                        *ptr = '\t';
                        break;
                     case '\\':
                        *ptr = *p;
                        break; 
                     case 'x':
                     case 'X':
                        p++;
                        sscanf (p, "%x", &n);
                        *ptr = n;
                        while ((*p != '\0') && (isxdigit (*p)))                     
                           p++;
                        p--;   
                        break;
                     default:
                        *ptr = *p;
                        break;
                  }
               }
               else
                  *ptr = *p;
               ptr++;
               p++;
              lnth++;          /* Anselm Lingnau 2005-01-04 */
            }
            *ptr = '\0';
            textfeature(COPYRIGHT, msg);
           if (*p != '\0') {   /* Anselm Lingnau 2005-01-04 */
               event_warning("ABC copyright notice abridged");
           }
        }
        else
        {

           /* Changed by Anselm Lingnau, 2005-01-04, for security */
#ifdef NO_SNPRINTF
          if (sprintf(msg, "%%%s%s", package, s) > sizeof(msg)) {
              event_warning("event_specific: comment too long");
          }
#else
          if (snprintf(msg,sizeof(msg), "%%%s%s", package, s) > sizeof(msg)) {
              event_warning("event_specific: comment too long");
          }
#endif
        event_comment(msg);
        }
      }
     if (strcmp(package, "propagate") == 0) {
          p = s;
          p = p + 13;
         printf("propagate-accidentals encountered\n");
         if (strcmp(p,"not") == 0) {
             nopropagate_accidentals = 1;
          } else {nopropagate_accidentals = 0;}
         done = 1;
            }

    if (done == 0)  
    {

/* changed by Anselm Lingnau on 2004-01-04, for security */
#ifdef NO_SNPRINTF
   if (sprintf(msg, "%%%s%s", package, s) > sizeof(msg)) {
      event_warning("event_specific: message too long");
   }
#else
   if (snprintf(msg, sizeof(msg), "%%%s%s", package, s) > sizeof(msg)) {
      event_warning("event_specific: message too long");
   }
#endif

    event_comment(msg);
    }
  }
}



/* global variables that can altered by %%MIDI before tune */
int default_middle_c = 60;
int default_retain_accidentals = 1;
int default_fermata_fixed = 0;
int default_ratio_a = 2;
int default_ratio_b = 4;

void event_specific_in_header(package, s)
/* package-specific command found i.e. %%NAME */
/* only %%MIDI commands are actually handled */
char *package, *s;
{
  char  command[40];
  char *p;
  int done;

  if (strcmp(package, "MIDI") == 0) {

    p = s;
    done = 0;
    skipspace(&p);
    readstr(command, &p, 40);



    if (strcmp(command, "C") == 0) {
      int val;

      skipspace(&p);
      val = readnump(&p);
      default_middle_c = val;
      done = 1;
    };
    if (strcmp(command, "nobarlines") == 0) {
      default_retain_accidentals = 0;
      done = 1;
    };
    if (strcmp(command, "barlines") == 0) {
      default_retain_accidentals = 1;
      done = 1;
    };
    if (strcmp(command, "fermatafixed") == 0) {
      default_fermata_fixed = 1;
      done = 1;
    };
    if (strcmp(command, "fermataproportional") == 0) {
      default_fermata_fixed = 0;
      done = 1;
    };
    if (strcmp(command, "harpmode") == 0) {  /* [JS] 2011-04-29 */
      skipspace(&p);
      harpmode = readnump(&p);
      done = 1;
    };
    if (strcmp(command, "ratio") == 0) {
      int a, b;
      skipspace(&p);
      b = readnump(&p);
      skipspace(&p);
      a = readnump(&p);
      if ((a > 0) && (b > 0)) {
        default_ratio_a = a;
        default_ratio_b = b;
        if (default_ratio_a + default_ratio_b % 2 == 1) {
          default_ratio_a = 2 * a;
          default_ratio_b = 2 * b;
        };
      } else {
        event_error("Invalid ratio");
      };
      done = 1;
    };

    if (strcmp(command, "chordname") == 0) {
      char name[20];
      int i, notes[6];
      skipspace(&p);
      i = 0;
      while ((i<19) && (*p != ' ') && (*p != '\0')) {
        name[i] = *p;
        p = p + 1;
        i = i + 1;
      };
      name[i] = '\0';
      if (*p != ' ') {
        event_error("Bad format for chordname command");
      } else {
        i = 0;
        while ((i<=6) && (*p == ' ')) {
          skipspace(&p);
          notes[i] = readsnump(&p); 
          i = i + 1;
        };
        addchordname(name, i, notes);
      };
      done = 1;
    };


    if (strcmp(command, "deltaloudness") == 0) {
      skipspace(&p);
      velocitychange = readnump(&p);
      done = 1;
      }

    if (done == 0) {
       event_warning("cannot handle this MIDI directive in file header");
    }
  }
}


void event_startinline()
/* start of in-line field in abc music line */
{
}

void event_closeinline()
/* end of in-line field in abc music line */
{
}

void extract_filename(char *f)
/* work out filename stem from tune title */
/* name length cannot exceed namelimit characters */
{
  char buffer[256];
  int i;
  char *p;

  i = 0;
  p = f;
  skipspace(&p);
  /* avoid initial 'The' or 'the' */
  if ((strncmp(p, "The", 3) == 0) || (strncmp(p, "the", 3) == 0)) {
    p = p + 3;
    skipspace(&p);
  };
  while ((*p != '\0') && (i < namelimit)) {
    if (isalnum(*p)) {
      buffer[i] = *p;
      i = i + 1;
    };
    p = p + 1;
  };
  buffer[i] = '\0';
  if (i == 0) {
    strcpy(buffer, "notitle");
    buffer[namelimit] = '\0';
  };
  strcpy(&buffer[strlen(buffer)], ".mid");
  if (outname != NULL) {
    free(outname);
  };
  outname = addstring(buffer);
  got_titlename = 1;
}

void event_field(k, f)
/* Handles R: T: and any other field not handled elsewhere */
/* Added code to handle C: field. */
char k;
char *f;
{
  if (dotune) {
    switch (k) {
    case 'T':
      textfeature(TITLE, f);
      if (titlenames && (!got_titlename)) {
        extract_filename(f);
      };
      break;
    case 'C':
      textfeature(COMPOSER, f);
      break;
    case 'R':
      {
        char* p; 
        p = f;
        strncpy(rhythmdesignator,f,32); /* [SS] 2011-08-19 */
        skipspace(&p);
        if (((strncmp(p, "Hornpipe", 8) == 0) ||
            (strncmp(p, "hornpipe", 8) == 0)) &&
             barflymode ==0) {   /* [SS] 2011-08-19 */
          hornpipe = 1;
        };
      };
      break;
    default:
      {
        char buff[256];
        
        if (strlen(f) < 256) {
          sprintf(buff, "%c:%s", k, f);
          textfeature(TEXT, buff);
        };
      };
    };
  } else {
    if (k == 'T') {
      event_warning("T: outside tune body - possible missing X:");
    };
  };
}

void event_words(p, continuation)
/* handles a w: field in the abc */
char* p;
int continuation;
{

  karaoke = 1;
  if (v == NULL) {
    event_error("missplaced w: field. w: field ignored");
    return;
    }
  v->haswords = 1;
  wordvoice = v->indexno;
  words[wcount] = addstring(p);
  addfeature(WORDLINE, wcount, 0, 0);
  if (continuation == 0) {
    addfeature(WORDSTOP, 0, 0, 0);
  };
  wcount = wcount + 1;
  if (wcount >= maxwords) {
    maxwords = textextend(maxwords, &words);
  };
}

static void checkbreak()
/* check that we are in not in chord, grace notes or tuple */
/* called at voice change */
{
  if (tuplecount != 0) {
    event_error("Previous voice has an unfinished tuple");
    tuplecount = 0;
  };
  if (v->inchord != 0) {
    event_error("Previous voice has incomplete chord");
    event_chordoff(1,1);
  };
  if (v->ingrace != 0) {
    event_error("Previous voice has unfinished grace notes");
    v->ingrace = 0;
  };
}

static void char_out(part, ch)
/* routine for building up part list */
struct vstring* part;
char ch;
{
/*
  if (*out - list >= MAXPARTS) {
    event_error("Expanded part is too large");
  } else {
    **out = ch;
    *out = *out + 1;
    parts = parts + 1;
  };
*/
  addch(ch, part);
  parts = parts + 1;
}

static void read_spec(spec, part)
/* converts a P: field to a list of part labels */
/* e.g. P:A(AB)3(CD)2 becomes P:AABABABCDCD */

/********** This feature is not supported ********[SS] 2004-10-08      */
/* A '+' indicates 'additive' behaviour (a part may include repeats).  */
/* A '-' indicates 'non-additive' behaviour (repeat marks in the music */
/* are ignored and only repeats implied by the part order statement    */
/* are played).  */

char spec[];
struct vstring* part;
{
  char* in;
  int i, j;
  int stackptr;
  char* stack[10];
  char lastch;

  stackptr = 0;
  in = spec;
  while (((*in >= 'A') && (*in <= 'Z')) || (*in == '(') || (*in == '.') ||
         (*in == ')') || (*in == '+') || (*in == '-') || 
         ((*in >= '0') && (*in <= '9'))) {
    if (*in == '.') {
      in = in + 1;
    };
/*    if (*in == '+') {     no longer supported
*      additive = 1;
*      in = in + 1;
*    };
*    if (*in == '-') {
*      additive = 0;
*      in = in + 1;
*    };
*/
    if ((*in >= 'A') && (*in <= 'Z')) {
      char_out(part, *in);
      lastch = *in;
      in = in + 1;
    };
    if (*in == '(') {
      if (stackptr < 10) {
        stack[stackptr] = part->st + strlen(part->st);
        stackptr = stackptr + 1;
      } else {
        event_error("nesting too deep in part specification");
      };
      in = in + 1;
    };
    if (*in == ')') {
      in = in + 1;
      if (stackptr > 0) {
        int repeats;
        char* start;
        char* stop;

        if ((*in >= '0') && (*in <= '9')) {
          repeats = readnump(&in);
        } else {
          repeats = 1;
        };
        stackptr = stackptr - 1;
        start = stack[stackptr];
        stop = part->st + strlen(part->st);
        for (i=1; i<repeats; i++) {
          for (j=0; j<((int) (stop-start)); j++) {
            char_out(part, *(start+j));
          };
        };
      } else {
        event_error("Too many )'s in part specification");
      };
    };
    if ((*in >= '0') && (*in <= '9')) {
      int repeats;

      repeats = readnump(&in);
      if (part->len > 0) {
        for (i = 1; i<repeats; i++) {
          char_out(part, lastch);
        };
      } else {
        event_error("No part to repeat in part specification");
      };
    };
  };
  if (stackptr != 0) {
    event_error("Too many ('s in part specification");
  };
}

void event_part(s)
/* handles a P: field in the abc */
char* s;
{
  char* p;

  if (dotune) {
    p = s;
    skipspace(&p);
    if (pastheader && parts == -1) return; /* [SS] 2014-04-10 */
    if (pastheader) {
      if (((int)*p < 'A') || ((int)*p > 'Z')) {
        event_error("Part must be one of A-Z");
        return;
      };
      if ((headerpartlabel == 1) && (part.st[0] == *p)) {
        /* P: field in header is not a label */
        headerpartlabel = 0;
        /* remove speculative part label */
        feature[part_start[(int)*p - (int)'A']] = NONOTE;
      } else {
        if (part_start[(int)*p - (int)'A'] != -1) {
          event_error("Part defined more than once");
        };
      };
      part_start[(int)*p - (int)'A'] = notes;
      addfeature(PART, (int)*p, 0, 0);
      checkbreak();
      v = getvoicecontext(1);
    } else {
      parts = 0;
      read_spec(p, &part);
      if (parts == 1) {
        /* might be a label not a specificaton */
        headerpartlabel = 1;
      };
    };
  };
}

void event_voice(n, s, vp)
/* handles a V: field in the abc */
int n;
char *s;
struct voice_params *vp;
{
  if (!voicesused && bodystarted) {event_warning("First V: field occurs past body; will combine this body with this voice.");
     bodystarted = 0;
     }
  if (pastheader || XTEN1) {
    voicesused = 1;
    if (pastheader)  checkbreak();


    v = getvoicecontext(n); 
    addfeature(VOICE, v->indexno, 0, 0); 
    
    dependent_voice[v->indexno] = 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;
{
  if (pastheader) {
    v->default_length = n;
  } else {
    global.default_length = n;
  };
}

static void tempounits(t_num, t_denom)
/* interprets Q: once default length is known */
int *t_num, *t_denom;
{
  /* calculate unit for tempo */
  if (tempo_num == 0) {
    *t_num = 1;
    *t_denom = global.default_length;
  } else {
    if (relative_tempo) {
      *t_num = tempo_num;
      *t_denom = tempo_denom*global.default_length;
    } else {
      *t_num = tempo_num;
      *t_denom = tempo_denom;
    };
  };
}

int get_tempo_from_name (s) /* [SS] 2010-12-07 */
char *s;
{
int i,n;
if (s == NULL) return 0; /* [SS] 2010-12-10 */
for (i=0;i<19;i++) {
   if (strcasecmp(s,temponame[i]) == 0)
      {n = temporate[i];
       return n;
      }
  }
return 0;
}


void event_tempo(n, a, b, rel, pre, post)
/* handles a Q: field e.g. Q: a/b = n  or  Q: Ca/b = n */
/* strings before and after are ignored */
int n;
int a, b, rel;
char *pre;
char *post;
{
  int t_num, t_denom;
  int new_div;
  long new_tempo;
  int tempo_l, tempo_h;


  if (n == 0) n = get_tempo_from_name(pre); /* [SS] 2010-12-07 */
  if ((n == 0) || ((a!=0) && (b == 0))) {
       event_error("malformed Q: field ignored");
    } else {
    if (dotune) {
      if (pastheader) {
        tempo_num = a;
        tempo_denom = b;
        relative_tempo = rel;
        tempounits(&t_num, &t_denom);
        new_tempo = (long) 60*1000000*t_denom/(n*4*t_num);
        /* split up into short ints */
        tempo_l = new_tempo & 0xffff;
        tempo_h = new_tempo >> 16;
        new_div = (int) ((float)DIV*(float)new_tempo/(float)tempo + 0.5);
        addfeature(TEMPO, new_div, tempo_h, tempo_l);
      } else {
        Qtempo = n;
        tempo_num = a;
        tempo_denom = b;
        relative_tempo = rel;
      };
    };
  };
}

void event_timesig(n, m, dochecking)
/* handles an M: field  M:n/m */
int n, m, dochecking;
{
  if (dotune) {
    if (pastheader) {
      addfeature(TIME, dochecking, n, m);
      mtime_num = n; /* [SS] 2012-11-03 */
      mtime_denom = m; /* [SS] 2012-11-03 */
      if (v != NULL) {
        v->active_meter_num =  n; /* [SS] 2012-11-08 */
        v->active_meter_denom =  m; /* [SS] 2012-11-08 */
        }
    } else {
      time_num = n;
      time_denom = m;
      mtime_num = n; /* [SS] 2012-11-03 */
      mtime_denom = m; /* [SS] 2012-11-03 */
      if (v != NULL) {
        v->active_meter_num =  n; /* [SS] 2012-11-08 */
        v->active_meter_denom =  m; /* [SS] 2012-11-08 */
        }
      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;

#ifdef INFO_OCTAVE_DISABLED
  return;
#endif
  if (strcmp(key, "octave")==0) {
    num = readsnumf(value);
    event_octave(num,0);
  };
  if (strcmp(key, "MIDI") == 0)
     event_specific(key, value);
}

static void stack_broken(v)
/* store away broken rhythm context on encountering grace notes */
struct voicecontext* v;
{
  v->broken_stack[0] = v->laststart;
  v->broken_stack[1] = v->lastend;
  v->broken_stack[2] = v->thisstart;
  v->broken_stack[3] = v->thisend;
  v->broken_stack[4] = v->brokentype;
  v->broken_stack[5] = v->brokenmult;
  v->broken_stack[6] = v->brokenpending;
  v->laststart = -1;
  v->lastend = -1;
  v->thisstart = -1;
  v->thisend = -1;
  v->brokenpending = -1;
}

static void restore_broken(v)
/* remember any broken rhythm context after grace notes */
struct voicecontext* v;
{
  if (v->brokenpending != -1) {
    event_error("Unresolved broken rhythm in grace notes");
  };
  v->laststart = v->broken_stack[0];
  v->lastend = v->broken_stack[1];
  v->thisstart = v->broken_stack[2];
  v->thisend = v->broken_stack[3];
  v->brokentype = v->broken_stack[4];
  v->brokenmult = v->broken_stack[5];
  v->brokenpending = v->broken_stack[6];
}

void event_graceon()
/* a { in the abc */
{
  if (gracenotes) {
    event_error("Nested grace notes not allowed");
  } else {
    if (v->inchord) {
      event_error("Grace notes not allowed in chord");
    } else {
      gracenotes = 1;
      addfeature(GRACEON, 0, 0, 0);
      v->ingrace = 1;
      stack_broken(v);
    };
  };
}

void event_graceoff()
/* a } in the abc */
{
  if (!gracenotes) {
    event_error("} without matching {");
  } else {
    gracenotes = 0;
    addfeature(GRACEOFF, 0, 0, 0);
    v->ingrace = 0;
    restore_broken(v);
  };
}


void event_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);
    };
  };
}



void event_sluron(t)
/* called when ( is encountered in the abc */
int t;
{
 if (v->inslur) event_warning("Slur within slur");
 else {
      addfeature(SLUR_ON, 0, 0, 0);
      v->inslur = 1;
      }
}

void event_sluroff(t)
/* called when ) is encountered */
int t;
{
if (v->inslur) {
    addfeature(SLUR_OFF, 0, 0, 0);
    v->inslur = 0;
    }
else event_warning("No slur to close");
}


void event_tie()
/* a tie - has been encountered in the abc */
{
if (gracenotes && ignore_gracenotes) return; /* [SS] 2010-01-12 */
if (feature[notes-1] == CHORDOFF ||
    feature[notes-1] == CHORDOFFEX) { /* did a TIE connect with a chord */
       patchup_chordtie(chordstart,notes-1);
      }
  else
  addfeature(TIE, 0, 0, 0);
}

void event_space()
/* space character in the abc is ignored by abc2midi */
{
  /* ignore */
  /* printf("Space event\n"); */
}

void event_lineend(ch, n)
/* called when \ or ! or * or ** is encountered at the end of a line */
char ch;
int n;
{
  /* ignore */
}

void event_broken(type, mult)
/* handles > >> >>> < << <<< in the abc */
int type, mult;
{
  if (v->inchord) {
    event_error("Broken rhythm not allowed in chord");
  } else {
    if (v->ingrace) {
      event_error("Broken rhythm not allowed in grace notes");
    } else {
      if ((hornpipe) && (feature[notes-1] == GT)) {
        /* remove any superfluous hornpiping */
        notes = notes - 1;
      };
      /* addfeature(type, mult, 0, 0); */
      v->brokentype = type;
      v->brokenmult = mult;
      v->brokenpending = 0;
    };
  };
}

void event_tuple(n, q, r)
/* handles triplets (3 and general tuplets (n:q:r in the abc */
int n, q, r;
{
  if (tuplecount > 0) {
    event_error("nested tuples");
  } else {
    if (r == 0) {
      specialtuple = 0;
      tuplecount = n;
    } else {
      specialtuple = 1;
      tuplecount = r;
    };
    if (q != 0) {
      tfact_num = q;
      tfact_denom = n;
    } else {
      if ((n < 2) || (n > 9)) {
        event_error("Only tuples (2 - (9 allowed");
        tfact_num = 1;
        tfact_denom = 1;
        tuplecount = 0;
      } else {
        /* deduce tfact_num using standard abc rules */
        if ((n == 2) || (n == 4) || (n == 8)) tfact_num = 3;
        if ((n == 3) || (n == 6)) tfact_num = 2;
        if ((n == 5) || (n == 7) || (n == 9)) {
          if ((time_num % 3) == 0) {
            tfact_num = 3;
          } else {
            tfact_num = 2;
          };
        };
        tfact_denom = n;
      };
    };
    tnote_num = 0;
    tnote_denom = 0;
  };
}

void event_chord()
/* a + or [ or ]  has been encountered in the abc */
{
  if (v->inchord) {
    event_chordoff(1,1);
  } else {
    event_chordon(dummydecorator);
  };
}

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)
       || (feature[n] == CHORDOFFEX)) /* [SS] 2013-04-20 */ {
    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;
  };
  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 (decorators[FERMATA] && !ignore_fermata) {
    if (fermata_fixed) addfract(&num,&denom,1,1);
    else num = num*2;
  };
  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)
/* multiple bar rest of n/m in the abc */
/* we check for m == 1 in the parser */
int n, m;
{
  int i;
  int decorators[DECSIZE];
  decorators[FERMATA]=0;
/* it is not legal to pass a fermata to a multirest */

  for (i=0; i<n; i++) {
    /* [SS] 2012-11-03 */
    /*event_rest(decorators,time_num*(v->default_length), time_denom,0);*/
    event_rest(decorators,mtime_num*(v->default_length), mtime_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 */
/* and is used here to handle fermatas.                          */
{
  inchordflag = 1; /* [SS] 2012-03-30 */
  apply_fermata_to_chord = chorddecorators[FERMATA]; /* [SS] 2012-03-26 */
  if (v->inchord) {
    event_error("Attempt to nest chords");
  } else {
    chordstart = notes;
    if (easyabcmode) 
         addfeature(META,0,lineno,lineposition); /* [SS] 2011-07-18 */
    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 */
{
  int c_n,c_m;
  inchordflag = 0; /* [SS] 2012-03-30 */
  if (chord_m == 1 && chord_n == 1) {
     c_m = denom[chordstart];
     c_n = num[chordstart];
     }
  else {c_m = chord_m; c_n = chord_n;}

  if (!v->inchord) {
    event_error("Chord already finished");
  } else {
  if (tuplecount > 0) {
    c_n = c_n * tfact_num;
    c_m = c_m * tfact_denom;
    if (tnote_num == 0) {
        tnote_num   = c_n;
        tnote_denom = c_m;
    } else {
    /*   if (tnote_num * c_m != c_n * tnote_denom) {
          if (!specialtuple) {
            event_warning("Different length notes in tuple for chord");
           };
        };
      [SS] 2013-04-24 */
     }
     if ((!gracenotes) && (!v->inchord)) {
        tuplecount = tuplecount - 1;
        };
    };

    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, c_n*4, c_m*v->default_length);
      fix_enclosed_note_lengths(chordstart, notes-1);
      }
      
    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, propagate_accs)
/* This code is used for handling gchords */
/* finds MIDI pitch value for note */
/* if propagate_accs is 1, apply any accidental to all instances of  */
/* that note in the bar. If propagate_accs is 0, accidental does not */
/* apply to other notes */
char note, accidental;
int mult, octave;
int propagate_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 == ' ' && !microtone ) { 
/* if microtone do not propagate accidentals to this
   note.
*/
    acc = v->workmap[noteno][octave+4];
    mul = v->workmul[noteno][octave+4];
  } else {
    if ((retain_accidentals) && (propagate_accs)) {
      v->workmap[noteno][octave+4] = acc;
      v->workmul[noteno][octave+4] = mul;
    };
  };
  if (acc == '^') p = p + mul;
  if (acc == '_') p = p - mul;
  return p + 12*octave + middle_c;
}

static int barepitch(note, accidental, mult, octave) 
/* Computes MIDI pitch ignoring any key signature.
 * Required for drum track
 */
char note, accidental;
int mult, octave;
{
int p,pitch;
int accidental_size = 1;
static const char *anoctave = "cdefgab";
static int scale[7] = {0, 2, 4, 5, 7, 9, 11};
p = (int) ((long) strchr(anoctave, note) - (long) anoctave);
p = scale[p];
if (accidental == '^') p = p + mult*accidental_size;
if (accidental == '_') p = p - mult*accidental_size;
pitch = p + 12*octave + middle_c;
return pitch;
}

static int pitchof_b(note, accidental, mult, octave, propagate_accs,pitchbend)
/* computes MIDI pitch for note. If global temperament is set,
   it will apply a linear temperament and return a
   pitchbend. If propagate_accs is 1, apply any accidental to all
   instances of  that note in the bar. If propagate_accs is 0, 
   accidental does not apply to other notes */
char note, accidental;
int mult, octave;
int propagate_accs;
int *pitchbend;
{
  int p;
  char acc;
  int mul, noteno;
  int pitch4096,pitch,bend;
  int a,b;

  static int scale[7] = {0, 2, 4, 5, 7, 9, 11};
  const int accidental_size = 7*fifth_size - 4*octave_size;
  const int tscale[7] = {
    0,
    2*fifth_size-octave_size,
    4*fifth_size-2*octave_size,
    -1*fifth_size+octave_size,
    fifth_size,
    3*fifth_size-octave_size,
    5*fifth_size-2*octave_size
  };
  static const char *anoctave = "cdefgab";

  acc = accidental;
  mul = mult;
  noteno = (int)note - 'a';

  if (acc == ' ' && !microtone) {
    acc = v->workmap[noteno][octave+4];
    mul = v->workmul[noteno][octave+4];
    a = v->workmic[noteno][octave+4].num;   /* 2014-01-26 */
    b = v->workmic[noteno][octave+4].denom;
    event_microtone(1,a,b);
  } else {
    if ((retain_accidentals) && (propagate_accs)) {
      v->workmap[noteno][octave+4] = acc;
      v->workmul[noteno][octave+4] = mul;
      /* [SS] 2014-01-26 */
      v->workmic[noteno][octave+4].num   = setmicrotone.num;
      v->workmic[noteno][octave+4].denom = setmicrotone.denom;
    };
  };

  p = (int) ((long) strchr(anoctave, note) - (long) anoctave);
  if (temperament) {
    p = tscale[p];
    if (acc == '^') p = p + mul*accidental_size;
    if (acc == '_') p = p - mul*accidental_size;
    pitch4096 =  p + octave*octave_size + middle_c;
    pitch =  (SEMISIZE*128+pitch4096+SEMISIZE/2)/SEMISIZE-128;
    bend =  8192+4096*(pitch4096 - pitch*SEMISIZE)/SEMISIZE;
    bend = bend<0?0:(bend>16383?16383:bend);
   } else {
    p = scale[p];
    if (acc == '^' && !microtone) p = p + mul; /* [SS] 2014-01-20 */
    if (acc == '_' && !microtone) p = p - mul;
    pitch = p + 12*octave + middle_c;
    bend = 8192; /* corresponds to zero bend */
    }
if (!microtone) *pitchbend = bend; /* don't override microtone */
if (comma53) 
#ifdef MAKAM
 if (comma53) fprintf(fc53,"%c%d ",note,octave+4);
#endif
 if (comma53) convert_to_comma53 (acc,  &pitch, pitchbend); 
 microtone = 0; /* [SS] 2014-01-25 */
 setmicrotone.num = 0; /* [SS] 2014-01-25 */
 setmicrotone.denom = 0;
return pitch; 
}



/* [SS] 2014-01-12  comma53 support: start */

int p48toc53[50];

void init_p48toc53 () {
int i,c;
c = 0;
for (i=0; i< 48; i++) {
  p48toc53[i] = c;
/* if black note leave room for extra comma */
  if (i == 4 || i == 12 || i == 24 || i == 32 || i == 40)
     c = c+2;
  else
     c = c+1;
  //printf("%d  ",p48toc53[i]);
  }
}

void convert_to_comma53 (char acc, int *midipitch, int* midibend) 
{
/* The function converts *midipitch, *midibend to the
   closest comma53 pitch values.
*/
float c53factor = 0.22641509;
float eqtempmidi,bendvalue,c53midi;
int p48,octave,c53;
bendvalue = (*midibend - 8192.0)/4096.0;
eqtempmidi = (float) (*midipitch) + bendvalue;
octave = (int) (eqtempmidi / 12.0);
p48 = (int) (eqtempmidi * 4.0) % 48;
c53 = p48toc53[p48] + 53*octave;
/* handle b4 or #5 (eg D4b4 or C4#5) in nameAE */
if (p48 == 4 || p48 == 12 || p48 == 24 || p48 == 32 || p48 == 40)
   if (bendvalue > 1.1 || bendvalue < -0.8) c53++;
#ifdef MAKAM
fprintf(fc53,"%d\n",c53);
#endif

/*printf("p48 = %d\n c53 = %d\n",p48,c53);*/
c53midi = (float) c53 * c53factor;
*midipitch = (int) c53midi;
bendvalue = c53midi - (float) *midipitch;
*midibend = 8192 + (int) (bendvalue * 4096);
}


/* [SS] 2014-01-12 */





static void doroll(note, octave, n, m, pitch)
/* applies a roll to a note */
char note;
int octave, n, m;
int pitch;
{
  char up, down;
  int t;
  int upoct, downoct, pitchup, pitchdown;
  int bend_up,bend_down;
  char *anoctave = "cdefgab";

  upoct = octave;
  downoct = octave;
  t = (int) ((long) strchr(anoctave, note)  - (long) anoctave);
  up = *(anoctave + ((t+1) % 7));
  down = *(anoctave + ((t+6) % 7));
  if (up == 'c') upoct = upoct + 1;
  if (down == 'b') downoct = downoct - 1;
  pitchup = pitchof_b(up, v->basemap[(int)up - 'a'], 1, upoct, 0,&bend_up);
  pitchdown = pitchof_b(down, v->basemap[(int)down - 'a'], 1, downoct, 0,&bend_down);
  bentpitch[notes] = active_pitchbend;
  addfeature(NOTE, pitch, n*4, m*(v->default_length)*5);
  marknotestart();
  bentpitch[notes] = bend_up;
  addfeature(NOTE, pitchup, n*4, m*(v->default_length)*5);
  bentpitch[notes] = active_pitchbend;
  addfeature(NOTE, pitch, n*4, m*(v->default_length)*5);
  bentpitch[notes] = bend_down;
  addfeature(NOTE, pitchdown, n*4, m*(v->default_length)*5);
  bentpitch[notes] = active_pitchbend;
  addfeature(NOTE, pitch, n*4, m*(v->default_length)*5);
  marknoteend();
}



/* [SS] 2012-06-30 */
static void doroll_setup(note,octave,n,m,pitch)
char note;
int octave, n, m;
int pitch;
{
  char up, down;
  int t;
  int upoct, downoct, pitchup, pitchdown;
  int bend_up,bend_down;
  char *anoctave = "cdefgab";
  struct notestruct *s;

  upoct = octave;
  downoct = octave;
  t = (int) ((long) strchr(anoctave, note)  - (long) anoctave);
  up = *(anoctave + ((t+1) % 7));
  down = *(anoctave + ((t+6) % 7));
  if (up == 'c') upoct = upoct + 1;
  if (down == 'b') downoct = downoct - 1;
  pitchup = pitchof_b(up, v->basemap[(int)up - 'a'], 1, upoct, 0,&bend_up);
  pitchdown = pitchof_b(down, v->basemap[(int)down - 'a'], 1, downoct, 0,&bend_down);

  s = (struct notestruct*) checkmalloc(sizeof(struct notestruct));
  s->pitch = pitch;
  s->index = notes;
  s->notetype = ROLL;
  s->pitchup = pitchup;
  s->pitchdown = pitchdown;
  s->bendup = bend_up;
  s->benddown = bend_down;
  s->default_length = global.default_length;
  if (notesdefined < 0 || notesdefined>999)
      printf("illegal notesdefined = %d\n",notesdefined);
  noteaddr[notesdefined] = s;
  if (notesdefined < 1000) notesdefined++;
}

static void doroll_output(i)
int i;
{
int deco_index;
int pitch,pitchup,pitchdown,bend_up,bend_down;
int default_length;
int n,m;
int a,b;
struct notestruct *s;
deco_index = decotype[i];
s =  noteaddr[deco_index];
pitch = s->pitch;
pitchdown = s->pitchdown;
pitchup = s->pitchup;
active_pitchbend = bentpitch[i];
bend_up =  s->bendup;
bend_down =  s->benddown;
default_length = s->default_length;
n = num[i]*default_length;
m = denom[i]*4;
reduce(&n,&m);
a = n*4;
b = m*default_length*5;
reduce(&a,&b);
replacefeature(NOTE, pitch, a, b,i);
i++;
insertfeature(NOTE, pitchup, a, b,i);
bentpitch[i] = bend_up;
i++;
insertfeature(NOTE, pitch, a, b,i);
bentpitch[i] = active_pitchbend;
i++;
insertfeature(NOTE, pitchdown, a, b,i);
bentpitch[i] = bend_down;
i++;
insertfeature(NOTE, pitch, a, b,i);
bentpitch[i] = active_pitchbend;
}


static void dotrill(note, octave, n, m, pitch)
/* applies a trill to a note */
char note;
int octave, n, m;
int pitch;
{
  char up;
  int i, t;
  int upoct, pitchup;
  char *anoctave = "cdefgab";
  int a, b, count;
  int bend;

  upoct = octave;
  t = (int) ((long) strchr(anoctave, note)  - (long) anoctave);
  up = *(anoctave + ((t+1) % 7));
  if (up == 'c') upoct = upoct + 1;
  pitchup = pitchof_b(up, v->basemap[(int)up - 'a'], 1, upoct, 0,&bend);
  a = 4;
  b = m*(v->default_length);
  count = n;
  while ((tempo*a)/((long)b) > 100000L) {
    count = count*2;
    b = b*2;
  };
  i = 0;
  while (i < count) {
    /*if (i == count - 1) {  **bug** [SS] 2006-09-10 */
     if (i == 0) {
      marknotestart();
    };
    if (i%2 == 0) {
      bentpitch[notes] = bend;
      addfeature(NOTE, pitchup, a, b);
    } else {
      bentpitch[notes] = active_pitchbend;
      addfeature(NOTE, pitch, a, b);
    };
    i = i + 1;
  };
  marknoteend();
}

static void dotrill_setup(note, octave, n, m, pitch)
char note;
int octave, n, m;
int pitch;
{
  char up;
  int i, t;
  int upoct, pitchup;
  char *anoctave = "cdefgab";
  int bend;
  struct notestruct *s;

  upoct = octave;
  t = (int) ((long) strchr(anoctave, note)  - (long) anoctave);
  up = *(anoctave + ((t+1) % 7));
  if (up == 'c') upoct = upoct + 1;
  pitchup = pitchof_b(up, v->basemap[(int)up - 'a'], 1, upoct, 0,&bend);
  s = (struct notestruct*) checkmalloc(sizeof(struct notestruct));
  s->pitch = pitch;
  s->index = notes;
  s->notetype = TRILL;
  s->pitchup = pitchup;
  s->pitchdown = 0;
  s->default_length = global.default_length;
  s->bendup = bend;
  s->benddown = active_pitchbend;
  if (notesdefined < 0 || notesdefined>999)
      printf("illegal notesdefined = %d\n",notesdefined);
  noteaddr[notesdefined] = s;
  if (notesdefined < 1000) notesdefined++;
  }

static void dotrill_output(i)
int i;
{
int deco_index;
int pitch,pitchup,pitchdown;
int bend;
int default_length;
int n,m,a,b;
int count,j;
struct notestruct *s;
deco_index = decotype[i];
s =  noteaddr[deco_index];
pitch = s->pitch;
pitchdown = s->pitchdown;
pitchup = s->pitchup;
default_length = s->default_length;
bend = s->bendup;
active_pitchbend = s->benddown;
n = num[i]*default_length;
m = denom[i]*4;
reduce(&n,&m);
removefeature(i);
  a = 4;
  b = m*default_length;
  count = n;
  while ((tempo*a)/((long)b) > 100000L) {
    count = count*2;
    b = b*2;
  };
  j = 0;
reduce(&a,&b);
  while (j < count) {
    /*if (i == count - 1) {  **bug** [SS] 2006-09-10 */
    if (j%2 == 0) {
      insertfeature(NOTE, pitchup, a, b,i);
      bentpitch[i] = bend;
      i++;
    } else {
      insertfeature(NOTE, pitch, a, b,i);
      bentpitch[i] = active_pitchbend;
      i++;
    };
    j = j + 1;
  };
}


void dump_notestruct () {
  int i;
  struct notestruct *s;
  for (i = 0;i<notesdefined; i++) {
    s = noteaddr[i];
    printf("%d ",i);
    printf("%d ",s->index);
    printf("%d ",s->notetype);
    printf("%d ",s->pitch);
    printf("%d ",s->pitchup);
    printf("%d\n",s->pitchdown);
    }
  } 

/* [SS] 2012-07-03 */

void free_notestructs () {
int i;
struct notestruct *s;
for (i=1;i<notesdefined;i++) {
  s =  noteaddr[i];
  free(s);
  }
}  
  

void makecut (mainpitch, shortpitch,mainbend,shortbend, n,m)
int mainpitch,shortpitch,mainbend,shortbend,n,m;
{
addfeature(GRACEON, 0, 0, 0);
bentpitch[notes] = shortbend;
addfeature(NOTE, shortpitch, 4,v->default_length);
addfeature(GRACEOFF, 0, 0, 0);
bentpitch[notes] = mainbend;
addfeature(NOTE, mainpitch, 4*n,m*v->default_length);
}

void makeharproll (pitch, bend,n,m)   /* [JS] 2011-04-29 */
int pitch,bend,n,m;
{
bentpitch[notes] = bend;
addfeature(NOTE, pitch, 4*n/2,m*2*v->default_length);
bentpitch[notes] = bend;
addfeature(NOTE, pitch, 4*n/2,m*2*v->default_length);
bentpitch[notes] = bend;
addfeature(NOTE, pitch, 4*n/2,m*v->default_length);
}

void makeharproll3 (pitch, bend,n,m) /* [JS] 2011-04-29 */
int pitch,bend,n,m;
{
int a=n-1;
bentpitch[notes] = bend;
addfeature(NOTE, pitch, 4*(a)/2,m*2*v->default_length);
bentpitch[notes] = bend;
addfeature(NOTE, pitch, 4*(a)/2,m*2*v->default_length);
bentpitch[notes] = bend;
addfeature(NOTE, pitch, 4*(n/2+1),m*v->default_length);
}


static void doornament(note, octave, n, m, pitch)
/* applies a roll to a note */
char note;
int octave, n, m;
int pitch;
{
  char up, down;
  int t;
  int upoct, downoct, pitchup, pitchdown;
  char *anoctave = "cdefgab";
  int nnorm,mnorm,nn;
  int bend_up,bend_down;

  upoct = octave;
  downoct = octave;
  t = (int) ((long) strchr(anoctave, note)  - (long) anoctave);
  up = *(anoctave + ((t+1) % 7));
  down = *(anoctave + ((t+6) % 7));
  if (up == 'c') upoct = upoct + 1;
  if (down == 'b') downoct = downoct - 1;
  if (harpmode)	/* [JS] 2011-04-29 */
  {
	  pitchup=pitch;
	  pitchdown=pitch;
	  bend_up=8192;
	  bend_down=8192;
  }
  else
  {
	pitchup = pitchof_b(up, v->basemap[(int)up - 'a'], 1, upoct, 0,&bend_up);
	pitchdown = pitchof_b(down, v->basemap[(int)down - 'a'], 1, downoct, 0,&bend_down);
  }
  marknotestart();
  /* normalize notelength to L:1/8 */
  /*  nnorm =n*8; [SS] 2008-06-14 */
  nnorm = n*(v->default_length);
  mnorm =m*(v->default_length);
  reduce(&nnorm,&mnorm);
  if (nnorm == 3 && mnorm == 1) /* dotted quarter note treated differently */
  {
	 if (harpmode)  /* [JS] 2011-04-29 */
	 {
	   makeharproll3(pitch,active_pitchbend,n,m);
	 }
	 else
	 {
		 nn = n/3; /* in case L:1/16 or smaller */
		 if(nn < 1) nn=1;
		 bentpitch[notes] = active_pitchbend; /* [SS] 2006-11-3 */
		 addfeature(NOTE, pitch, 4*nn,v->default_length);
		 makecut(pitch,pitchup,active_pitchbend,bend_up,nn,m);
		 makecut(pitch,pitchdown,active_pitchbend,bend_down,nn,m);
	 }
  }
  else 
  {
	   if (harpmode) /* [JS] 2011-04-29 */
	   {
		   makeharproll(pitch,active_pitchbend,n,m);
	   }
	   else
	   {
		   makecut(pitch,pitchup,active_pitchbend,bend_up,n,m);
	   }
  }
  marknoteend();
}



static void hornp(num, denom)
/* If we have used R:hornpipe, this routine modifies the rhythm by */
/* applying appropriate broken rhythm */
int num, denom;
{
  if ((hornpipe) && (notes > 0) && (feature[notes-1] != GT)) {
    if ((num*last_denom == last_num*denom) && (num == 1) &&
        (denom*time_num == 32)) {
      if (((time_num == 4) && (bar_denom == 8)) ||
          ((time_num == 2) && (bar_denom == 16))) {
           /* addfeature(GT, 1, 0, 0); */
           v->brokentype = GT;
           v->brokenmult = 1;
           v->brokenpending = 0;
      };
    };
    last_num = num;
    last_denom = denom;
  };
}

void event_note(decorators, accidental, mult, note, xoctave, n, m)
/* handles a note in the abc */
int decorators[DECSIZE];
int mult;
char accidental, note;
int xoctave, n, m;
{
  int num, denom;
  int octave;
  int pitch;
  int pitch_noacc;
  int dummy;

  decotype[notes] = 0; /* [SS] 2012-07-02 no decoration */
  if (voicesused == 0) bodystarted=1;
  if (v == NULL) {
    event_fatal_error("Internal error - no voice allocated");
  };
  if (gracenotes && ignore_gracenotes) return; /* [SS] 2010-01-08 */
  octave = xoctave + v->octaveshift;
  num = n;
  denom = m;
  if (v->inchord) v->chordcount = v->chordcount + 1;
/*  if (tuplecount > 0 && !v->inchord) { [SS] 2008-06-13} */
  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->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));
  };

/* linear temperament support */
  if (v->drumchannel) pitch = barepitch(note,accidental,mult,octave);
  else if (nopropagate_accidentals == 1 ) 
    pitch = pitchof_b(note, accidental, mult, octave, 0,&active_pitchbend);
  else
    pitch = pitchof_b(note, accidental, mult, octave, 1,&active_pitchbend);
#ifndef MAKAM
  pitch_noacc = pitchof_b(note, 0, 0, octave, 0,&dummy);
#endif
  if (decorators[FERMATA] && !ignore_fermata) {
    if(fermata_fixed) addfract(&num,&denom,1,1);
    else num = num*2;
  };
  if (v->chordcount == 1) {
    v->chord_num = num*4;
    v->chord_denom = denom*(v->default_length);
  };
  if ((decorators[ROLL]) || (decorators[ORNAMENT]) || (decorators[TRILL])) {
    if (v->inchord) {
      event_error("Rolls and trills not supported in chords");
      pitchline[notes] = pitch_noacc; /* [SS] 2013-03-26 */
      bentpitch[notes] = active_pitchbend; /* [SS] 2013-03-26 */
      addfeature(NOTE, pitch, num*4, denom*2*(v->default_length)); /* [SS] */
    } else {
      if (easyabcmode) /* [SS] 2011-07-18 */ 
         addfeature(META,0,lineno,lineposition); /* [SS] 2011-07-18 */
      if (decorators[TRILL]) {
        decotype[notes] = notesdefined; /* [SS] 2012-06-29 */
        /*dotrill(note, octave, num, denom, pitch);*/
        dotrill_setup(note, octave, num, denom, pitch);
        addfeature(NOTE, pitch, num*4, denom*(v->default_length));
      }
      else if (decorators[ORNAMENT]) {
        doornament(note, octave, num, denom, pitch);
      }
      else { 
        decotype[notes] = notesdefined; /* [SS] 2012-06-29 */
        /*doroll(note, octave, num, denom, pitch);*/
        doroll_setup(note, octave, num, denom, pitch);
        bentpitch[notes] = active_pitchbend;
        addfeature(NOTE, pitch, num*4, denom*(v->default_length));
      };
     }; /* end of else block for not in chord */
   } /* end of if block for ROLL,ORNAMENT,TRILL */
   else {
    if (decorators[STACCATO] || decorators[BREATH]) {
      if (v->inchord) {
        if (v->chordcount == 1) {
          addfeature(REST, pitch, num*4, denom*(v->default_length));
        };
	pitchline[notes] = pitch_noacc;
        bentpitch[notes] = active_pitchbend;
        addfeature(NOTE, pitch, num*4, denom*2*(v->default_length));
      } else {
	pitchline[notes] = pitch_noacc;
        if (easyabcmode) /* [SS] 2011-07-18 */ 
         addfeature(META,0,lineno,lineposition); /* [SS] 2011-07-18 */
        bentpitch[notes] = active_pitchbend; /* [SS] 2012-05-29 */
        addfeature(NOTE, pitch, num*4, denom*2*(v->default_length));
        marknotestart();
        addfeature(REST, pitch, num*4, denom*2*(v->default_length));
        marknoteend();
      };
    } else {
      pitchline[notes] = pitch_noacc;
    if (easyabcmode && !v->inchord) /* [SS] 2011-07-18 */ 
         addfeature(META,0,lineno,lineposition); /* [SS] 2011-07-18 */
      bentpitch[notes] = active_pitchbend; /* [SS] 2012-05-29 */
      addfeature(NOTE, pitch, num*4, denom*(v->default_length));
      if (!v->inchord) {
        marknote();
      }; 
      if ((v->inslur) && (!v->ingrace)) {
        addfeature(SLUR_TIE, 0, 0, 0);
      };
    };
  };
}


void event_microtone(int dir, int a, int b)
{
int bend;
/* pitchwheel range +/- 2 semitones according to General MIDI
specification*/
/* resolution of 14bit -- order of bytes is inverted for pitchbend */
setmicrotone.num = dir*a; /* [SS] 2014-01-20 */
setmicrotone.denom = b;
if (a == 0) {bend = 8192; /* [SS] 2014-01-19 */
             microtone = 0; /* [SS] 2014-01-20 */
             setmicrotone.num = 0; /* [SS] 2014-01-25 */
             setmicrotone.denom = 0;
             return;
            }
else {
  bend = dir*((int)(4096.0*a/b))+8192; /* sorry for the float! */
  bend = bend<0?0:(bend>16383?16383:bend);
  }
active_pitchbend = bend;
microtone=1;
}


void event_normal_tone()
{ 
/* event_specific("MIDI", "pitchbend 0 64"); [SS] 2006-09-30 */
microtone = 0;
}

char *get_accidental(place, accidental)
/* read in accidental - used by event_handle_gchord() */
char *place; /* place in string being parsed */
char *accidental; /* pointer to char variable */
{
  char *p;

  p = place;
  *accidental = '=';
  if (*p == '#') {
    *accidental = '^';
    p = p + 1;
  };
  if (*p == 'b') {
    *accidental = '_';
    p = p + 1;
  };
  return(p);
}

void event_handle_gchord(s)
/* handler for the guitar chords */
char* s;
{
  int basepitch;
  char accidental, note;
  char* p;
  char name[9];
  int i;
  int chordno;
  int bassnote;
  int inversion;

  if ((*s >= '0') && (*s <= '5')) {
    event_finger(s);
    return;
  };
  p = s;
  if (*p == '(') p = p+1; /* ignore leading ( [SS] 2005-03-19*/
  if ((*p >= 'A') && (*p <= 'G')) {
    note = *p - (int) 'A' + (int) 'a';
    bassnote = 0;
    p = p + 1;
  } else {
    if ((*p >= 'a') && (*p <= 'g')) {
      note = *p;
      bassnote = 1;
      p = p + 1;
    } else {
      /* Experimental feature supports "_ignored words" */
      if (strchr("_^<>@", (int)*p) == NULL) {
        event_error("Guitar chord does not start with A-G or a-g");
      };
      return;
    };
  };
  p = get_accidental(p, &accidental);
  basepitch = pitchof(note, accidental, 1, 0, 0) - middle_c;
  i = 0;
  while ((i<9) && (*p != ' ') && (*p != '\0') && (*p != '(')
               && (*p != '/') && (*p != ')')) {
/* also ignore closing parentheses ')'  [SS] 2005-03-19*/
    name[i] = *p;
    i = i+1;
    p = p + 1;
  };
  inversion = -1;
  if (*p == '/') {
    p = p + 1;
    if ((*p >= 'A') && (*p <= 'G')) {
      note = (int)*p - 'A' + 'a';
      p = p + 1;
      p = get_accidental(p, &accidental);
      inversion = pitchof(note, accidental, 1, 0, 0) - middle_c;
    } else if ((*p >= 'a') && (*p <= 'g')) {
      note = (int)*p;
      p = p + 1;
      p = get_accidental(p, &accidental);
      inversion = pitchof(note, accidental, 1, 0, 0) - middle_c;
    } else {
      event_error(" / must be followed by A-G or a-g in gchord");
    };
  };
  name[i] = '\0';
  chordno = getchordnumber(name);
  if (chordno == 0) {
    char msg[200];

    sprintf(msg, "Unrecognized chord name \"%s\"", name);
    event_error(msg);
    chordno = 1; /* defaults to major */
  } else {
    /* only record voice as having chords if we recognize chord type */
    v->hasgchords = 1;
    gchordvoice = v->indexno;
  };
  if (bassnote) {
    chordno = -1;
  };
  addfeature(GCHORD, basepitch, inversion, chordno);
}

void event_handle_instruction(s)
/* handler for ! ! instructions */
/* does ppp pp p mp mf f ff fff */
/* also does !drum! and !nodrum! */
char* s;
{
  char buff[MAXLINE];
  char* p;
  char* q;
  int done;
  char midimsg[40];

  p = s;
  /* remove any leading spaces */
  skipspace(&p);
  /* remove any trailing spaces */
  q = p;
  while ((*q != '\0') && (*q != ' ')) {
    q = q + 1;
  };
  if (*q == ' ') {
    *q = '\0';
  };
  done = 0;
/* add nofnop ... */
if (nofnop == 0) {
  if (strcmp(p, "ppp") == 0) {
    event_specific("MIDI", "beat 30 20 10 1");
    done = 1;
  };
  if (strcmp(p, "pp") == 0) {
    event_specific("MIDI", "beat 45 35 20 1");
    done = 1;
  };
  if (strcmp(p, "p") == 0) {
    event_specific("MIDI", "beat 60 50 35 1");
    done = 1;
  };
  if (strcmp(p, "mp") == 0) {
    event_specific("MIDI", "beat 75 65 50 1");
    done = 1;
  };
  if (strcmp(p, "mf") == 0) {
    event_specific("MIDI", "beat 90 80 65 1");
    done = 1;
  };
  if (strcmp(p, "f") == 0) {
    event_specific("MIDI", "beat 105 95 80 1");
    done = 1;
  };
  if (strcmp(p, "ff") == 0) {
    event_specific("MIDI", "beat 120 110 95 1");
    done = 1;
  };
  if (strcmp(p, "fff") == 0) {
    event_specific("MIDI", "beat 127 125 110 1");
    done = 1;
  };

  if ((strcmp(p,"crescendo(") == 0) || (strcmp(p,"<(") == 0) || 
      (strcmp(p,"crescendo)") == 0) || (strcmp(p,"<)") == 0)) {
          sprintf(midimsg,"beatmod %d",velocitychange);
          event_specific("MIDI", midimsg);
          done = 1;
   }

  if (
      (strcmp(p,"diminuendo)") == 0) || (strcmp(p,">)") == 0) || 
      (strcmp(p,"diminuendo(") == 0) || (strcmp(p,">(") == 0)) {
          sprintf(midimsg,"beatmod -%d",velocitychange);
          event_specific("MIDI", midimsg);
          done = 1;
   }

}; /* end nofnop */
  if (strcmp(p, "drum") == 0) {
    addfeature(DRUMON, 0, 0, 0);
    drumvoice = v->indexno;
    done = 1;
  };
  if (strcmp(p, "nodrum") == 0) {
    addfeature(DRUMOFF, 0, 0, 0);
    done = 1;
  };

  if (strcmp(s, "fermata") == 0) {
    decorators_passback[FERMATA] =1;
    done = 1;
    };

  if (strcmp(s, "trill") == 0) {
    decorators_passback[TRILL] =1;
    done = 1;
    };


  if (strcmp(p, "arpeggio") == 0) {
    addfeature(ARPEGGIO, 0, 0, 0);
    done = 1;
  };

/* [SS] 2011-10-19 */
  if (strcmp(p, "ped") == 0) {
    addfeature(PEDAL_ON, 0, 0, 0);
    done = 1;
  };

/* [SS] 2011-10-19 */
  if (strcmp(p, "ped-end") == 0) {
    addfeature(PEDAL_OFF, 0, 0, 0);
    done = 1;
  };

  if (strcmp(s, "breath") == 0) {
    decorators_passback[BREATH] =1;
    done = 1;
    };

 if (strcmp(s, "bend") == 0) {   /* [SS] 2012-12-11 */
   addfeature(EFFECT, 1, 0, 0);
   done = 1;
  };

  if (done == 0 && quiet == -1) {    /* [SS] 2013-11-02 */
    sprintf(buff, "instruction !%s! ignored", s);
    event_warning(buff);
  };
}

static void setmap(sf, map, mult)
/* work out accidentals to be applied to each note */
int sf; /* number of sharps in key signature -7 to +7 */
char map[7];
int mult[7];
{
  int j;

  for (j=0; j<7; j++) {
    map[j] = '=';
    mult[j] = 1;
  };
  if (sf >= 1) map['f'-'a'] = '^';
  if (sf >= 2) map['c'-'a'] = '^';
  if (sf >= 3) map['g'-'a'] = '^';
  if (sf >= 4) map['d'-'a'] = '^';
  if (sf >= 5) map['a'-'a'] = '^';
  if (sf >= 6) map['e'-'a'] = '^';
  if (sf >= 7) map['b'-'a'] = '^';
  if (sf <= -1) map['b'-'a'] = '_';
  if (sf <= -2) map['e'-'a'] = '_';
  if (sf <= -3) map['a'-'a'] = '_';
  if (sf <= -4) map['d'-'a'] = '_';
  if (sf <= -5) map['g'-'a'] = '_';
  if (sf <= -6) map['c'-'a'] = '_';
  if (sf <= -7) map['f'-'a'] = '_';
}

static void altermap(v, modmap, modmul,modmic)
/* apply modifiers to a set of accidentals */
struct voicecontext* v;
char modmap[7];
int modmul[7];
struct fraction modmic[7];
{
  int i;

  for (i=0; i<7; i++) {
    if (modmap[i] != ' ') {
      v->basemap[i] = modmap[i];
      v->basemul[i] = modmul[i];
      v->basemic[i].num = modmic[i].num;
      v->basemic[i].denom = modmic[i].denom;
      /*printf("basemic[%d] = %d %d\n",i,modmic[i].num,modmic[i].denom);*/
    };
  };
}

static void copymap(v)
/* sets up working map at the start of each bar */
struct voicecontext* v;
{
  int i,j;

  for (i=0; i<7; i++) {
    for (j=0;j<10;j++) {
      v->workmap[i][j] = v->basemap[i];
      v->workmul[i][j] = v->basemul[i];
    /* [SS] 2014-01-26 */
      v->workmic[i][j].num = v->basemic[i].num;
      v->workmic[i][j].denom = v->basemic[i].denom;
      }
   };
}

/* workaround for problems with PCC compiler */
/* data may be written to an internal buffer */

int myputc(c)
char c;
{
  return (putc(c,fp));
}

void addfract(int *xnum, int *xdenom, int a, int b)
/* add a/b to the count of units in the bar */
{
  *xnum = (*xnum)*b + a*(*xdenom);
  *xdenom = (*xdenom) * b;
  if (*xnum < 0 && *xdenom < 0) {*xnum = -*xnum; *xdenom = -*xdenom;}
  reduce(xnum, xdenom);
}

void nondestructive_readstr(out, in, limit)
char out[];
char **in;
int limit;
/* copy across alphanumeric string */
{
  int i;
  i = 0;
  while ((isalpha(*(*in+i))) && (i < limit-1)) {
    out[i] = *(*in+i);
    i = i + 1;
  };
  out[i] = '\0';
}


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 newbar;
  int lastnote, lasttie;
  int inchord;
  int tied_num, tied_denom;
  int localvoiceno;
  int samechord;
  char command[40];

  /* find note to be tied */
  samechord = 0;
  newbar =0;  /* if 1 it allows pitchline[] == pitchline[] test */
  if (xinchord) samechord = 1;
  tienote = j;
  localvoiceno = voiceno;
  while ((tienote > 0) && (feature[tienote] != NOTE) &&
         (feature[tienote] != REST)) {
    tienote = tienote - 1;
    if (feature[tienote] == VOICE) break; /* [SS] 2010-01-15 */
  };
  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;

/* fix for tied microtone note */
    if (feature[tienote+1] == DYNAMIC) { 
      nondestructive_readstr(command, &atext[pitch[tienote+1]], 40);
      if (strcmp(command, "pitchbend") == 0) {
        /*printf("need to remove pitchbend following TNOTE\n"); */
        removefeature(tienote+1);
        j = j-1;
        }
    }

/* fix for tied microtone note inside a slur */
    if (feature[tienote+1] == SLUR_TIE && feature[tienote+2] == DYNAMIC) {
      nondestructive_readstr(command, &atext[pitch[tienote+2]], 40);
      if (strcmp(command, "pitchbend") == 0) {
        /*printf("need to remove pitchbend following TNOTE in slur\n"); */
        removefeature(tienote+2);
        j = j-1;
        }
    }

    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 SINGLE_BAR:
        case BAR_REP:
        case REP_BAR:
        case DOUBLE_BAR: /* [SS] 2011-09-13 */
          if (tietodo) newbar = 1;
          else newbar=0;
          break; 
        case NOTE:
          if(localvoiceno != voiceno) break;
          lastnote = place;
          if ((tied_num == 0) && (tietodo == 0)) {
            done = 1;
          };
          if (((pitchline[place] == pitchline[tienote] && newbar) || (pitch[place] == pitch[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");
    };
  };
/*if (verbose > 3) printf("dotie finished\n"); */
}

static void fix_enclosed_note_lengths(int from, int end) 
{
/* in event that the chord length was set outside [], */
/* we must go back and adjust the note length of each note */
/* inside the chord.                                   */

int j;
if (apply_fermata_to_chord && !ignore_fermata)   /* [SS] 2012-03-26 */
    {
    if (fermata_fixed) addfract(&num[end],&denom[end],1,1);
    else num[end] *= 2;
    }
for (j = from; j < end; j++)
  {
  if (feature[j] == NOTE || feature[j] == TNOTE) 
    {
      num[j] = num[end];
      denom[j] =denom[end]; 
    }
  }
apply_fermata_to_chord = 0;
}


static int patchup_chordtie(int chordstart,int chordend)
{
int i,tieloc;
for (i=chordend;i>=chordstart;i--) {
	if(feature[i]==NOTE && feature[i+1] != TIE) {
             insertfeature(TIE,0,0,0,i+1);
	     tieloc = i+1;
	}
     }
 return tieloc;
}


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, chord_denom;
  int chord_start,chord_end;
  int voiceno;

  j = 0;
  inchord = 0;
  voiceno = 1;
  while (j<notes) {
    switch (feature[j]) {
    case CHORDON:
      inchord = 1;
      chord_num = -1;
      chord_start = j;
      j = j + 1;
      break;
    case CHORDOFFEX:
      inchord = 0;
      chord_end=j;
      j = j + 1;
      break;
    case CHORDOFF:
      if (!((!inchord) || (chord_num == -1))) {
        num[j] = chord_num; 
        denom[j] = chord_denom; 
      };
      inchord = 0;
      chord_end=j;
      j = j + 1;
      break;
    case NOTE:
      if ((inchord) && (chord_num == -1)) {
        chord_num = num[j];
        chord_denom = denom[j];
        /* use note length in num,denom as chord length */
        /* if chord length is not set outside []        */
      };
      j = j + 1;
      break;
    case REST:
      if ((inchord) && (chord_num == -1)) {
        chord_num = num[j];
        chord_denom = denom[j];
      };
      j = j + 1;
      break;
    case TIE:
      dotie(j, inchord,voiceno);
      j = j + 1;
      break;
    case LINENUM:
      lineno = pitch[j];
      j = j + 1;
      break;
    case VOICE:
      voiceno = pitch[j];
    default:
      j = j + 1;
      break;
    };
  };
if (verbose >3) printf("tiefix finished\n");
}

static void applygrace_orig(int);
static void applygrace_new(int);

static void applygrace(place)
int place;
{
if (gfact_method)  applygrace_orig(place); 
else applygrace_new(place);
}


static void applygrace_orig(place)
int place;
/* assign lengths to grace notes before generating MIDI */
/* This version adjusts the length of the grace notes
 * based on the length of the following note, the
 * number of the notes in the group of grace notes
 * and the desired fraction, gfact_num/gfact_denom.
 * This does not sound very natural.
 */
{
  int start, end, p;
  int next_num, next_denom;
  int fact_num, fact_denom;
  int grace_num, grace_denom;
  int j;
  int nextinchord;
  int hostnotestart, hostnoteend;

  j = place;
  start = -1;
  while ((j < notes) && (start == -1)) {
    if (feature[j] == GRACEON) {
      start = j;
    };
    if (feature[j] == GRACEOFF) {
      event_error("} with no matching {");
    };
    j = j + 1;
  };
  /* now find end of grace notes */
  end = -1;
  while ((j < notes) && (end == -1)) {
    if (feature[j] == GRACEOFF) {
      end = j;
    };
    if ((feature[j] == GRACEON) && (j != start - 1)) {
      event_error("nested { not allowed");
    };
    j = j + 1;
  };
  /* now find following note */
  nextinchord = 0;
  hostnotestart = -1;
  while ((hostnotestart == -1) && (j < notes)) {
    if ((feature[j] == NOTE) || (feature[j] == REST)) {
      hostnotestart = j;
    };
    if (feature[j] == GRACEON) {
      event_error("Intervening note needed between grace notes");
    };
    if (feature[j] == CHORDON) {
      nextinchord = 1;
    };
    j = j + 1;
  };
  hostnoteend = -1;
  if (nextinchord) {
    while ((hostnoteend == -1) && (j < notes)) {
      if (feature[j] == CHORDOFF || feature[j] == CHORDOFFEX) {
        /*hostnoteend = j-1; [SS] 2009-10-27 */
        hostnoteend = j;
      };
      j = j + 1;
    };
  } else {
    hostnoteend = hostnotestart;
  };
  if (hostnotestart == -1) {
    event_error("No note found to follow grace notes");
  } else {
    /* count up grace units */
    grace_num = 0;
    grace_denom = 1;
    p = start;
    while (p <= end) {
      if ((feature[p] == NOTE) || (feature[p] == REST)) {
        grace_num = grace_num * denom[p] + grace_denom * num[p];
        grace_denom = grace_denom * denom[p];
        reduce(&grace_num, &grace_denom);
      };
      p = p + 1;
    };
    /* adjust host note or notes */
    p = hostnotestart;
    while (p <= hostnoteend) {
      if ((feature[p] == NOTE) || (feature[p] == REST) || 
          (feature[p] == CHORDOFF) || (feature[p] == CHORDOFFEX)) {
        next_num = num[p];
        next_denom = denom[p];
        num[p] = num[p] * (gfact_denom - gfact_num);
        denom[p] = next_denom * gfact_denom;
        reduce(&num[p], &denom[p]);
      };
      p = p + 1;
    };
    fact_num = next_num * grace_denom * gfact_num;
    fact_denom = next_denom * grace_num * gfact_denom;
    reduce(&fact_num, &fact_denom);
    /* adjust length of grace notes */
    p = start;
    while (p <= end) {
      lenmul(p, fact_num, fact_denom);
      p = p + 1;
    };
  };
}


static void applygrace_new(place)
int place;
/* assign lengths to grace notes before generating MIDI */
/* In this version each grace note has a predetermined
 * length, eg, (1/64 th note) and the total length of
 * the group of grace notes is stolen from the following
 * note. If the length of the following note is note
 * long enough to accommodate the group of grace notes,
 * then all the grace notes are given a length of 0.
 */
{
  int start, end, p;
  int fact_num, fact_denom;
  int grace_num, grace_denom;
  int j;
  int nextinchord;
  int hostnotestart, hostnoteend;
  int  adjusted_num, adjusted_den;

  j = place;
  start = -1;
  while ((j < notes) && (start == -1)) {
    if (feature[j] == GRACEON) {
      start = j;
    };
    if (feature[j] == GRACEOFF) {
      event_error("} with no matching {");
    };
    j = j + 1;
  };
  /* now find end of grace notes */
  end = -1;
  while ((j < notes) && (end == -1)) {
    if (feature[j] == GRACEOFF) {
      end = j;
    };
    if ((feature[j] == GRACEON) && (j != start - 1)) {
      event_error("nested { not allowed");
    };
    j = j + 1;
  };
  /* now find following note */
  nextinchord = 0;
  hostnotestart = -1;
  while ((hostnotestart == -1) && (j < notes)) {
    if ((feature[j] == NOTE) || (feature[j] == REST)) {
      hostnotestart = j;
    };
    if (feature[j] == GRACEON) {
      event_error("Intervening note needed between grace notes");
    };
    if (feature[j] == CHORDON) {
      nextinchord = 1;
    };
    j = j + 1;
  };
  hostnoteend = -1;
  if (nextinchord) {
    while ((hostnoteend == -1) && (j < notes)) {
      if (feature[j] == CHORDOFF || feature[j] == CHORDOFFEX) {
      /*  hostnoteend = j-1;  [SS] 2009-10-27 */
      hostnoteend = j;
      };
      j = j + 1;
    };
  } else {
    hostnoteend = hostnotestart;
  };
  if (hostnotestart == -1) {
    event_error("No note found to follow grace notes");
  } else {
    /* count up grace units */
    grace_num = 0;
    grace_denom = 1;
    p = start;
    while (p <= end) {
      if ((feature[p] == NOTE) || (feature[p] == REST)) {
        grace_num = grace_num * denom[p] + grace_denom * num[p];
        grace_denom = grace_denom * denom[p];
        reduce(&grace_num, &grace_denom);
      };
      p = p + 1;
    };

/* new stuff starts here [SS] 2004/06/11 */

/* is the following note long enough */
   p = hostnotestart;
   adjusted_num = num[p]*grace_denom*gfact_denom - denom[p]*grace_num;
   adjusted_den = denom[p]*grace_denom*gfact_denom;
   if (adjusted_den <=0.0) /* not long enough*/
      {
      p = start;
      while (p <= end)
        { 
        lenmul(p,0,1);
	p = p+1;
	}
      return;
      }

    /* adjust host note or notes */
    p = hostnotestart;
    while (p <= hostnoteend) {
      if ((feature[p] == NOTE) || (feature[p] == REST) || 
          (feature[p] == CHORDOFF) || (feature[p] == CHORDOFFEX)) {
        num[p] = adjusted_num;
        denom[p] = adjusted_den;
        reduce(&num[p], &denom[p]);
      };
      p = p + 1;
    };

    fact_num = 1;
    fact_denom =gfact_denom;
    reduce(&fact_num, &fact_denom);
    /* adjust length of grace notes */
    p = start;
    while (p <= end) {
      lenmul(p, fact_num, fact_denom);
      p = p + 1;
    };
  };
}



static void dograce()
/* assign lengths to grace notes before generating MIDI */
{
  int j;

  j = 0;
  while (j < notes) {
    if (feature[j] == GRACEON) {
      applygrace(j);
    };
    if (feature[j] == SETGRACE) {
      gfact_method = pitch[j];
      gfact_num =  num[j];
      gfact_denom = denom[j];
    };
    if (feature[j] == LINENUM) {
      lineno = pitch[j];
    };
    j = j + 1;
  };
if (verbose >3) printf("dograce finished\n");
}

static void zerobar()
/* start a new count of beats in the bar */
{
  bar_num = 0;
  bar_denom = 1;
}

void event_bar(type, replist)
/* handles bar lines of various types in the abc */
/* This function was reorganized on 2013-12-02 [SS] to 
   handle play on repeats when there are split voices.
*/
int type;
char* replist;
{
  int newtype;
  int depth;
  int voiceno, indexno;

  depth = splitdepth;
  if (splitdepth > 0) {
  /* we encountered the repeat while in a split voice;
     first recurse back to the original voice.
  */
  recurse_back_to_original_voice();
  }

  newtype = type;
  if ((type == THIN_THICK) || (type == THICK_THIN)) {
    newtype = DOUBLE_BAR;
  };
  addfeature(newtype, 0, 0, 0);
  copymap(v);
  zerobar();
  if (strlen(replist) > 0) {
    event_playonrep(replist);
  };

/* If there are any split voices (voice overlays),  we need to
   put repeat symbol in all the split voices. This is all done
   by sync_voice() [SS] 2013-12-02.
*/
 {
 voiceno = v->voiceno;
 indexno = v->indexno;
 while (v->tosplitno != -1)
     { 
      v = getvoicecontext(v->tosplitno);
      splitdepth++;
      addfeature(VOICE, v->indexno, 0, 0);
      sync_voice (v,0,0);
     }
 if (v->fromsplitno != -1 || splitdepth >0) recurse_back_to_original_voice();
 }
}



static void placeendrep(j)
/* patch up missing repeat */
int j;
{
  if (quiet == -1) event_warning("Assuming repeat");
  switch(feature[j]) {
  case DOUBLE_BAR:
    feature[j] = REP_BAR;
    break;
  case SINGLE_BAR:
    feature[j] = REP_BAR;
    break;
  case BAR_REP:
    feature[j] = DOUBLE_REP;
    event_warning("replacing with double repeat (::)");
    break;
  default:
    event_error("Internal error - please report");
    break;
  };
}

static void placestartrep(j)
/* patch up missing repeat */
int j;
{
  if (quiet == -1) event_warning("Assuming repeat");
  switch(feature[j]) {
  case DOUBLE_BAR:
    feature[j] = BAR_REP;
    break;
  case SINGLE_BAR:
    feature[j] = BAR_REP;
    break;
  case REP_BAR:
    if (quiet == -1) event_warning("replacing |: with double repeat (::)");
    feature[j] = DOUBLE_REP;
    break;
  case BAR_REP:
    if (quiet == -1) event_error("Too many end repeats");
    break;
  case DOUBLE_REP:
    if (quiet == -1) event_error("Too many end repeats");
    break;
  default:
    event_error("Internal error - please report");
    break;
  };
}

static void fixreps()
/* find and correct missing repeats in music */
/* abc2midi places an extra || at the start of the music */
/* This can be converted to |: if necessary. */
{
  int j;
  int rep_point; /* where to assume a repeat starts */
  int expect_repeat;   /* = 1 after |: or ::  otherwise 0*/
  int use_next; /* if 1 set next bar line as ref_point */
  int nplays; /* counts PLAY_ON_REP associated with a BAR_REP*/

  expect_repeat = 0; 
  use_next = 0;
  nplays=0;
  j = 0;
  while (j < notes) {
    switch(feature[j]) {
    case SINGLE_BAR:           /*  |  */
      if (use_next) {
        rep_point = j;
        use_next = 0;
      };
      break;
    case DOUBLE_BAR:           /*  || */
      rep_point = j;
      use_next = 0;
      break;
    case BAR_REP:              /* |:  */
/* if |:  .. |: encountered. Report error and change second |: to :: */
      if (expect_repeat) {
          if (quiet == -1) event_error(" found another |: after a |:");
          placeendrep(j);
      };
      expect_repeat = 1;
      use_next = 0;
      break;
    case REP_BAR:                /* :| */
/* if :| encountered before |: place  "|:" at reference point */
       if ((!expect_repeat) && (nplays < 1)) {
          placestartrep(rep_point);
        };
      expect_repeat = 0; 
      rep_point = j;
      use_next = 0;
      break;

    case PLAY_ON_REP:         /*  [1 or [2 or [1,3 etc  */
       if(nplays == 0 && !expect_repeat) {
           if (quiet == -1) event_error(" found [1 or like before |:");
           placestartrep(rep_point);
           }
       nplays++;
       break;

    case DOUBLE_REP:           /*  ::  */
      if (!expect_repeat) {
          if (quiet == -1) event_error(" found :: before |:");
          placestartrep(rep_point);
       };
      expect_repeat = 1;
      break;
    default:
      break;
    };
    j = j + 1;
  };
}


/* Barfly stress model support functions */

extern int segnum,segden; /* from genmidi.c */
extern int ngain[32];
extern int beatmodel;
/* [SS] 2011-08-17 */
void fdursum_at_segment(int segposnum, int segposden, int *val_num, int *val_den);

/* [SS] 2011-08-17 */
void beat_modifier (int i)
/* modifies the note durations inside a beat using Phil Taylor's model */
{
  int start_num,start_denom,startseg_num,startseg_denom;
  int segnumber;
  int end_num,end_denom,endseg_num,endseg_denom;
  int mstart_num,mstart_denom,mend_num,mend_denom;
  int delta_num,delta_denom;
/* start_num/start_denom is the original position of the note onset
 * end_num/end_denom is the original position of the end of the note
 * mstart_num/mstart_denom is the modified position of the note onset
 * mend_num/mend_denom is the modified position of the note end
 * startseg_num/startseg_denom is the position of the note onset in
 * segment units. eg. if a note starts in the middle of segment 1,
 * its value would be 3/2 (assuming we are counting from 0).
 */
  int inchord,notecount;
  notecount=0;
  inchord = 0;
  start_num=0;
  start_denom = 1;
  end_num=start_num;
  end_denom = start_denom;
  i++;
  while (feature[i] != SINGLE_BAR) {
    if (feature[i] == DOUBLE_BAR ||
        feature[i] == BAR_REP ||
        feature[i] == DOUBLE_REP ||
        feature[i] == REP_BAR) break;
    if (feature[i] == CHORDON) {
                               inchord = 1;
                               notecount = 0;
                               i++;
                               continue;
                               }
    if (feature[i] == CHORDOFF ||
        feature[i] == CHORDOFFEX) {
                               inchord = 0;
                               notecount = 0;
                               start_num = end_num;
                               start_denom = end_denom;
                               i++;
                               continue;
                               }
    if (feature[i] == NOTE || feature[i] == TNOTE ||
        (feature[i] == REST && pitch[i] ==0)) {
/* Care is needed for tied notes; they appear as TNOTE followed by*/
/* two REST. We want to ignore those two rests.*/
/* if REST and pitch[i] != 0 it is a tied note converted to a rest */
/* Hopefully we do not encounter tied rests. */
       if (notecount == 0) {
         addfract(&end_num,&end_denom,num[i],denom[i]);
         reduce(&end_num, &end_denom);
   /* Convert note positions to where they would map to after applying
      the duration modifiers. We map the positions to segment units and then
      transform them back to note units using the modified space fdursum.
      Since all units of time are represented as fractions, the operations
      are less transparent.
   */
   /* divide start_num/start_denom by segnum/segden */
   /* (divide note duration by 4 because they are 4 times larger in num/denom*/
         startseg_num = start_num*segden;
         startseg_denom = start_denom*segnum*4;
         reduce(&startseg_num,&startseg_denom);
   /* repeat for end_num and end_denom; */
         endseg_num = end_num*segden;
         endseg_denom = end_denom*segnum*4;
         reduce(&endseg_num,&endseg_denom);
         fdursum_at_segment(startseg_num,startseg_denom, &mstart_num, &mstart_denom);
         fdursum_at_segment(endseg_num,endseg_denom, &mend_num, &mend_denom);
   /*  now compute the new note length */
         delta_num = mend_num;
         delta_denom = mend_denom;
         addfract(&delta_num,&delta_denom,-mstart_num,mstart_denom);
         reduce(&delta_num,&delta_denom);
         if (inchord) notecount++;
         }
       if (verbose > 1) {
       printf("pitch %d from = %d/%d (%d/%d) to %d/%d (%d/%d) becomes %d/%d %d/%d",pitch[i],
start_num,start_denom,startseg_num,startseg_denom,end_num,end_denom,
endseg_num,endseg_denom,mstart_num,mstart_denom,mend_num,mend_denom);
       printf(" - %d/%d\n",delta_num,delta_denom);
       }
       num[i] = delta_num; 
       denom[i] = delta_denom;
       segnumber = startseg_num/startseg_denom; /* [SS] 2011-08-17 */
       stressvelocity[i] = ngain[segnumber];
         
       if (notecount == 0) {start_num = end_num;
                            start_denom = end_denom;}
       if (feature[i] == TNOTE) i++; /*skip following rest */
       }
    i++;
    }
}

/* [SS] 2011-08-24 */
static void apply_bf_stress_factors () {
  int j;
  char command[40];
  char inputfile[64];
  char *p;
  int barnumber;
  load_stress_parameters(rhythmdesignator);
  if (verbose)
  printf("beat_modifer using segment size %d/%d\n",segnum,segden);
  j = 0;
  barnumber = 0;
  while (j < notes) {
   switch(feature[j]) {
    case SINGLE_BAR:           /*  |  */
    case DOUBLE_BAR:           /*  || */
    case BAR_REP:              /*  |: */
    case REP_BAR:              /*  :| */
    case DOUBLE_REP:           /*  :: */
      barnumber++;
      if (verbose>1) printf("bar %d\n",barnumber);
      if (beatmodel == 2) beat_modifier(j);
    break;
    case DYNAMIC:
       p = atext[pitch[j]];
       skipspace(&p);
       readstr(command, &p, 40);
       if (strcmp(command, "stressmodel") == 0) {
          skipspace(&p);
          beatmodel = readnump(&p); /* [SS] 2011-08-23 */
          stressmodel=beatmodel;
          if (verbose) printf("%s %d\n",command,beatmodel);
          if (beatmodel == 1)
             return; /* do not apply beat_modifier */
         }
       if (strcmp(command, "ptstress") == 0) {
          skipspace(&p);
          strncpy(inputfile,p,60);
          if (verbose) printf("ptstress file = %s\n",inputfile);
          if (parse_stress_params (inputfile) == -1) readstressfile (inputfile);
          calculate_stress_parameters();
          beatmodel = 2;
          if (stressmodel !=0 && beatmodel != stressmodel) beatmodel = stressmodel;
         }
       break;
    default:
      break;
    }
  j = j+1;
  }
}



static void startfile()
/* called at the beginning of an abc tune by event_refno */
/* This sets up all the default values */
{
  int j;

  if (verbose) {
    printf("scanning tune\n");
  };
  /* set up defaults */
  sf = 0;
  mi = 0;
  setmap(0, global.basemap, global.basemul);
  copymap(&global);
  global.octaveshift = 0;
  global.keyset = 0;
  voicecount = 0;
  head = NULL;
  for (j=0;j<64;j++) vaddr[j]=NULL; 
  v = NULL;
  got_titlename = 0;
  time_num = 4;
  time_denom = 4;
  mtime_num = 4;
  mtime_denom = 4;
  timesigset = 0;
  barchecking = 1;
  global.default_length = -1;
  event_tempo(default_tempo, 1, 4, 0, NULL, NULL);
  notes = 0;
  ntexts = 0;
  gfact_num = 1;
  gfact_denom = 4;
  hornpipe = 0;
  karaoke = 0;
  numsplits = 0;
  splitdepth = 0; /* [SS] 2010-02-07 */
  v1index = -1; /* [SS] 2010-02-07 */
  retain_accidentals = default_retain_accidentals;
  fermata_fixed = default_fermata_fixed;
  if (ratio_standard == -1) {
    ratio_a = default_ratio_a;
    ratio_b = default_ratio_b;
  } else {
     ratio_a = 2;
     ratio_b = 6;
   }
  wcount = 0;
  parts = -1;
  middle_c = default_middle_c;
  temperament=0;
  for (j=0; j<26; j++) {
    part_start[j] = -1;
  };
  headerpartlabel = 0;
  initvstring(&part);
  for (j=0; j<16;j++) {
    channels[j] = 0;
  };
  set_gchords("x"); /* [SS] 2010-07-11 */
  gchordvoice = 0;
  set_drums("z");
  drumvoice = 0;
  wordvoice = 0;
  notesdefined = 1; /* [SS] 2012-07-02 */
}

void setbeat()
/* default accompaniment patterns for various time signatures */
{
  /* set up chord/fundamental sequence if not already set */
  if ((time_num == 2) && (time_denom == 2)) {
    set_gchords("fzczfzcz");
  };
  if (((time_num == 2) || (time_num == 4)) && (time_denom == 4)) {
    set_gchords("fzczfzcz");
  };
  if ((time_num == 3) && (time_denom == 4)) {
    set_gchords("fzczcz");
  };
  if ((time_num == 6) && (time_denom == 8)) {
    set_gchords("fzcfzc");
  };
  if ((time_num == 9) && (time_denom == 8)) {
    set_gchords("fzcfzcfzc");
  };
  if ((time_num == 12) && (time_denom == 8)) {
    set_gchords("fzcfzcfzcfzc");
  };
}

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;
  struct voicecontext *p;

  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;
    };
  };
/* ensure default_length is defined for all voices */
  p = head;
  while ((p != NULL)) {
    if (p->default_length == -1) p->default_length=global.default_length;
    p = p->next;
  };

  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;
  minor =0;
  if (modeindex >0 && modeindex <4) minor = 1;
  if ((dotune) && gotkey) {
    if (pastheader) {
      if (!explict) setmap(sharps, v->basemap, v->basemul); /* [SS] 2010-05-08*/
      altermap(v, modmap, modmul,modmicrotone);
      copymap(v);
      addfeature(KEY, sharps, 0, minor);
      if (gottranspose) {
        addfeature(TRANSPOSE, transpose, 0, 0);
      };
    } else {
      if (gottranspose) {
        addfeature(GTRANSPOSE, transpose, 0, 0);
      };
      if (!explict) setmap(sharps, global.basemap, global.basemul); /* [SS] 2010-05-08 */
      altermap(&global, modmap, modmul,modmicrotone);
      global.keyset=1;
      copymap(&global);
      sf = sharps;
      mi = minor;
      headerprocess();

      v = getvoicecontext(1);
      if (!inbody) v1index = notes; /* save position in case of split voice */
    };
    if (gotoctave) {
      event_octave(octave,0);
    };
  };
}



/* Handling missing repeats in multivoiced and multipart abc tune */


int voicestart[64];
int bar_rep_found[64];
int add_leftrepeat_at[100];
int num2add;

void add_missing_repeats (); 

void clear_voice_repeat_arrays () {
int i;
for (i=0;i<64;i++) {
   voicestart[i] = 0;
   bar_rep_found[i] = 0;
   }
}
  


void scan_for_missing_repeats ()
{
/* The function attempts to clean up the missing left repeats |:
 * that occur in many multivoiced or multipart abc tunes.
 * The function scans the feature[] representation and
 * keeps track where the voice appears for the first time
 * in an individual part. If a :| appears in that voice,
 * but no |: was detected, then the function inserts a |:
 * at the point where the voice first appears.
 */
int i,j;
int voicenum;
char part;
num2add = 0; /* 2010-02-06 */
part = '0';
voicenum = 0;
clear_voice_repeat_arrays(); /* [SS] 2012-03-22 */
for (i=0;i<notes;i++) {
  j = feature[i];
  /*if (j == PART || j == VOICE || j == BAR_REP || j == REP_BAR
     || j == DOUBLE_REP) [SS] 2013-03-14 */
  /* if (j == PART) [SS] 2013-03-14 */
  if (j == PART && parts != -1) /* [SS] 2013-03-14 */
                 {clear_voice_repeat_arrays();
                  part = (char) pitch[i];
                  voicestart[0] = i;
                 }
  if (j == VOICE) {
        voicenum = pitch[i];
        if(!voicestart[voicenum]) voicestart[voicenum] = i;
        }
  if (j == BAR_REP) bar_rep_found[voicenum] = 1;
  if ((j == REP_BAR || j == DOUBLE_REP) && (!bar_rep_found[voicenum])) {
    /* printf("missing BAR_REP for voice inserted for voice %d part %c\n",voicenum,part); [SS] 2011-04-19 */
     /*** add_leftrepeat_at[num2add] = voicestart[voicenum]+3; [SS] 2009-12-20*/
     add_leftrepeat_at[num2add] = voicestart[voicenum]+2; /* [SS] 2009-12-20*/
     num2add++;
     bar_rep_found[voicenum] = 1;
     }
  }
if (num2add > 0) 
 add_missing_repeats (); 
if (verbose >3) printf("scan_for_missing_repeats finished\n");
}


void add_missing_repeats () {
int i,j;
for (i = num2add-1; i >= 0; i--) {
 insertfeature(BAR_REP,0,0,0,add_leftrepeat_at[i]); 
 /*for (j=0;j<parts;j++) {*/
 /* for (j=0;j<=parts;j++) {   [SS] 2011-06-06 */
 for (j=0;j<26;j++) {  /* [SS] 2011-08-03 */
   if (part_start[j] > add_leftrepeat_at[i])
     part_start[j]++;
     }
  }
}

/* [SS] 2012-11-23 */
void convert_tnote_to_note (int loc) {
/* tied notes TNOTE are handled in a different
   manner by genmidi so we need to change it
   to NOTE so it expand_ornament can process
   it. We change TNOTE to NOTE and eliminate the
   RESTS associated with TNOTE
*/
int i,j,pitchflag;
feature[loc] = NOTE;
/* Look ahead and remove the REST associated with TNOTE    */
/* The last REST in the TNOTE sequence has a nonzero pitch */
j=loc;
for (i=0;i<6;i++) {
  if(feature[j] == REST) {
    pitchflag = pitch[j];
    removefeature(j);
    if (pitchflag != 0) break;
    } else j++; 
  }
}



/* [SS] 2012-05-30  2012-11-23 */
void expand_ornaments () {
int i;
struct notestruct *s;
int notetype,deco_index;
for (i=0;i<notes;i++) {
  if (decotype[i] != 0 && feature[i] == TNOTE) convert_tnote_to_note(i);
  if (decotype[i] != 0 && feature[i] == NOTE) {
      deco_index = decotype[i];
      s =  noteaddr[deco_index];
      notetype = s->notetype;
      switch (notetype) {
         case TRILL:
           dotrill_output(i);
           break;
         case ROLL:
           doroll_output(i);
           break;
         default: printf("no such decoration %d\n",decotype);
         }
      }
  }
/*dump_notestruct();*/
if (verbose> 3) printf("expand_ornaments finished\n");
 }

/* [SS] 2012-12-25 */
void fix_part_start () 
/* update part_start array in case things have moved around */
{
int i;
int partnum;
for (i=0;i<notes;i++) {
  if (feature[i] == PART) {
    /*printf("%d PART %d\n",i,pitch[i]); */
    partnum = pitch[i]-65;
    if (partnum >= 0 && partnum < 26) part_start[partnum] = i;
    }
  }
}




static void finishfile()
/* end of tune has been reached - write out MIDI file */
{
  extern int nullputc();

  complete_all_split_voices ();
  /* dump_voicecontexts(); for debugging*/
  setup_trackstructure();
  clearvoicecontexts();
  init_drum_map();
  if (!pastheader) {
    event_error("No valid K: field found at start of tune");
  } else {
    int i;

    scan_for_missing_repeats();

    if (parts > -1) {
      addfeature(PART, ' ', 0, 0);
    };
    if (headerpartlabel == 1) {
      event_error("P: field in header should go after K: field");
    };
    if (verbose > 1) {
      printf("handling grace notes\n");
    };
    dograce();
    if (barflymode) apply_bf_stress_factors (); /* [SS] 2011-08-24 */ 
    tiefix(); /* [SS] 2014-04-03 */
    if ((parts == -1) && (voicecount == 1)) {
      if (verbose > 1) {
        printf("fixing repeats\n");
      };
      fixreps();
    };

 
    expand_ornaments();
    if (parts >= 0) fix_part_start(); /* [SS] 2012-12-25 */
    if (verbose > 5) dumpfeat(0,notes);

    if (check) {
      Mf_putc = nullputc;
      header_time_num = time_num; /* [SS] 2010-05-21 */
      header_time_denom = time_denom; /* [SS] 2010-05-21 */
      if (ntracks == 1) {
        writetrack(0);
      } else {
        for (i=0; i<ntracks; i++) {
          writetrack(i);
        };
      };
    } else {
      if ((fp = fopen(outname, "wb")) == NULL) {
        event_fatal_error("File open failed");
      };
      printf("writing MIDI file %s\n", outname);
      Mf_putc = myputc;
      Mf_writetrack = writetrack;
      header_time_num = time_num;
      header_time_denom = time_denom;
      if (ntracks == 1) {
        mfwrite(0, 1, division, fp);
      } else {
        mfwrite(1, ntracks, division, fp);
      };
      fclose(fp);
#ifdef __MACINTOSH__
      (void) setOutFileCreator(outname,'Midi','ttxt');
#endif /* __MACINTOSH__ */

    };
    for (i=0; i<ntexts; i++) {
      free(atext[i]);
    };
    for (i=0; i<wcount; i++) {
      free(words[i]);
    };
    freevstring(&part);
    free_notestructs(); /* [SS] 2012-06-03 */ 
  };
}

void event_blankline()
/* blank line found in abc signifies the end of a tune */
{
  if (dotune) {
    print_voicecodes();
    finishfile();
    parseroff();
    dotune = 0;
  };
}

void event_refno(n)
/* handles an X: field (which indicates the start of a tune) */
int n;
{
  char numstr[23]; /* Big enough for a 64-bit int! */
  char newname[256];

  started_parsing = 1;
  bodystarted =0; /* [SS] 2011-01-01 */
  if (dotune) {
    finishfile();
    parseroff();
    dotune = 0;
  };
  if (verbose) {
    printf("Reference X: %d\n", n);
  };
  if ((n == xmatch) || (xmatch == 0) || (xmatch == -1)) {
    if (xmatch == -1) {
      xmatch = -2;
    };
    parseron();
    dotune = 1;
     
      v = newvoice(1);
      head = v;
      vaddr[0] = v;
      vaddr[v->indexno] = v;

    pastheader = 0;
    if (userfilename == 0) {
      if (outname != NULL) {
        free(outname);
      };
      sprintf(numstr, "%d", n);
      if ( (int) strlen(numstr) > namelimit - 1) {
        numstr[namelimit - 1] = '\0';
      };
      if ((int) (strlen(outbase) + strlen(numstr)) > namelimit) {
        strncpy(newname, outbase, namelimit - strlen(numstr));
        strcpy(&newname[namelimit - strlen(numstr)], numstr);
        strcpy(&newname[strlen(newname)], ".mid");
      } else {
        sprintf(newname, "%s%s.mid", outbase, numstr);
      };
      outname = addstring(newname);
      /* outname = (char*)checkmalloc(strlen(outbase) + 22 + strlen(".mid")); */
      /* sprintf(outname, "%s%d.mid", outbase, n); */
    };
    startfile();
  };
}

void event_eof()
/* end of abc file encountered */
{
  if (dotune) {
    dotune = 0;
    parseroff();
    finishfile();
  };
  if (verbose) {
    printf("End of File reached\n");
  };
  free(pitch);
  free(pitchline);
  free(bentpitch);
  free(num);
  free(denom);
  free(feature);
  free(words);
  free(outname);
  free(outbase);
}


int main(argc,argv)
int argc;
char *argv[];
{
  char *filename;
  int i;
  oldchordconvention = 0; /* for handling +..+ chords */

  for (i=0;i<DECSIZE;i++) decorators_passback[i]=0;
  for (i=0;i<64;i++) dependent_voice[i]=0;
  event_init(argc, argv, &filename);
  /* [SS] 2013-04-10 */
  if (csmfilename != NULL) read_custom_stress_file(csmfilename);
  if (argc < 2) {
    /* printf("argc = %d\n", argc); */
  } else {
    init_abbreviations();
    parsefile(filename);
    free_abbreviations();
  };
  return(0);
}

