/*

 MikMod Sound System

  By Jake Stine of Divine Entertainment (1996-2000)

 Support:
  If you find problems with this code, send mail to:
    air@divent.org

 Distribution / Code rights:
  Use this source code in any fashion you see fit.  Giving me credit where
  credit is due is optional, depending on your own levels of integrity and
  honesty.

 -----------------------------------------
 Module: LOAD_ABC

  ABC module loader.
	by Peter Grootswagers (2006)
	<email:pgrootswagers@planet.nl>

 Portability:
	All systems - all compilers (hopefully)
*/

#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <math.h>
#include <ctype.h>

#ifdef NEWMIKMOD
#include "mikmod.h"
#include "uniform.h"
typedef UBYTE BYTE;
typedef UWORD WORD;
#else
#include "stdafx.h"
#include "sndfile.h"
#endif

#include "load_pat.h"

#define MAXABCINCLUDES	8
#define MAXCHORDNAMES 80
#define ABC_ENV_DUMPTRACKS		"MMABC_DUMPTRACKS"
#define ABC_ENV_NORANDOMPICK	"MMABC_NO_RANDOM_PICK"

// gchords use tracks with vpos 1 thru 7
// drums use track with vpos 8
// voice chords use vpos 0 and vpos from 11 up
#define GCHORDBPOS 1
#define GCHORDFPOS 2
#define GCHORDCPOS 3
#define DRUMPOS	8
#define DRONEPOS1 9
#define DRONEPOS2 10

// in the patterns a whole note at unmodified tempo is 16 rows
#define ROWSPERNOTE 16
// a 1/64-th note played in triool equals a 1/96-th note, to be able
// to play them and also to play the 1/64-th we need a resolution of 192
// because 2/192 = 1/96 and 3/192 = 1/64
#define RESOLUTION	192

#pragma pack(1)

/**************************************************************************
**************************************************************************/
#ifdef NEWMIKMOD
static char  ABC_Version[] = "ABC+2.0 (draft IV)";
#endif

typedef enum {
	note,
	octave,
	smpno,
	volume,
	effect,
	effoper
} ABCEVENT_X_NOTE;

typedef enum {
	none,
	trill,
	bow,
	accent
} ABCEVENT_X_EFFECT;

typedef enum {
	cmdflag,
	command,
	chordnum,
	chordnote,
	chordbase,
	jumptype
} ABCEVENT_X_CMD;

typedef enum {
	cmdsegno   = '$',
	cmdcapo    = 'B',
	cmdchord   = 'C',
	cmdfine    = 'F',
	cmdhide    = 'H',
	cmdjump    = 'J',
	cmdloop    = 'L',
	cmdcoda    = 'O',
	cmdpartbrk = 'P',
	cmdsync    = 'S',
	cmdtempo   = 'T',
	cmdvariant = 'V',
	cmdtocoda  = 'X'
} ABCEVENT_CMD;

typedef enum {
	jumpnormal,
	jumpfade,
	jumpdacapo,
	jumpdcfade,
	jumpdasegno,
	jumpdsfade,
	jumpfine,
	jumptocoda,
	jumpvariant,
	jumpnot
} ABCEVENT_JUMPTYPE;

typedef struct _ABCEVENT
{
	struct _ABCEVENT *next;
	ULONG	tracktick;
	union {
		BYTE	par[6];
		struct {
			BYTE flg;
			BYTE cmd;
			ULONG lpar;	// for variant selections, bit pattern
		};
	};
	BYTE part;
	BYTE tiednote;
} ABCEVENT;

typedef struct _ABCTRACK
{
	struct _ABCTRACK *next;
	ABCEVENT *head;
	ABCEVENT *tail;
	ABCEVENT *capostart;
	ABCEVENT *tienote;
	int transpose;
	int octave_shift;
	ULONG slidevoltime;	// for crescendo and diminuendo
	int slidevol; // -2:fade away, -1:diminuendo, 0:none, +1:crescendo
	BYTE vno; // 0 is track is free for use, from previous song in multi-songbook
	BYTE vpos; // 0 is main voice, other is subtrack for gchords, gchords or drumnotes
	BYTE tiedvpos;
	BYTE mute;
	BYTE chan; // 10 is percussion channel, any other is melodic channel
	BYTE volume;
	BYTE instr;	// current instrument for this track
	BYTE legato;
	char v[22];	// first twenty characters are significant
} ABCTRACK;

typedef struct _ABCMACRO
{
	struct _ABCMACRO *next;
	char *name;
	char *subst;
	char *n;
} ABCMACRO;

/**************************************************************************
**************************************************************************/

typedef struct _ABCHANDLE
{
#ifdef NEWMIKMOD
	MM_ALLOC *allochandle;
	MM_ALLOC *macrohandle;
	MM_ALLOC *trackhandle;
	MM_ALLOC *ho;
#endif
	ABCMACRO *macro;
	ABCMACRO *umacro;
	ABCTRACK *track;
	long int pickrandom;
	unsigned int len;
	int speed;
	char *line;
	char *beatstring;
	BYTE beat[4]; // a:first note, b:strong notes, c:weak notes, n:strong note every n
	char gchord[80];	// last setting for gchord
	char drum[80]; // last setting for drum 
	char drumins[80]; // last setting for drum 
	char drumvol[80]; // last setting for drum 
	ULONG barticks;
	// parse variables, declared here to avoid parameter pollution
	int abcchordvol, abcchordprog, abcbassvol, abcbassprog;
	int ktrans;
	int drumon, gchordon, droneon;
	int dronegm, dronepitch[2], dronevol[2];
	ABCTRACK *tp, *tpc, *tpr;
	ULONG tracktime;
} ABCHANDLE;

static int global_voiceno, global_octave_shift, global_tempo_factor, global_tempo_divider;
static char global_part;
static ULONG global_songstart;
/* Named guitar chords */
static char chordname[MAXCHORDNAMES][8];
static int chordnotes[MAXCHORDNAMES][6];
static int chordlen[MAXCHORDNAMES];
static int chordsnamed = 0;

static const char *sig[] = {
	" C D EF G A Bc d ef g a b",	// 7 sharps C#
	" C D EF G AB c d ef g ab ",	// 6 sharps F#
	" C DE F G AB c de f g ab ",	// 5 sharps B
	" C DE F GA B c de f ga b ",	// 4 sharps E
	" CD E F GA B cd e f ga b ",	// 3 sharps A
	" CD E FG A B cd e fg a b ",	// 2 sharps D
	" C D E FG A Bc d e fg a b",	// 1 sharps G
	" C D EF G A Bc d ef g a b",	// 0 sharps C
	" C D EF G AB c d ef g ab ",	// 1 flats  F
	" C DE F G AB c de f g ab ",	// 2 flats  Bb
	" C DE F GA B c de f ga b ",	// 3 flats  Eb
	" CD E F GA B cd e f ga b ",	// 4 flats  Ab
	" CD E FG A B cd e fg a b ",	// 5 flats  Db
	"C D E FG A Bc d e fg a b ",	// 6 flats  Gb
	"C D EF G A Bc d ef g a b ",	// 7 flats  Cb
// 0123456789012345678901234	
};

static const char *keySigs[] = {
/* 0....:....1....:....2....:....3....:....4....:....5. */
	"7 sharps: C#    A#m   G#Mix D#Dor E#Phr F#Lyd B#Loc ",
	"6 sharps: F#    D#m   C#Mix G#Dor A#Phr BLyd  E#Loc ",
	"5 sharps: B     G#m   F#Mix C#Dor D#Phr ELyd  A#Loc ",
	"4 sharps: E     C#m   BMix  F#Dor G#Phr ALyd  D#Loc ",
	"3 sharps: A     F#m   EMix  BDor  C#Phr DLyd  G#Loc ",
	"2 sharps: D     Bm    AMix  EDor  F#Phr GLyd  C#Loc ",
	"1 sharp : G     Em    DMix  ADor  BPhr  CLyd  F#Loc ",
	"0 sharps: C     Am    GMix  DDor  EPhr  FLyd  BLoc  ",
	"1 flat  : F     Dm    CMix  GDor  APhr  BbLyd ELoc  ",
	"2 flats : Bb    Gm    FMix  CDor  DPhr  EbLyd ALoc  ",
	"3 flats : Eb    Cm    BbMix FDor  GPhr  AbLyd DLoc  ",
	"4 flats : Ab    Fm    EbMix BbDor CPhr  DbLyd GLoc  ",
	"5 flats : Db    Bbm   AbMix EbDor FPhr  GbLyd CLoc  ",
	"6 flats : Gb    Ebm   DbMix AbDor BbPhr CbLyd FLoc  ",
	"7 flats : Cb    Abm   GbMix DbDor EbPhr FbLyd BbLoc ",
	0
};

// local prototypes
static int abc_getnumber(const char *p, int *number);
static ABCTRACK *abc_locate_track(ABCHANDLE *h, const char *voice, int pos);
static void	abc_add_event(ABCHANDLE *h, ABCTRACK *tp, ABCEVENT *e);
static void abc_add_setloop(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime);
static void abc_add_setjumploop(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime, ABCEVENT_JUMPTYPE j);
static ULONG abc_pattracktime(ABCHANDLE *h, ULONG tracktime);
static int abc_patno(ABCHANDLE *h, ULONG tracktime);

static void abc_message(const char *s1, const char *s2)
{
	char txt[256];
	if( strlen(s1) + strlen(s2) > 255 ) return;
	sprintf(txt, s1, s2);
#ifdef NEWMIKMOD
	_mmlog(txt);
#else
	fprintf(stderr, "load_abc > %s\n", txt);
#endif
}

static ULONG modticks(ULONG abcticks)
{
	return abcticks / RESOLUTION;
}

static ULONG abcticks(ULONG modticks)
{
	return modticks * RESOLUTION;
}

static ULONG notelen_notediv_to_ticks(int speed, int len, int div)
{
	ULONG u;
	u = (ROWSPERNOTE * RESOLUTION * speed * len * global_tempo_factor) / (div * global_tempo_divider);
	return u;
}

static void abc_dumptracks(ABCHANDLE *h, const char *p)
{
	ABCTRACK *t;
	ABCEVENT *e;
	int n,pat,row,tck;
	char nn[3];
	if( !h ) return;
	for( t=h->track; t; t=t->next ) {
		printf("track %d.%d chan=%d %s\n", (int)(t->vno), (int)(t->vpos),
		                                   (int)(t->chan), (char *)(t->v));
		if( strcmp(p,"nonotes") )
			n = 1;
		else
			n = 0;
		for( e=t->head; e; e=e->next ) {
			tck = modticks(e->tracktick);
			row = tck / h->speed;
			pat = row / 64;
			tck = tck % h->speed;
			row = row % 64; 
			nn[0] = ( e->tracktick % abcticks(h->speed * 64) ) ? ' ': '-';
			if( e->flg == 1 ) {
				printf("  %6d.%02d.%d%c%c %d.%d %s ",
				       pat, row, tck, nn[0], (int)(e->part), (int)(t->vno),
				       (int)(t->vpos), (char *)(t->v));
				if( e->cmd == cmdchord ) {
					nn[0] = "CCCDDEFFGGAABccddeffggaabb"[e->par[chordnote]];
					nn[1] = "b # #  # # #  # #  # # # #"[e->par[chordnote]];
					nn[2] = '\0';
					if( isspace(nn[1]) ) nn[1] = '\0';
					printf("CMD %c: gchord %s%s",
					       (char)(e->cmd), nn, chordname[e->par[chordnum]]);
					if( e->par[chordbase] != e->par[chordnote] ) {
						nn[0] = "CCCDDEFFGGAABccddeffggaabb"[e->par[chordbase]];
						nn[1] = "b # #  # # #  # #  # # # #"[e->par[chordbase]];
						nn[2] = '\0';
						printf("/%s", nn);
					}
					printf("\n");
				}
				else
					printf("CMD %c @%p 0x%08lX\n",
					       (char)(e->cmd), e,
					       (unsigned long)(e->lpar));
				if( strcmp(p,"nonotes") )
					n = 1;
				else
					n = 0;
			}
			else if( n ) {
				printf("  %6d.%02d.%d%c%c %d.%d %s ", pat, row, tck, nn[0], e->part, t->vno, t->vpos, t->v);
				if( e->par[note] ) {
					nn[0] = "CCCDDEFFGGAABccddeffggaabb"[e->par[note]-23];
					nn[1] = "b # #  # # #  # #  # # # #"[e->par[note]-23];
					nn[2] = '\0';
				}
				else strcpy(nn,"--");
				printf("NOTE %s octave %d inst %s vol %03d\n", 
					nn, e->par[octave], pat_gm_name(pat_smptogm(e->par[smpno])),e->par[volume]);
				if( strcmp(p,"all") )
					n = 0;
			}
		}
	}
}

#ifdef NEWMIKMOD

#define MMFILE				MMSTREAM
#define mmfgetc(x)			_mm_read_SBYTE(x)
#define mmfeof(x)			_mm_feof(x)
#define mmfgets(buf,sz,f) _mm_fgets(f,buf,sz)
#define mmftell(x)			_mm_ftell(x)
#define mmfseek(f,p,w)		_mm_fseek(f,p,w)
#define mmfopen(s,m)			_mm_fopen(s,m)
#define mmfclose(f)			_mm_fclose(f)

#else

#define MMSTREAM										FILE
#define _mm_fopen(name,mode)		fopen(name,mode)
#define _mm_fgets(f,buf,sz)		fgets(buf,sz,f)
#define _mm_fseek(f,pos,whence)		fseek(f,pos,whence)
#define _mm_ftell(f)								ftell(f)
#define _mm_read_UBYTES(buf,sz,f)	fread(buf,sz,1,f)
#define _mm_read_SBYTES(buf,sz,f)	fread(buf,sz,1,f)
#define _mm_feof(f)									feof(f)
#define _mm_fclose(f)								fclose(f)
#define DupStr(h,buf,sz)		strdup(buf)
#define _mm_calloc(h,n,sz)		calloc(n,sz)
#define _mm_recalloc(h,buf,sz,elsz)	realloc(buf,sz)
#define _mm_free(h,p)								free(p)

typedef struct {
	char *mm;
	int sz;
	int pos;
} MMFILE;

static MMFILE *mmfopen(const char *name, const char *mode)
{
	FILE *fp;
	MMFILE *mmfile;
	long len;
	if( *mode != 'r' ) return NULL;
	fp = fopen(name, mode);
	if( !fp ) return NULL;
	fseek(fp, 0, SEEK_END);
	len = ftell(fp);
	mmfile = (MMFILE *)malloc(len+sizeof(MMFILE));
	if( !mmfile ) return NULL;
	fseek(fp, 0, SEEK_SET);
	fread(&mmfile[1],1,len,fp);
	fclose(fp);
	mmfile->mm = (char *)&mmfile[1];
	mmfile->sz = len;
	mmfile->pos = 0;
	return mmfile;
}

static void mmfclose(MMFILE *mmfile)
{
	free(mmfile);
}

static bool mmfeof(MMFILE *mmfile)
{
	if( mmfile->pos < 0 ) return TRUE;
	if( mmfile->pos < mmfile->sz ) return FALSE;
	return TRUE;
}

static int mmfgetc(MMFILE *mmfile)
{
	int b;
	if( mmfeof(mmfile) ) return EOF;
	b = mmfile->mm[mmfile->pos];
	mmfile->pos++;
	if( b=='\r' && mmfile->mm[mmfile->pos] == '\n' ) {
		b = '\n';
		mmfile->pos++;
	}
	return b;
}

static void mmfgets(char buf[], unsigned int bufsz, MMFILE *mmfile)
{
	int i,b;
	for( i=0; i<(int)bufsz-1; i++ ) {
		b = mmfgetc(mmfile);
		if( b==EOF ) break;
		buf[i] = b;
		if( b == '\n' ) break;
	}
	buf[i] = '\0';
}

static long mmftell(MMFILE *mmfile)
{
	return mmfile->pos;
}

static void mmfseek(MMFILE *mmfile, long p, int whence)
{
	switch(whence) {
		case SEEK_SET:
			mmfile->pos = p;
			break;
		case SEEK_CUR:
			mmfile->pos += p;
			break;
		case SEEK_END:
			mmfile->pos = mmfile->sz + p;
			break;
	}
}
#endif

// =====================================================================================
static ABCEVENT *abc_new_event(ABCHANDLE *h, ULONG abctick, const char data[])
// =====================================================================================
{
    ABCEVENT   *retval;
		int i;

    retval = (ABCEVENT *)_mm_calloc(h->trackhandle, 1,sizeof(ABCEVENT));
		retval->next        = NULL;
    retval->tracktick   = abctick;
		for( i=0; i<6; i++ )
	    retval->par[i]    = data[i];
		retval->part = global_part;
		retval->tiednote = 0;
    return retval;
}

// =============================================================================
static ABCEVENT *abc_copy_event(ABCHANDLE *h, ABCEVENT *se)
// =============================================================================
{
	ABCEVENT *e;
	e = (ABCEVENT *)_mm_calloc(h->trackhandle, 1,sizeof(ABCEVENT));
	e->next        = NULL;
	e->tracktick   = se->tracktick;
	e->flg         = se->flg;
	e->cmd         = se->cmd;
	e->lpar        = se->lpar;
	e->part        = se->part;
	return e;
}

// =============================================================================
static void abc_new_macro(ABCHANDLE *h, const char *m)
// =============================================================================
{
    ABCMACRO *retval;
		const char *p;
		char buf[256],*q;
		for( p=m; *p && isspace(*p); p++ ) ;
		for( q=buf; *p && *p != '='; p++ )
			*q++ = *p;
		if( q != buf )
			while( isspace(q[-1]) ) q--;
		*q = '\0';
    retval = (ABCMACRO *)_mm_calloc(h->macrohandle, 1,sizeof(ABCTRACK));
    retval->name  = DupStr(h->macrohandle, buf,strlen(buf));
		retval->n     = strrchr(retval->name, 'n'); // for transposing macro's
		for( p++; *p && isspace(*p); p++ ) ;
		strncpy(buf,p,200);
		for( q=&buf[strlen(buf)-1]; q!=buf && isspace(*q); q-- ) *q = '\0';
    retval->subst = DupStr(h->macrohandle, buf, strlen(buf));
		retval->next  = h->macro;
		h->macro      = retval;
}

// =============================================================================
static void abc_new_umacro(ABCHANDLE *h, const char *m)
// =============================================================================
{
    ABCMACRO *retval, *mp;
		const char *p;
		char buf[256], let[2], *q;
		for( p=m; *p && isspace(*p); p++ ) ;
		for( q=buf; *p && *p != '='; p++ )
			*q++ = *p;
		if( q != buf )
			while( isspace(q[-1]) ) q--;
		*q = '\0';
		if( strlen(buf) > 1 || strchr("~HIJKLMNOPQRSTUVWXY",toupper(buf[0])) == 0 || strchr("xy",buf[0]) ) return;
		strcpy(let,buf);
		for( p++; *p && isspace(*p); p++ ) ;
		strncpy(buf,p,200);
		for( q=&buf[strlen(buf)-1]; q!=buf && isspace(*q); q-- ) *q = '\0';
		for( q=buf; *q; q++ )	if( *q == '!' ) *q = '+';	// translate oldstyle to newstyle
		if( !strcmp(buf,"+nil+") ) { // delete a macro
			mp = NULL;
			for( retval=h->umacro; retval; retval = retval->next ) {
				if( retval->name[0] == let[0] ) {	// delete this one
					if( mp ) mp->next = retval->next;
					else h->umacro = retval->next;
					_mm_free(h->macrohandle, retval);
					return;
				}
				mp = retval;
			}
			return;
		}
    retval = (ABCMACRO *)_mm_calloc(h->macrohandle, 1,sizeof(ABCTRACK));
    retval->name  = DupStr(h->macrohandle, let,1);
    retval->subst = DupStr(h->macrohandle, buf, strlen(buf));
		retval->n     = 0;
		retval->next  = h->umacro; // by placing it up front we mask out the old macro until we +nil+ it
		h->umacro      = retval;
}

// =============================================================================
static ABCTRACK *abc_new_track(ABCHANDLE *h, const char *voice, int pos)
// =============================================================================
{
	ABCTRACK *retval;
	if( !pos ) global_voiceno++;
	retval = (ABCTRACK *)_mm_calloc(h->trackhandle, 1,sizeof(ABCTRACK));
	retval->next         = NULL;
	retval->vno          = global_voiceno;
	retval->vpos         = pos;
	retval->tiedvpos     = pos;
	retval->instr        = 1;
	strncpy(retval->v, voice, 20);
	retval->v[20]        = '\0';
	retval->head         = NULL;
	retval->tail         = NULL;
	retval->capostart    = NULL;
	retval->tienote      = NULL;
	retval->mute         = 0;
	retval->chan         = 0;
	retval->transpose    = 0;
	retval->volume       = h->track? h->track->volume: 120;
	retval->slidevoltime = 0;
	retval->slidevol     = 0;
	retval->legato       = 0;
	return retval;
}

static int abc_numtracks(ABCHANDLE *h)
{
	int n;
	ABCTRACK *t;
	n=0;
	for( t = h->track; t; t=t->next )
		n++;
	return n;
}

static int abc_interval(const char *s, const char *d)
{
	const char *p;
	int i,j,k;
	int n,oct,m[2];
	for( j=0; j<2; j++ ) {
		if( j ) p = d; 
		else p = s;
		switch(p[0]) {
			case '^':
				n = p[1];
				i = 2;
				break;
			case '_':
				n = p[1];
				i = 2;
				break;
			case '=':
				n = p[1];
				i = 2;
				break;
			default:
				n = p[0];
				i = 1;
				break;
		}
		for( k=0; k<25; k++ )
			if( n == sig[7][k] )
				break;
		oct = 4;	// ABC note pitch C is C4 and pitch c is C5
		if( k > 12 ) {
			oct++;
			k -= 12;
		}
		while( p[i] == ',' || p[i] == '\'' ) {
			if( p[i] == ',' )
				oct--;
			else
				oct++;
			i++;
		}
		m[j] = k + 12 * oct;
	}
	return m[0] - m[1];
}

static int abc_transpose(const char *v)
{
	int i,j,t;
	const char *m = "B", *mv = "";
	t = 0;
	global_octave_shift = 99;
	for( ; *v && *v != ']'; v++ ) {
		if( !strncasecmp(v,"t=",2) ) {
			v+=2;
			if( *v=='-' )	{
				j = -1;
				v++;
			}
			else j = 1;
			v+=abc_getnumber(v,&i);
			t += i * j;
			global_octave_shift = 0;
		}
		if( !strncasecmp(v,"octave=",7) ) {
			v+=7;
			if( *v=='-' )	{
				j = -1;
				v++;
			}
			else j = 1;
			v+=abc_getnumber(v,&i);
			t += i * j * 12;
			global_octave_shift = 0;
		}
		if( !strncasecmp(v,"transpose=",10) ) {
			v+=10;
			if( *v=='-' )	{
				j = -1;
				v++;
			}
			else j = 1;
			v+=abc_getnumber(v,&i);
			t += i * j;
			global_octave_shift = 0;
		}
		if( !strncasecmp(v,"octave=",7) ) { // used in kv304*.abc
			v+=7;
			if( *v=='-' )	{
				j = -1;
				v++;
			}
			else j = 1;
			v+=abc_getnumber(v,&i);
			t += i * j * 12;
			global_octave_shift = 0;
		}
		if( !strncasecmp(v,"m=",2) ) {
			v += 2;
			mv = v; // get the pitch for the middle staff line 
			while( *v && *v != ' ' && *v != ']' ) v++;
			global_octave_shift = 0;
		}
		if( !strncasecmp(v,"middle=",7) ) {
			v += 7;
			mv = v; // get the pitch for the middle staff line 
			while( *v && *v != ' ' && *v != ']' ) v++;
			global_octave_shift = 0;
		}
		if( !strncasecmp(v,"clef=",5) )
			v += 5;
		j = 1;
		if( !strncasecmp(v,"treble",6) ) {
			j = 0;
			v += 6;
			switch( *v ) {
				case '1':	v++; m = "d";	break;
				case '2': v++;
				default: m = "B";	break;
				case '3': v++; m = "G";	break;
				case '4': v++; m = "E";	break;
				case '5': v++; m = "C";	break;
			}
			global_octave_shift = 0;
		}
		if( j && !strncasecmp(v,"bass",4) ) {
			m = "D,";
			j = 0;
			v += 4;
			switch( *v ) {
				case '1':	v++; m = "C";	break;
				case '2': v++; m = "A,";	break;
				case '3': v++; m = "F,";	break;
				case '4': v++;
				default: m = "D,";	break;
				case '5': v++; m = "B,,";	break;
			}
			if( global_octave_shift == 99 )
				global_octave_shift = -2;
		}
		if( j && !strncasecmp(v,"tenor",5) ) {
			j = 0;
			v += 5;
			switch( *v ) {
				case '1':	v++; m = "G";	break;
				case '2': v++; m = "E";	break;
				case '3': v++; m = "C";	break;
				case '4': v++;
				default: m = "A,";	break;
				case '5': v++; m = "F,";	break;
			}
			if( global_octave_shift == 99 )
				global_octave_shift = 1;
		}
		if( j && !strncasecmp(v,"alto",4) ) {
			j = 0;
			v += 4;
			switch( *v ) {
				case '1':	v++; m = "G";	break;
				case '2': v++; m = "E";	break;
				case '3': v++; 
				default: m = "C";	break;
				case '4': v++; m = "A,";	break;
				case '5': v++; m = "F,";	break;
			}
			if( global_octave_shift == 99 )
				global_octave_shift = 1;
		}
		if( j && strchr("+-",*v) && *v && v[1]=='8' ) {
			switch(*v) {
				case '+':
					t += 12;
					break;
				case '-':
					t -= 12;
					break;
			}
			v += 2;
			if( !strncasecmp(v,"va",2) ) v += 2;
			global_octave_shift = 0;
			j = 0;
		}
		if( j ) {
			while( *v && *v != ' ' && *v != ']' ) v++;
		}
	}
	if( strlen(mv) > 0 ) // someone set the middle note
		t += abc_interval(mv, m);
	if( global_octave_shift == 99 )
		global_octave_shift = 0;
	return t;
}

// =============================================================================
static ABCTRACK *abc_locate_track(ABCHANDLE *h, const char *voice, int pos)
// =============================================================================
{
	ABCTRACK *tr, *prev, *trunused;
	char vc[21];
	int i, trans=0, voiceno=0, instrno = 1, channo = 0;
	for( ; *voice == ' '; voice++ ) ;	// skip leading spaces
	for( i=0; *voice && *voice != ']' && *voice != '%' && !isspace(*voice); voice++ )	// can work with inline voice instructions
		vc[i++] = *voice;
	vc[i] = '\0';
	prev = NULL;
	trunused = NULL;
	if( !pos )	trans = abc_transpose(voice);
	for( tr=h->track; tr; tr=tr->next ) {
		if( tr->vno == 0 ) {
			if( !trunused ) trunused = tr; // must reuse mastertrack (h->track) as first
		}
		else {
			if( !strncasecmp(tr->v, vc, 20) ) {
				if( tr->vpos == pos ) 
					return tr;
				trans = tr->transpose;
				global_octave_shift = tr->octave_shift;
				voiceno = tr->vno;
				instrno = tr->instr;
				channo  = tr->chan;
			}
		}
		prev = tr;
	}
	if( trunused ) {
		tr = trunused;
		if( pos ) {
			tr->vno   = voiceno;
			tr->instr = instrno;
			tr->chan  = channo;
		}
		else {
			global_voiceno++;
	    tr->vno   = global_voiceno;
			tr->instr = 1;
			tr->chan  = 0;
		}
    tr->vpos         = pos;
		tr->tiedvpos     = pos;
		strncpy(tr->v, vc, 20);
		tr->v[20]        = '\0';
		tr->mute         = 0;
		tr->transpose    = trans;
		tr->octave_shift = global_octave_shift;
		tr->volume       = h->track->volume;
		tr->tienote      = NULL;
		tr->legato       = 0;
		return tr;
	}
	tr = abc_new_track(h, vc, pos);
	if( pos ) {
		tr->vno   = voiceno;
		tr->instr = instrno;
		tr->chan  = channo;
	}
	tr->transpose    = trans;
	tr->octave_shift = global_octave_shift;
	if( prev ) prev->next = tr;
	else	h->track = tr;
	return tr;
}

// =============================================================================
static ABCTRACK *abc_check_track(ABCHANDLE *h, ABCTRACK *tp)
// =============================================================================
{
	if( !tp ) { 
		tp = abc_locate_track(h, "", 0);	// must work for voiceless abc too...
		tp->transpose = h->ktrans;
	}
	return tp;
}

static void abc_add_capo(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime)
{
	ABCEVENT *e;
	char d[6];
	d[0] = d[1] =	d[2] = d[3] = d[4] = d[5] = 0;
	d[cmdflag] = 1;
	d[command] = cmdcapo;
	e = abc_new_event(h, tracktime, d);
	tp->capostart = e;
	abc_add_event(h, tp, e); // do this last (recursion danger)
}

static void abc_add_segno(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime)
{
	ABCEVENT *e;
	char d[6];
	d[0] = d[1] =	d[2] = d[3] = d[4] = d[5] = 0;
	d[cmdflag] = 1;
	d[command] = cmdsegno;
	e = abc_new_event(h, tracktime, d);
	abc_add_event(h, tp, e);
}

static void abc_add_coda(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime)
{
	ABCEVENT *e;
	char d[6];
	d[0] = d[1] =	d[2] = d[3] = d[4] = d[5] = 0;
	d[cmdflag] = 1;
	d[command] = cmdcoda;
	e = abc_new_event(h, tracktime, d);
	abc_add_event(h, tp, e);
}

static void abc_add_fine(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime)
{
	ABCEVENT *e;
	char d[6];
	d[0] = d[1] =	d[2] = d[3] = d[4] = d[5] = 0;
	d[cmdflag] = 1;
	d[command] = cmdfine;
	e = abc_new_event(h, tracktime, d);
	abc_add_event(h, tp, e);
}

static void abc_add_tocoda(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime)
{
	ABCEVENT *e;
	char d[6];
	d[0] = d[1] =	d[2] = d[3] = d[4] = d[5] = 0;
	d[cmdflag] = 1;
	d[command] = cmdtocoda;
	e = abc_new_event(h, tracktime, d);
	abc_add_event(h, tp, e);
}

// first track is dirigent, remove all control events from other tracks
// to keep the information where the events should be relative to note events
// in the same tick the ticks are octated and four added for note events
// the control events that come before the note events get a decremented tick,
// those that come after get an incremented tick, for example:
//             ctrl ctrl note ctrl ctrl  note
//   original: t    t    t    t    t+1   t+1
//   recoded:  8t+1 8t+2 8t+4 8t+5 8t+11 8t+12
static void abc_remove_unnecessary_events(ABCHANDLE *h)
{
	ABCTRACK *tp,*ptp;
	ABCEVENT *ep, *el;
	ULONG ct, et;
	int d;
	ptp = NULL;
	for( tp=h->track; tp; tp=tp->next ) {
		el = NULL;
		ep = tp->head;
		ct = 0;
		d  = -3;
		while( ep ) {
			et = ep->tracktick;
			ep->tracktick <<= 3;
			ep->tracktick += 4;
			if( ep->flg == 1 ) {
				ep->tracktick += d;	
				d++;
				if( d == 0 ) d = -1;
				if( d == 4 ) d =  3;
				if( tp!=h->track ) ep->cmd = cmdhide;
				switch( ep->cmd ) {
					case cmdhide:
					case cmdsync:
						if( el ) {
							el->next = ep->next;
							_mm_free(h->trackhandle,ep);
							ep = el->next;
						}
						else {
							tp->head = ep->next;
							_mm_free(h->trackhandle,ep);
							ep = tp->head;
						}
						break;
					default:
						el = ep;
						ep = ep->next;
						break;
				}
			}
			else {
				el = ep;
				ep = ep->next;
				d  = 1;
			}
			if( et > ct )
				d = -3;
			ct = et;
		}
		if( !tp->head ) { // no need to keep empty tracks...
			if( ptp ) {
				ptp->next = tp->next;
				_mm_free(h->trackhandle,tp);
				tp = ptp;
			}
			else {
				h->track = tp->next;
				_mm_free(h->trackhandle,tp);
				tp = h->track;
			}
		}
		ptp = tp;	// remember previous track
	}
}

// set ticks back, and handle partbreaks
static void abc_retick_events(ABCHANDLE *h)
{
	ABCTRACK *tp;
	ABCEVENT *ep;
	ULONG et, tt=0, at = abcticks(64 * h->speed);
	for( tp=h->track; tp; tp=tp->next ) {
		// make ticks relative
		tt = 0;
		for( ep=tp->head; ep; ep=ep->next ) {
			et = ep->tracktick >> 3;
			ep->tracktick = et - tt;
			tt = et;
		}
		// make ticks absolute again, skipping no-op partbreaks
		tt = 0;
		for( ep=tp->head; ep; ep=ep->next ) {
			ep->tracktick += tt;
			tt = ep->tracktick;
			if( ep->flg == 1 && ep->cmd == cmdpartbrk ) {
				if( tt % at ) {
					tt += at;
					tt /= at;
					tt *= at;
					ep->tracktick -= abcticks(h->speed); // break plays current row
				}
				else ep->cmd = cmdhide;
			}
		}
	}
}

// make sure every track has the control events it needs, this way it is not
// necessary to have redundant +segno+ +D.C.+ etc in the voices, the first voice
// is the master, it is pointed to by the member 'track' in the ABCHANDLE
static void abc_synchronise_tracks(ABCHANDLE *h)
{
	ABCTRACK *tp;
	ULONG tm;	// tracktime in master
	ABCEVENT *em, *es, *et, *ec;	// events in master, slave, slave temporary and copied event
	if( !h || !h->track ) return;
	abc_remove_unnecessary_events(h);
	for( tp = h->track->next; tp; tp = tp->next ) {
		for( em=h->track->head; em; em=em->next ) {
			if( em->flg == 1 ) { // some kind of control event
				switch( em->cmd ) {
					case cmdchord:
					case cmdhide:
					case cmdtempo:
					case cmdsync:
						break;
					default:	// check to see if copy is necessary
						ec = abc_copy_event(h, em);
						tm = em->tracktick;
						es = tp->head; // allways search from the begin...
						for( et=es; et && et->tracktick <= tm; et=et->next )
							es = et;
						if( es == NULL || es->tracktick > tm ) {	// special case: head of track
							ec->next = es;
							tp->head = ec;
						}
						else {
							ec->next = es->next;
							es->next = ec;
						}
						break;
				}
			}
		}
	}
	abc_retick_events(h);
}

static void	abc_add_event(ABCHANDLE *h, ABCTRACK *tp, ABCEVENT *e)
{
	if( !tp->capostart ) abc_add_capo(h, tp, global_songstart);
	if( tp->tail ) {
		tp->tail->next = e;
		tp->tail = e;
	}
	else {
		tp->head = e;
		tp->tail = e;
	}
}

static void abc_add_partbreak(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime)
{
	ABCEVENT *e;
	char d[6];
	d[0] = d[1] =	d[2] = d[3] = d[4] = d[5] = 0;
	d[cmdflag] = 1;
	d[command] = cmdpartbrk;
	e = abc_new_event(h, tracktime, d);
	abc_add_event(h, tp, e);
}

static void abc_add_tempo_event(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime, int tempo)
{
	ABCEVENT *e;
	char d[6];
	d[0] = d[1] =	d[2] = d[3] = d[4] = d[5] = 0;
	d[cmdflag] = 1;
	d[command] = cmdtempo;
	e = abc_new_event(h, tracktime, d);
	e->lpar = tempo;
	abc_add_event(h, tp, e);
}

static void abc_add_noteoff(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime)
{
	ABCEVENT *e;
	char d[6];
	d[note]    = 0;
	d[octave]  = 0;
	d[smpno]   = pat_gmtosmp(tp->instr);
	d[volume]  = 0;
	d[effect]  = 0;
	d[effoper] = 0;
	e = abc_new_event(h, tracktime, d);
	abc_add_event(h, tp, e);
}

static int abc_dynamic_volume(ABCTRACK *tp, ULONG tracktime, int vol)
{
	ULONG slidetime;
	int voldelta;
	if( tp->mute ) return 0;
	if( tp->slidevol == 0 ) return vol;
	if( tracktime < tp->slidevoltime ) return vol;
	slidetime = modticks(tracktime - tp->slidevoltime);
	voldelta = (slidetime * 15) / 64 / 6;	// slide from say mf up to f in one pattern's time
	if( tp->slidevol > -2 && voldelta > 15 ) voldelta = 15;	// never to much dynamics
	if( tp->slidevol > 0 ) vol += voldelta;
	else vol -= voldelta;
	if( vol < 2 ) vol = 2; // xmms divides this by 2....
	if( vol > 127 ) vol = 127;
	return vol;
}

static void abc_track_untie_short_chordnotes(ABCHANDLE *h)
{
	ABCTRACK *tp;
	int vn;
	tp = h->tp;
	vn = tp->vno;
	for( tp = h->track; tp; tp = tp->next )
		if( tp != h->tp && tp->vno == vn && tp->tienote ) {
			abc_message("short notes in chord can not be tied:\n%s", h->line);
			tp->tienote = 0;
		}
}

static void abc_track_clear_tiednote(ABCHANDLE *h)
{
	ABCTRACK *tp;
	int vn;
	tp = h->tp;
	vn = tp->vno;
	for( tp = h->track; tp; tp = tp->next )
		if( tp->vno == vn ) tp->tienote = 0;
}

static void abc_track_clear_tiedvpos(ABCHANDLE *h)
{
	ABCTRACK *tp;
	int vn;
	tp = h->tp;
	vn = tp->vno;
	for( tp = h->track; tp; tp = tp->next )
		if( tp->vno == vn ) tp->tiedvpos = tp->vpos;
}

static ABCTRACK *abc_track_with_note_tied(ABCHANDLE *h, ULONG tracktime, int n, int oct)
{
	int vn, vp;
	ABCTRACK *tp;
	ABCEVENT *e;
	tp = h->tp;
	vn = tp->vno;
	vp = tp->vpos;
	for( tp = h->track; tp; tp = tp->next ) {
		if( tp->vno == vn ) {
			e = tp->tienote;
			if( e && e->tracktick < tracktime
			&& e->par[octave] == oct && abs(e->par[note] - n) < 3 ) {
				if( tp->vpos != vp ) tp->tiedvpos = vp;
				h->tp = tp;
				return tp;
			}
		}
	}
	tp = h->tp;
	vp = tp->tiedvpos;
	if( tp->vpos != vp ) {
		// chord note track allready returned in previous call
		for( tp = h->track; tp; tp = tp->next ) {
			if( tp->vno == vn && tp->vpos == vp ) {
				tp->tiedvpos = h->tp->vpos;
				h->tp = tp;
				return tp;
			}
		}
	}
	return h->tp;
}

static int abc_add_noteon(ABCHANDLE *h, int ch, const char *p, ULONG tracktime, char *barkey, int vol, ABCEVENT_X_EFFECT fx, int fxop)
{
	ABCEVENT *e;
	ABCTRACK *tp;
	int i,j,k;
	int n,oct;
	char d[6];
	tp = h->tp;
	switch(ch) {
		case '^':
			if( p[0] == '^' ) {
				n = p[1];
				i = 2;
				ch = 'x';
			}
			else {
				n = p[0];
				i = 1;
			}
			break;
		case '_':
			if( p[0] == '_' ) {
				n = p[1];
				i = 2;
				ch = 'b';
			}
			else {
				n = p[0];
				i = 1;
			}
			break;
		case '=':
			n = p[0];
			i = 1;
			break;
		default:
			n = ch;
			i = 0;
			break;
	}
	for( k=0; k<51; k++ ) {
		if( n == barkey[k] )
			break;
	}
	j = k;
	if( k > 24 )
		k -= 25; // had something like A# over Bb key F signature....
	if( i ) {
		// propagate accidentals if necessary
		// DON'T do redundant accidentals they're always relative to C-scale
		for( k=0; k<25; k++ ) {
			if( n == sig[7][k] )
				break;
		}
		if( k < 25 ) { // only do real notes...
		switch(ch) {
			case 'x':
				k++;
			case '^':
				k++;
				break;
			case 'b':
				k--;
			case '_':
				k--;
				break;
			case '=':
						break;
				}
			if( j < 25 ) // was it not A# over Bb?
			barkey[j] = ' ';
			barkey[k] = n;
		}
	}
	oct = 3;	// ABC note pitch C is C4 and pitch c is C5
	if( k < 25 ) {
		k += tp->transpose;
		while( k > 12 ) {
			oct++;
			k -= 12;
		}
		while( k < 0 ) {
			oct--;
			k += 12;
		}
		d[note] = 23 + k;	// C0 is midi notenumber 24
	}
	else
		d[note] = 0; // someone has doen ^X3 or something like it...
	while( p[i] && strchr(",'",p[i]) ) {
		if( p[i]==',' ) oct--;
		else oct++;
		i++;
		tp->octave_shift = 0;	// forget we ever had to look at it
	}
	if( tp->octave_shift )
		tp->transpose += 12 * tp->octave_shift;
	oct += tp->octave_shift;
	tp->octave_shift = 0;	// after the first note we never have to look at it again
	if( oct < 0 ) oct = 0;
	if( oct > 9 ) oct = 9;
	d[octave]  = oct;
	d[smpno]   = pat_gmtosmp(tp->instr);
	d[volume]  = abc_dynamic_volume(tp, tracktime, vol);
	d[effect]  = fx; // effect
	d[effoper] = fxop;
	tp = abc_track_with_note_tied(h, tracktime, d[note], oct);
	if( tp->tienote ) {
		if( tp->tienote->par[note] != d[note] ) {
			if( abs(tp->tienote->par[note] - d[note]) < 3 ) {
				// may be tied over bar symbol, recover local accidental to barkey
				k = tp->tienote->par[note] - 23 - tp->transpose;
				while( k < 0 ) k += 12;
				while( k > 12 ) k -= 12;
				if( (isupper(n) && barkey[k+12] == ' ') || (islower(n) && barkey[k] == ' ') ) {
					barkey[j] = ' ';
					if( isupper(n) )
						barkey[k] = n;
					else
						barkey[k+12] = n;
					d[note]   = tp->tienote->par[note];
					d[octave] = tp->tienote->par[octave];
				}
			}
		}
	}
	if( tp->tienote
	&& tp->tienote->par[note]   == d[note]
	&& tp->tienote->par[octave] == d[octave] ) {
		for( e = tp->tienote; e; e = e->next ) {
			if( e->par[note] == 0 && e->par[octave] == 0 ) {	// undo noteoff
				e->flg = 1;
				e->cmd = cmdhide;
				e->lpar = 0;
				break;
			}
		}
		tp->tienote->tiednote = 1; // mark him for the pattern writers
		for( j=i; isdigit(p[j]) || p[j]=='/'; j++ ) ; // look ahead to see if this one is tied too
		if( p[j] != '-' ) // is this note tied too?
			tp->tienote = NULL; // if not the tie ends here...
		return i;
	}
	tp->tienote = NULL;
	if( tp->tail 
	&& tp->tail->tracktick == tracktime
	&& tp->tail->par[note]      == 0
	&& tp->tail->par[octave]    == 0 ) {
		for( j=0; j<6; j++ )
	    tp->tail->par[j]   = d[j];
	}
	else {
		e = abc_new_event(h, tracktime, d);
		abc_add_event(h, tp, e);
	}
	if( i > 0 && p[i-1] == '"' ) {
		i--; // someone coded a weird note like ^"E"
		abc_message("strange note encountered scanning %s", h->line);
	}
	return i;
}

static void abc_add_dronenote(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime, int nnum, int vol)
{
	ABCEVENT *e;
	int j,k;
	int oct;
	char d[6];
	oct = -1;	// ABC note pitch C is C4 and pitch c is C5
	k = nnum + 1;
	while( k > 12 ) {
		oct++;
		k -= 12;
	}
	while( k < 0 ) {
		oct--;
		k += 12;
	}
	if( oct < 0 ) oct = 0;
	d[note] = 23 + k;	// C0 is midi notenumber 24
	d[octave]  = oct;
	d[smpno]   = pat_gmtosmp(tp->instr);
	d[volume]  = abc_dynamic_volume(tp, tracktime, vol);
	d[effect]  = 0; // effect
	d[effoper] = 0;
	if( tp->tail 
	&& tp->tail->tracktick == tracktime
	&& tp->tail->par[note]      == 0
	&& tp->tail->par[octave]    == 0 ) {
		for( j=0; j<6; j++ )
	    tp->tail->par[j]   = d[j];
	}
	else {
		e = abc_new_event(h, tracktime, d);
		abc_add_event(h, tp, e);
	}
}

static void abc_add_chordnote(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime, int nnum, int vol)
{
	abc_add_dronenote(h, tp, tracktime, nnum + 23, tp->mute? 0: vol);
}

static void abc_add_drumnote(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime, int nnum, int vol)
{
	abc_add_dronenote(h, tp, tracktime, nnum, tp->mute? 0: vol);
}

static void abc_add_variant_start(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime, int n)
{
	ABCEVENT *e;
	char d[6];
	d[0] = d[1] =	d[2] = d[3] = d[4] = d[5] = 0;
	d[cmdflag] = 1;
	d[command] = cmdvariant;
	e = abc_new_event(h, tracktime, d);
	e->lpar = 1<<n;
	abc_add_event(h, tp, e);
}

static void abc_add_variant_choise(ABCTRACK *tp, int n)
{
  tp->tail->lpar |= 1<<n;
}

static void	abc_add_chord(const char *p, ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime)
{
	ABCEVENT *e;
	char d[6];
	char s[8];
	int i;
	const char *n = " C D EF G A Bc d ef g a b";
	d[0] = d[1] =	d[2] = d[3] = d[4] = d[5] = 0;
	d[cmdflag] = 1;
	d[command] = cmdchord;
	if( p[0] == '(' ) p++;	// chord between parens like: (C)
	for( i=0; n[i]; i++ )
		if( *p == n[i] ) {
			d[chordnote] = i;
			break;
		}
	p++;
	switch(*p) {
		case 'b':
			d[chordnote]--;
			p++;
			break;
		case '#':
			d[chordnote]++;
			p++;
			break;
	}
	d[chordbase] = d[chordnote];
	for( i=0; p[i] && p[i] != '"' && p[i] != '/' && p[i] != '(' && p[i] != ')' && p[i] != ' '; i++ ) s[i] = p[i];
	s[i] = '\0';
	p = &p[i];
	if( *p=='/' ) {
		p++;
		for( i=0; n[i]; i++ )
			if( *p == n[i] ) {
				d[chordbase] = i;
				break;
			}
		p++;
		switch(*p) {
			case 'b':
				d[chordbase]--;
				p++;
				break;
			case '#':
				d[chordbase]++;
				p++;
				break;
		}
	}
	for( i=0; i<chordsnamed; i++ )
		if( !strcmp(s, chordname[i]) ) {
			d[chordnum] = i;
			break;
		}
	if( i==chordsnamed ) {
		abc_message("Failure: unrecognized chordname %s",s);
		return;
	}
	e = abc_new_event(h, tracktime, d);
	abc_add_event(h, tp, e);
}

static void abc_add_setloop(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime)
{
	ABCEVENT *e;
	char d[6];
	d[0] = d[1] =	d[2] = d[3] = d[4] = d[5] = 0;
	d[cmdflag] = 1;
	d[command] = cmdloop;
	e = abc_new_event(h, tracktime, d);
	abc_add_event(h, tp, e);
}

static void abc_fade_track(ABCTRACK *tp, ABCEVENT *e)
{
	while(e) {
		if( e->flg != 1 && e->par[note] != 0 )
			e->par[volume] = abc_dynamic_volume(tp, e->tracktick, e->par[volume]);
		e = e->next;
	}
}

static void abc_add_setjumploop(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime, ABCEVENT_JUMPTYPE j)
{
	ABCEVENT *e;
	char d[8];
	d[0] = d[1] =	d[2] = d[3] = d[4] = d[5] = 0;
	d[cmdflag] = 1;
	d[command] = cmdjump;
	d[jumptype] = j;
	e = abc_new_event(h, tracktime, d);
	abc_add_event(h, tp, e);
}

static void abc_add_sync(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime)
{
	ABCEVENT *e;
	char d[6];
	e = tp->tail;
	if( e && e->tracktick == tracktime ) return;
	if( e && e->flg == 1 && e->cmd == cmdsync ) {
		e->tracktick = tracktime;
		return;
	}
	d[0] = d[1] =	d[2] = d[3] = d[4] = d[5] = 0;
	d[cmdflag] = 1;
	d[command] = cmdsync;
	e = abc_new_event(h, tracktime, d);
	abc_add_event(h, tp, e);
}

static void abc_add_gchord_syncs(ABCHANDLE *h, ABCTRACK *tpc, ULONG tracktime)
{
	ABCTRACK *tp;
	int i;
	for( i = GCHORDBPOS; i < DRUMPOS; i++ ) {
		tp = abc_locate_track(h, tpc->v, i);
		abc_add_sync(h,tp,tracktime);
	}
}

static void abc_add_drum_sync(ABCHANDLE *h, ABCTRACK *tpr, ULONG tracktime)
{
	ABCTRACK *tp;
	tp = abc_locate_track(h, tpr->v, DRUMPOS);
	abc_add_sync(h,tp,tracktime);
}

static int abc_getnumber(const char *p, int *number)
{
	int i,h;
	i = 0;
	h = 0;
	while( isdigit(p[i]) ) {
		h = 10 * h + p[i] - '0';
		i++;
	}
	if( i==0 )
		*number = 1;
	else
		*number = h;
	return i;
}

static int abc_getexpr(const char *p, int *number)
{
	int i, term, total;
	i = 0;
	while( isspace(p[i]) )
		i++;
	if( p[i] == '(' ) {
		i += abc_getexpr(p+i+1, number);
		while( p[i] && (p[i] != ')') )
			i++;
		return i;
		}
	i += abc_getnumber(p+i, &total);
	while( isspace(p[i]) )
		i++;
	while( p[i] == '+' ) {
		i += abc_getexpr(p+i+1, &term);
		total += term;
		while( isspace(p[i]) )
			i++;
	}
	*number = total;
	return i;
}

static int abc_notelen(const char *p, int *len, int *div)
{
	int i,h,k;
	i = abc_getnumber(p,len);
	h = 1;
	while( p[i] == '/' ) {
		h *= 2;
		i++;
	}
	if( isdigit(p[i]) ) {
		h /= 2;
		i += abc_getnumber(p+i,&k);
	}
	else k = 1;
	*div = h * k;
	return i;
}

static int abc_brokenrithm(const char *p, int *nl, int *nd, int *b, int hornpipe)
{
	switch( *b ) {
		case '<':
			*nl *= 3;
			*nd *= 2;
			hornpipe = 0;
			break;
		case '>':
			*nd *= 2;
			hornpipe = 0;
			break;
	}
	*b = *p;
	switch( *b ) {
		case '>':
			*nl *= 3;
			*nd *= 2;
			return 1;
		case '<':
			*nd *= 2;
			return 1;
		default:
			*b = 0;
			break;
	}
	if( hornpipe ) { // still true then make 1/8 notes broken rithme
		if( *nl == 1 && *nd == 1 ) {
			*b = '>';
			*nl = 3;
			*nd = 2; 
		}
	}  
	return 0;
}

// put p notes in the time q for the next r notes
static int abc_tuplet(int *nl, int *nd, int p, int q, int r)
{
	if( !r ) return 0;
	*nl *= q;
	*nd *= p;
	return r - 1;
}

// evaluate [Q:"string" n1/m1 n2/m2 n3/m3 n4/m4=bpm "string"] 
// minimal form [Q:"string"]
// most used form [Q: 1/4=120]
static int abc_extract_tempo(const char *p, int invoice)
{
	int nl, nd, ns, in, tempo;
	int nl1=0, nd1, notes, state;
	const char *q;
	in = 0;
	nl = 0;
	nd = 1;
	ns = 120;
	notes = 0;
	state = 0;
	for( q=p; *q; q++ ) {
		if( in ) {
			if( *q=='"' )
				in = 0;
		}
		else {
			if( *q == ']' ) break;
			switch( *q ) {
				case '"':
					in = 1;
					break;
				case '/':
					notes++;
					state = 1;
					nl1 = ns;
					break;
				case '=':
					break;
				default:
					if( isdigit(*q) ) {
						if( state ) {
							q+=abc_getnumber(q,&nd1)-1;
							state = 0;
							nl = nl * nd1 + nl1 * nd;
							nd = nd * nd1;
						}
						else
							q+=abc_getnumber(q,&ns)-1;
					}
					break;
			}
		}
	}
	if( !notes ) {
		nl = 1;
		nd = 4;
	}
	if( !nd ) tempo = 120;
	else tempo = ns * nl * 4 / nd; // mod tempo is really BPM where one B is equal to a quartnote
	if( invoice ) {
		nl = global_tempo_factor;
		nd = global_tempo_divider;
	}
	global_tempo_factor = 1;
	global_tempo_divider = 1;
	while( tempo/global_tempo_divider > 255 )
		global_tempo_divider++;
	tempo /= global_tempo_divider;
	while( tempo * global_tempo_factor < 256 )
		global_tempo_factor++;
	global_tempo_factor--;
	tempo *= global_tempo_factor;
	if( tempo * 3 < 512 ) {
		global_tempo_factor *= 3;
		global_tempo_divider *= 2;
		tempo = (tempo * 3) / 2;
	}
	if( invoice ) {
		if( nl != global_tempo_factor || nd != global_tempo_divider ) {
			ns = (tempo * nl * global_tempo_divider) / (nd * global_tempo_factor);
			if( ns > 31 && ns < 256 ) {
				tempo = ns;
				global_tempo_factor = nl;
				global_tempo_divider = nd;
			}
			else
				abc_message("Failure: inconvenient tempo change in middle of voice (%s)", p);
		}
	}
	return tempo;
}

static void	abc_set_parts(char **d, char *p)
{
	int i,j,k,m,n;
	char *q;
#ifdef NEWMIKMOD
	static MM_ALLOC *h;
	if( *d )	_mmalloc_close(h);
#else
	if( *d )	free(*d);
#endif
	*d = 0;
	if( !p ) return;
	for( i=0; p[i] && p[i] != '%'; i++ ) {
		if( !strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ().0123456789 ",p[i]) ) {
			abc_message("invalid characters in part string scanning P:%s", p);
			return;
		}
	}
#ifdef NEWMIKMOD
	h = _mmalloc_create("Load_ABC_parts", NULL);
#endif
	// decode constructs like "((AB)2.(CD)2)3.(AB)E2" to "ABABCDCDABABCDCDABABCDCDABEE"
	// first compute needed storage...
	j=0;
	k=0;
	for( i=0; p[i] && p[i] != '%'; i++ ) {
		if( isupper(p[i]) ) {
			j++;
		}
		if( isdigit(p[i]) ) {
			n=abc_getnumber(p+i,&k);
			if( p[i-1] == ')' )
				j *= k;	// never mind multiple parens, just take the worst case
			else
				j += k-1;
			i += n-1;
		}
	}
	q = (char *)_mm_calloc(h, j+1, sizeof(char));	// enough storage for the worst case
	// now copy bytes from p to *d, taking parens and digits in account
	j = 0;
	for( i=0; p[i] && p[i] != '%'; i++ ) {
		if( isdigit(p[i]) || isupper(p[i]) || p[i] == '(' || p[i] == ')' ) {
			if( p[i] == ')' ) {
				for( n=j; n > 0 && q[n-1] != '('; n-- )	;	// find open paren in q
				// q[n+1] to q[j] contains the substring that must be repeated
				if( n > 0 ) {
					for( k = n; k<j; k++ ) q[k-1] = q[k];	// shift to the left...
					j--;
				}
				else
					abc_message("Warning: Unbalanced right parens in P: definition %s",p);
				n = j - n + 1;	// number of repeatable characters
				i += abc_getnumber(p+i+1,&k);
				while( k-- > 1 ) {
					for( m=0; m<n; m++ ) {
						q[j] = q[j-n];
						j++;
					}
				}
				continue;
			}
			if( isdigit(p[i]) ) {
				n = abc_getnumber(p+i,&k);
				i += n - 1;
				while( k-- > 1 ) {
					q[j] = q[j-1];
					j++;
				}
				continue;
			}
			q[j] = p[i];
			j++;
		}
	}
	q[j] = '\0';
	// remove any left over parens
	for( i=0; i<j; i++ ) {
		if( q[i] == '(' ) {
			abc_message("Warning: Unbalanced left parens in P: definition %s",p);
			for( k=i; k<j; k++ ) q[k] = q[k+1];
			j--;
		}
	}
	*d = q;
}

static void abc_appendpart(ABCHANDLE *h, ABCTRACK *tp, ULONG pt1, ULONG pt2)
{
	ABCEVENT *e, *ec;
	ULONG dt;
	dt = tp->tail->tracktick - pt1;
	for( e=tp->head; e && e->tracktick <= pt2; e=e->next ) {
		if( e->tracktick >= pt1 ) {
			if( e->flg != 1 || e->cmd == cmdsync || e->cmd == cmdchord ) {
				if( e != tp->tail ) {
					// copy this event at tail
					ec = abc_copy_event(h,e);
					ec->tracktick += dt;
					ec->part = '*';
					tp->tail->next = ec;
					tp->tail = ec;
				}
			}
		}
	}
	abc_add_sync(h, tp, pt2 + dt); // make sure there is progression...
}

static ULONG abc_pattracktime(ABCHANDLE *h, ULONG tracktime)
{
	ABCEVENT *e;
	ULONG dt,et,pt=abcticks(64 * h->speed);
	if(!h || !h->track || !h->track->head ) return 0;
	dt = 0;
	for( e=h->track->head; e && e->tracktick <= tracktime; e=e->next ) {
		if( e->flg == 1 && e->cmd == cmdpartbrk ) {
			et = e->tracktick + dt;
			if( et % pt ) {
				et += pt;
				et /= pt;
				et *= pt;
				dt = et - e->tracktick;
			}
		}
	}
	return (tracktime + dt);
}

static int abc_patno(ABCHANDLE *h, ULONG tracktime)
{
	return modticks(abc_pattracktime(h, tracktime)) / 64 / h->speed;
}

static void abc_stripoff(ABCHANDLE *h, ABCTRACK *tp, ULONG tt)
{
	ABCEVENT *e1, *e2;
	e2 = NULL;
	for( e1 = tp->head; e1 && e1->tracktick <= tt; e1=e1->next )
		e2 = e1;
	if( e2 ) {
		e1 = e2->next;
		tp->tail = e2;
		e2->next = NULL;
	}
	else {
		e1 = tp->tail;
		tp->head = NULL;
		tp->tail = NULL;
	}
	while( e1 ) {
		e2 = e1->next;
		_mm_free(h->trackhandle,e1);
		e1 = e2;
	}
}

static void abc_keeptiednotes(ABCHANDLE *h, ULONG fromtime, ULONG totime) {
	ABCTRACK *tp;
	ABCEVENT *e,*n,*f;
	if( totime <= fromtime ) return;
	for( tp=h->track; tp; tp=tp->next ) {
		if( tp->vno ) { // if track is in use...
			n = NULL;
			for( e=tp->head; e && e->tracktick < fromtime; e = e->next )
				if( e->flg != 1 ) n = e; // remember it when it is a note event
			if( n && n->tiednote ) { // we've a candidate to tie over the break
				while( e && e->tracktick < totime ) e=e->next; // skip to other part
				if( e && e->tracktick == totime ) { // if this is on begin row of this part
					f = NULL;
					while( !f && e && e->tracktick == totime ) {
						if( e->flg != 1 ) f = e;
						e = e->next;
					}
					if( f && f->par[note] ) { // pfoeie, we've found a candidate
						if( abs(n->par[note] - f->par[note]) < 3 ) { // undo the note on
							f->flg = 1;
							f->cmd = cmdhide;
							f->lpar = 0;
						}
					}
				}
			}
		}
	}
}

static ULONG abc_fade_tracks(ABCHANDLE *h, char *abcparts, ULONG ptt[27])
{
	ABCTRACK *tp;
	ABCEVENT *e0;
	char *p;
	int vol;
	ULONG pt1,pt2;
	ULONG tt;
	tt = h->track->tail->tracktick;
	for( tp=h->track->next; tp; tp=tp->next ) {
		if( !tp->tail ) abc_add_sync(h, tp, tt); // no empty tracks please...
		if( tp->tail->tracktick > tt ) abc_stripoff(h, tp, tt); // should not happen....
		if( tp->tail->tracktick < tt ) abc_add_sync(h, tp, tt);
	}
	for( tp=h->track; tp; tp=tp->next ) {
		vol = 127;
		e0 = tp->tail;
		if( tp->slidevol != -2 ) {
			tp->slidevol = -2;
			tp->slidevoltime = e0->tracktick;
		}
		tp->mute = 0; // unmute track for safety, notes in a muted track already have zero volume...
		while( vol > 5 ) {
			for( p=abcparts; *p && vol > 5; p++ ) {
				pt1 = ptt[*p-'A'];
				pt2 = ptt[*p-'A'+1];
				abc_appendpart(h, tp, pt1, pt2);
				vol = abc_dynamic_volume(tp, tp->tail->tracktick, 127);
			}
		}
		abc_fade_track(tp,e0);
	}
	return h->track->tail->tracktick;
}

static void abc_song_to_parts(ABCHANDLE *h, char **abcparts, BYTE partp[27][2])
{
	ULONG starttick;
	ABCEVENT *e;
	int i, fading, loop, normal, partno, partsegno, partloop, partcoda, parttocoda, partfine, skip, x, y;
	int vmask[27],nextp[27];
	ULONG ptt[27];
	char buf[256];	// must be enough, mod's cannot handle more than 240 patterns
	char *pfade;
	if( !h || !h->track || !h->track->capostart ) return;
	strcpy(buf,"A");	// initialize our temporary array
	i = 1;
	loop = 1;
	partno = 0;
	partsegno = 0;
	partloop = 0;
	partcoda = -1;
	parttocoda = -1;
	partfine = -1;
	starttick = h->track->capostart->tracktick;
	ptt[0] = starttick;
	vmask[0] = -1;
	nextp[0] = 1;
	for( e=h->track->capostart; e; e=e->next ) {
		if( e->flg == 1 ) {
			switch( e->cmd ) {
				case cmdpartbrk:
					if( e->tracktick > starttick) {
						starttick = e->tracktick;	// do not make empty parts
						if( partno < 26 ) {
							partno++;
							ptt[partno] = starttick;
						}
						if( i < 255 )	buf[i++] = partno+'A';
						vmask[partno] = -1;
						nextp[partno] = partno+1;
					}
					break;
				case cmdloop:
					partloop = partno;
					loop = 1; // start counting anew...
					break;
				case cmdvariant:
					vmask[partno] = e->lpar;
					break;
				case cmdjump:
					x = 0;
					fading = 0;
					normal = 0;
					skip   = 0;
					pfade  = &buf[i];
					switch( e->par[jumptype] ) {
						case jumpfade:
							fading = 1;
						case jumpnormal:
							normal = 1;
							x = partloop;
							loop++;
							break;
						case jumpdsfade:
							fading = 1;
						case jumpdasegno:
							x = partsegno;
							break;
						case jumpdcfade:
							fading = 1;
						case jumpdacapo:
							x = 0;
							break;
						default:
							x = 0;
							break;
					}
					if( vmask[partno] != -1 ) nextp[partno] = x;
					if( partno < 26 ) ptt[partno+1] = e->tracktick; // for handling ties over breaks
					while( x <= partno ) {
						if( skip == 1 && x == partcoda ) skip = 0;
						y = !skip;
						if( y ) {
							if( !normal ) {
								if( x == partfine ) skip = 2;
								if( x == parttocoda ) skip = 1;
								y = !skip;
							}  
							if( !(vmask[x] & (1<<loop)) ) y = 0;
						}
						if( y ) {
							if( i < 255 )	buf[i++] = x+'A';
							if( nextp[x] != x + 1 ) loop++;
							x = nextp[x];
						}
						else
							x++;
					}
					if( fading && partno < 26 && i < 255 ) {	// add single part with fading tracks
						partno++;
						ptt[partno] = e->tracktick;
						buf[i] = '\0'; // close up pfade with zero byte
						starttick = abc_fade_tracks(h, pfade, ptt);
						buf[i++] = partno+'A';
						partno++;
						ptt[partno] = starttick;
						buf[i++] = partno+'A'; // one extra to throw away...
						e = h->track->tail; // this is the edge of the world captain...
					}
					break;
				case cmdtocoda:
					parttocoda = partno;
					break;
				case cmdcoda:
					partcoda = partno;
					break;
				case cmdfine:
					partfine = partno;
					break;
				case cmdsegno:
					partsegno = partno;
					break;
			}
		}
		e->part = partno+'a';	// small caps for generated parts...
	}
	i--; // strip off last partno
	if( partno > 0 ) partno--;
	buf[i] = '\0';
	if( i > 1 ) {
		for( i=1; buf[i]; i++ ) {
			if( buf[i] != buf[i-1] + 1 ) {
				x = buf[i-1] - 'A';
				y = buf[i] - 'A';
				abc_keeptiednotes(h, ptt[x+1], ptt[y]);
			}
		}
	}
	starttick = h->track->tail->tracktick;
	ptt[partno+1] = starttick;
	for( i=0; i<=partno; i++ ) {
		partp[i][0] = abc_patno(h, ptt[i]);
		partp[i][1] = abc_patno(h, ptt[i+1]);
	}
	// calculate end point of last part
	starttick = abc_pattracktime(h, starttick);
	if( starttick % abcticks(64 * h->speed) )
		partp[partno][1]++;
	abc_set_parts(abcparts, buf);
}

// =====================================================================================
static char *abc_fgets(MMFILE *mmfile, char buf[], unsigned int bufsz)
// =====================================================================================
{
	if( mmfeof(mmfile) ) return NULL;
	mmfgets(buf,bufsz,mmfile);
	return buf;
}

// =====================================================================================
static char *abc_fgetbytes(MMFILE *mmfile, char buf[], unsigned int bufsz)
// =====================================================================================
{
	unsigned int i;
	long pos;
	if( mmfeof(mmfile) ) return NULL;
	for( i=0; i<bufsz-2; i++ ) {
		buf[i] = (char)mmfgetc(mmfile);
		if( buf[i] == '\n' ) break;
		if( buf[i] == '\r' ) {
			pos = mmftell(mmfile);
			if( mmfgetc(mmfile) != '\n' )	mmfseek(mmfile, pos, SEEK_SET);
			buf[i] = '\n';
			break;
		}
	}
	if( buf[i] == '\n' ) i++;
	buf[i] = '\0';
	return buf;
}

static void abc_substitute(ABCHANDLE *h, char *target, char *s)
{
	char *p, *q;
	int i;
	int l = strlen(target);
	int n = strlen(s);
	while( (p=strstr(h->line, target)) ) {
		if( (i=strlen(h->line)) + n - l >= (int)h->len ) {
			h->line = (char *)_mm_recalloc(h->allochandle, h->line, h->len<<1, sizeof(char));
			h->len <<= 1;
			p=strstr(h->line, target);
		}
		if( n > l ) {
			for( q=&h->line[i]; q>p; q-- ) q[n-l] = q[0];
			for( q=s; *q; q++ ) *p++ = *q;
		}
		else {
			strcpy(p,s);
			strcat(p,p+l);
		}
	}
}

static void abc_preprocess(ABCHANDLE *h, ABCMACRO *m)
{
	int i, j, k, l, a, b;
	char t[32];
	char s[200],*p;
	if( m->n ) {
		k = m->n - m->name;
		for( i=0; i<14; i++ ) {
			strncpy(t, m->name, 32);
			t[k] = "CDEFGABcdefgab"[i];
			l = strlen(m->subst);
			p = s;
			for( j=0; j<l; j++ ) {
				a = m->subst[j];
				if( a > 'g' && islower(a) ) {
					b = a - 'n';
					a = "CDEFGABCDEFGABcdefgabcdefgab"[i+b+7];
					*p++ = a;
					if( i+b < 0 )
						*p++ = ',';
					if( i+b > 13 )
						*p++ = '\'';
				}
				else *p++ = a;
			}
			*p = '\0';
			abc_substitute(h, t, s);
		}
	}
	else
		abc_substitute(h, m->name, m->subst);
}

static char *abc_gets(ABCHANDLE *h, MMFILE *mmfile)
{
	int i;
	ABCMACRO *mp;
	if( !h->len ) {
		h->len = 64;	// initial line size, adequate for most abc's
		h->line = (char *)_mm_calloc(h->allochandle, h->len, sizeof(char));
	}
	if( abc_fgetbytes(mmfile, h->line, h->len) ) {
		while( (i=strlen(h->line)) > (int)(h->len - 3) ) {
			// line too short, double it
			h->line = (char *)_mm_recalloc(h->allochandle, h->line, h->len<<1, sizeof(char));
			if( h->line[i-1] != '\n' )
				abc_fgetbytes(mmfile, &h->line[i], h->len);
			h->len <<= 1;
		}
		h->line[i-1] = '\0'; // strip off newline
		for( mp=h->macro; mp; mp=mp->next )
			abc_preprocess(h,mp);
		return h->line;
	}
	return NULL;
}

static int abc_parse_decorations(ABCHANDLE *h, ABCTRACK *tp, const char *p)
{
	int vol=0;
	if( !strncmp(p,"mp",2) ) vol = 75;
	if( !strncmp(p,"mf",2) ) vol = 90;
	if( !strncmp(p,"sfz",3) ) vol = 100;
	if( *p == 'p' ) {
		vol = 60;
		while( *p++ == 'p' ) vol -= 15;
		if( vol < 1 ) vol = 1;
	}	
	if( *p == 'f' ) {
		vol = 105;
		while( *p++ == 'f' ) vol += 15;
		if( vol > 135 ) vol = 127;	// ffff
		if( vol > 127 ) vol = 125;	// fff
	}
	if( vol ) {
		tp->volume = vol;
		if( tp == h->track ) {	// copy volume over to all voice tracks
			for( ; tp; tp=tp->next ) {
				if( tp->vpos == 0 || tp->vpos > DRONEPOS2 ) tp->volume = vol;
			}
			tp = h->track;
		}
	}
	return tp->volume;
}

// =====================================================================================
#ifdef NEWMIKMOD
BOOL ABC_Test(MMSTREAM *mmfile)
#else
BOOL CSoundFile::TestABC(const BYTE *lpStream, DWORD dwMemLength)
#endif
// =====================================================================================
{
    char id[128];
    // scan file for first K: line (last in header)
#ifdef NEWMIKMOD
    _mm_fseek(mmfile,0,SEEK_SET);
		while(abc_fgets(mmfile,id,128)) {
#else
		MMFILE mmfile;
		mmfile.mm = (char *)lpStream;
		mmfile.sz = dwMemLength;
    mmfseek(&mmfile,0,SEEK_SET);
		while(abc_fgets(&mmfile,id,128)) {
#endif
	    if(id[0]=='K' 
			&& id[1]==':' 
			&& (isalpha(id[2]) || isspace(id[2])) ) return 1;
		}
    return 0;
}

// =====================================================================================
static ABCHANDLE *ABC_Init(void)
{
    ABCHANDLE   *retval;
		char *p;
		char buf[10];
#ifdef NEWMIKMOD
    MM_ALLOC     *allochandle;
    
		allochandle = _mmalloc_create("Load_ABC", NULL);
    retval = (ABCHANDLE *)_mm_calloc(allochandle, 1,sizeof(ABCHANDLE));
		if( !retval ) return NULL;
    retval->allochandle = allochandle;
    allochandle = _mmalloc_create("Load_ABC_macros", NULL);
    retval->macrohandle = allochandle;
    allochandle = _mmalloc_create("Load_ABC_tracks", NULL);
    retval->trackhandle = allochandle;
#else
    retval = (ABCHANDLE *)calloc(1,sizeof(ABCHANDLE));
		if( !retval ) return NULL;
#endif
		retval->track       = NULL;
		retval->macro       = NULL;
		retval->umacro      = NULL;
		retval->beatstring  = NULL;
		retval->pickrandom  = 0;
		retval->len         = 0;
		retval->line        = NULL;
		strcpy(retval->gchord, "");
		retval->barticks    = 0;
		p = getenv(ABC_ENV_NORANDOMPICK);
		if( p ) {
			if( isdigit(*p) )
				retval->pickrandom = atoi(p);
			if( *p == '-' ) {
#ifdef NEWMIKMOD
				retval->pickrandom = atoi(p+1);
				sprintf(buf,"%s=-%ld",ABC_ENV_NORANDOMPICK,retval->pickrandom+1);
#else
				retval->pickrandom = atoi(p+1)-1; // xmms preloads the file
				sprintf(buf,"%s=-%ld",ABC_ENV_NORANDOMPICK,retval->pickrandom+2);
#endif
				putenv(buf);
			}
		}
		else {
			srand(time(NULL)); // initialize random generator with seed
			retval->pickrandom = 1+(int)(10000.0*rand()/(RAND_MAX+1.0));
			// can handle pickin' from songbooks with 10.000 songs
#ifdef NEWMIKMOD
			sprintf(buf,"%s=-%ld",ABC_ENV_NORANDOMPICK,retval->pickrandom+1); // next in sequence
#else
			sprintf(buf,"%s=-%ld",ABC_ENV_NORANDOMPICK,retval->pickrandom); // xmms preloads the file
#endif
			putenv(buf);
		}
    return retval;
}

#ifndef NEWMIKMOD
static void ABC_CleanupTrack(ABCTRACK *tp)
{
	ABCEVENT *ep, *en;
	if( tp ) {
		for( ep=tp->head; ep; ep = en ) {
			en=ep->next;
			free(ep);
		}
		tp->head = NULL;
	}
}

static void ABC_CleanupMacro(ABCMACRO *m)
{
	if( m->name )
		free(m->name);
	if( m->subst )
		free(m->subst);
	free(m);
}
#endif

// =====================================================================================
static void ABC_CleanupTracks(ABCHANDLE *handle)
// =====================================================================================
{
#ifdef NEWMIKMOD
	if(handle && handle->trackhandle) {
		_mmalloc_close(handle->trackhandle);
		handle->trackhandle = 0;
	}
#else
	ABCTRACK *tp, *tn;
	if(handle) {
		for( tp=handle->track; tp; tp = tn ) {
			tn=tp->next;
			ABC_CleanupTrack(tp);
		}
		handle->track = NULL;
	}
#endif
}

// =====================================================================================
static void ABC_CleanupMacros(ABCHANDLE *handle)
// =====================================================================================
{
#ifdef NEWMIKMOD
	if(handle && handle->macrohandle) {
		_mmalloc_close(handle->macrohandle);
		handle->macrohandle = 0;
	}
#else
	ABCMACRO *mp, *mn;
	if(handle) {
		for( mp=handle->macro; mp; mp = mn ) {
			mn=mp->next;
			ABC_CleanupMacro(mp);
		}
		for( mp=handle->umacro; mp; mp = mn ) {
			mn=mp->next;
			ABC_CleanupMacro(mp);
		}
		handle->macro = NULL;
		handle->umacro = NULL;
	}
#endif
}

// =====================================================================================
static void ABC_Cleanup(ABCHANDLE *handle)
// =====================================================================================
{
#ifdef NEWMIKMOD
	if(handle && handle->allochandle) {
#else
	if(handle) {
#endif
		ABC_CleanupMacros(handle);
		ABC_CleanupTracks(handle);
#ifdef NEWMIKMOD
		_mmalloc_close(handle->allochandle);
		handle->allochandle = 0;
		handle->len = 0;
#else
		if( handle->line )
			free(handle->line);
		if( handle->beatstring )
			free(handle->beatstring);
		free(handle);
#endif
	}
}

static int abc_is_global_event(ABCEVENT *e)
{
	return e->flg == 1 && (e->cmd == cmdtempo || e->cmd == cmdpartbrk); 
}

static ABCEVENT *abc_next_global(ABCEVENT *e)
{
	for( ; e && !abc_is_global_event(e); e=e->next ) ;
	return e;
}

static ABCEVENT *abc_next_note(ABCEVENT *e)
{
	for( ; e && e->flg == 1; e=e->next ) ;
	return e;
}

// =============================================================================
#ifdef NEWMIKMOD
static void ABC_ReadPatterns(UNIMOD *of, ABCHANDLE *h, int numpat)
// =============================================================================
{
	int pat,row,i,ch,trillbits;
	BYTE n,ins,vol;
	ABCTRACK *t;
	ABCEVENT *e, *en, *ef, *el;
	ULONG tt1, tt2;
	UNITRK_EFFECT eff;

	// initialize start points of event list in tracks
	for( t = h->track; t; t = t->next ) t->capostart = t->head;
	trillbits = 0; // trill effect admininstration: one bit per channel, max 32 channnels
	for( pat = 0; pat < numpat; pat++ ) {
		utrk_reset(of->ut);
		for( row = 0; row < 64; row++ ) {
			tt1 = abcticks((pat * 64 + row ) * h->speed);
			tt2 = tt1 + abcticks(h->speed);
			ch = 0;
			for( e=abc_next_global(h->track->capostart); e && e->tracktick < tt2; e=abc_next_global(e->next) ) {
				if( e && e->tracktick >= tt1 ) {	// we have a tempo event in this row
					switch( e->cmd ) {
						case cmdtempo:
							eff.effect   = UNI_GLOB_TEMPO;
							eff.param.u  = e->lpar;
							eff.framedly = UFD_RUNONCE;
							utrk_write_global(of->ut, &eff, PTMEM_TEMPO);
							break;
						case cmdpartbrk:
							eff.effect   = UNI_GLOB_PATBREAK;
							eff.param.u  = 0;
							eff.framedly = UFD_RUNONCE;
							utrk_write_global(of->ut, &eff, UNIMEM_NONE);
							break;
					}
				}
			}
			for( t = h->track; t; t = t->next ) {
				for( e=abc_next_note(t->capostart); e && e->tracktick < tt1; e=abc_next_note(e->next) )
					t->capostart = e;
				i = 0;
				ef = NULL;
				en = e;
				el = e;
				for( ; e && e->tracktick < tt2; e=abc_next_note(e->next) ) {	// we have a note event in this row
					t->capostart = e;
					i++;
					if( e->par[volume] ) {
						if( !ef ) ef = e;
						el = e;
					}
				}
				if( i ) {
					trillbits &= ~(1<<ch);
					utrk_settrack(of->ut, ch);
					if( i == 1 || ef == el || !ef ) { // only one event in this row
						if( ef ) e = ef;
						else e = en;
						el = t->capostart;
						i  = e->par[note] + ((e->par[octave])*12);
						if( t->chan == 10 ) {
							n   = pat_gm_drumnote(i) + 23;
							ins = pat_gmtosmp(pat_gm_drumnr(i));
						}
						else {
							n   = pat_modnote(i);
							ins = e->par[smpno];
						}
						eff.framedly = modticks(e->tracktick - tt1);
						vol = e->par[volume];
						if( e->par[effect] == accent ) {
							vol += vol / 10;
							if( vol > 127 ) vol = 127;
						}
						if (vol <= 0) {}
						else if( el->par[volume] == 0 ) {
							eff.framedly     = modticks(el->tracktick - tt1);
							eff.param.u      = 0;
							eff.param.byte_a = n;
							eff.param.byte_b = ins;
							eff.effect       = UNI_NOTEKILL;
							utrk_write_local(of->ut, &eff, UNIMEM_NONE);
						}
						else {
							switch( e->par[effect] ) {
								case trill:
									eff.effect  = UNI_VIBRATO_DEPTH;
									eff.param.u = 12;	// depth 1.5
									utrk_write_local(of->ut, &eff, PTMEM_VIBRATO_DEPTH);
									eff.effect  = UNI_VIBRATO_SPEED;
									eff.param.u = 48; // speed 12
									utrk_write_local(of->ut, &eff, PTMEM_VIBRATO_SPEED);
									trillbits |= (1<<ch);
									break;
								case bow:
									eff.effect   = UNI_PITCHSLIDE;
									eff.framedly = (h->speed/2)|UFD_RUNONCE;
									eff.param.s  = 2;
									utrk_write_local(of->ut, &eff, (e->par[effoper])? PTMEM_PITCHSLIDEUP: PTMEM_PITCHSLIDEDN);
									break;
								default:
									break;
							}
							if( eff.framedly ) {
								eff.param.u      = 0;
								eff.param.byte_a = n;
								eff.param.byte_b = ins;
								eff.effect       = UNI_NOTEDELAY;
								utrk_write_local(of->ut, &eff, UNIMEM_NONE);
							}
						}
						utrk_write_inst(of->ut, ins);
						utrk_write_note(of->ut, n); // <- normal note
						pt_write_effect(of->ut, 0xc, vol);
					}
					else { 
						// two notes in one row, use FINEPITCHSLIDE runonce effect
						// start first note on first tick and framedly runonce on seconds note tick
						// use volume and instrument of last note
						if( t->chan == 10 ) {
							i   = el->par[note] + ((el->par[octave])*12);
							n   = pat_gm_drumnote(i) + 23;
							ins = pat_gmtosmp(pat_gm_drumnr(i));
							i   = n; // cannot change instrument here..
						}
						else {
							i   = ef->par[note] + ((ef->par[octave])*12);
							n   = pat_modnote(i);
							ins = el->par[smpno];
							i   = pat_modnote(el->par[note] + ((el->par[octave])*12));
						}
						vol = el->par[volume];
						eff.effect    = UNI_PITCHSLIDE;
						eff.framedly  = modticks(el->tracktick - tt1)|UFD_RUNONCE;
						eff.param.s   = ((i > n)?i-n:n-i);
						utrk_write_inst(of->ut, ins);
						utrk_write_note(of->ut, n); // <- normal note
						pt_write_effect(of->ut, 0xc, vol);
						utrk_write_local(of->ut, &eff, (i > n)? PTMEM_PITCHSLIDEUP: PTMEM_PITCHSLIDEDN);
					}
				}
				else { // no new notes, keep on trilling...
					if( trillbits & (1<<ch) ) {
						utrk_settrack(of->ut, ch);
						eff.effect  = UNI_VIBRATO_DEPTH;
						eff.param.u = 12;	// depth 1.5
						utrk_write_local(of->ut, &eff, PTMEM_VIBRATO_DEPTH);
						eff.effect  = UNI_VIBRATO_SPEED;
						eff.param.u = 60; // speed 15
						utrk_write_local(of->ut, &eff, PTMEM_VIBRATO_SPEED);
					}
				}
				ch++;
			}
			utrk_newline(of->ut);
		}
		if(!utrk_dup_pattern(of->ut,of)) return;
	}
}

#else

static int ABC_ReadPatterns(MODCOMMAND *pattern[], WORD psize[], ABCHANDLE *h, int numpat, int channels)
// =====================================================================================
{
	int pat,row,i,ch,trillbits;
	BYTE n,ins,vol;
	ABCTRACK *t;
	ABCEVENT *e, *en, *ef, *el;
	ULONG tt1, tt2;
	MODCOMMAND *m;
	int patbrk, tempo;
	if( numpat > MAX_PATTERNS ) numpat = MAX_PATTERNS;

	// initialize start points of event list in tracks
	for( t = h->track; t; t = t->next ) t->capostart = t->head;
	trillbits = 0; // trill effect admininstration: one bit per channel, max 32 channnels
	for( pat = 0; pat < numpat; pat++ ) {
		pattern[pat] = CSoundFile::AllocatePattern(64, channels);
		if( !pattern[pat] ) return 0;
		psize[pat] = 64;
		for( row = 0; row < 64; row++ ) {
			tt1 = abcticks((pat * 64 + row ) * h->speed);
			tt2 = tt1 + abcticks(h->speed);
			ch = 0;
			tempo = 0;
			patbrk = 0;
			for( e=abc_next_global(h->track->capostart); e && e->tracktick < tt2; e=abc_next_global(e->next) ) {
				if( e && e->tracktick >= tt1 ) {	// we have a tempo event in this row
					switch( e->cmd ) {
						case cmdtempo:
							tempo = e->lpar;
							break;
						case cmdpartbrk:
							patbrk = 1;
							break;
					}
				}
			}
			for( t = h->track; t; t = t->next ) {
				for( e=abc_next_note(t->capostart); e && e->tracktick < tt1; e=abc_next_note(e->next) ) ;
				i = 0;
				ef = NULL;
				en = e;
				el = e;
				for( ; e && e->tracktick < tt2; e=abc_next_note(e->next) ) {	// we have a note event in this row
					t->capostart = e;
					i++;
					if( e->par[volume] ) {
						if( !ef ) ef = e;
						el = e;
					}
				}
				m = &pattern[pat][row * channels + ch];
				m->param   = 0;
				m->command = CMD_NONE;
				if( i ) {
					trillbits &= ~(1<<ch);
					if( i == 1 || ef == el || !ef ) { // only one event in this row
						if( ef ) e = ef;
						else e = en;
						el = t->capostart;
						i  = e->par[note] + ((e->par[octave])*12);
						if( t->chan == 10 ) {
							n   = pat_gm_drumnote(i) + 23;
							ins = pat_gmtosmp(pat_gm_drumnr(i));
						}
						else {
							n   = pat_modnote(i);
							ins = e->par[smpno];
						}
							vol = e->par[volume]/2;
						if( e->par[volume] > 0 ) {
							if( e->par[effect] == accent ) vol += vol / 20;
							if( vol > 64 ) vol = 64;
							if( el->par[volume] == 0 ) { // note cut
								m->param   = el->tracktick - tt1;
								m->command = CMD_S3MCMDEX;
								m->param  |= 0xC0;
							}
							else {
								switch( e->par[effect] ) {
									case trill:
										m->command = CMD_VIBRATO;
										m->param   = 0xC2;	// speed 12 depth 2
										trillbits |= (1<<ch);
										break;
									case bow:
										m->command = CMD_XFINEPORTAUPDOWN;
										m->param  |= (e->par[effoper])? 0x12: 0x22;
										break;
									default:
										m->param = modticks(e->tracktick - tt1);
										if( m->param ) { // note delay
											m->command = CMD_S3MCMDEX;
											m->param  |= 0xD0;
										}
										break;
								}
							}
						}
						m->instr  = ins;
						m->note   = n; // <- normal note
						m->volcmd = VOLCMD_VOLUME;
						m->vol    = vol;
					}
					else { 
						// two notes in one row, use FINEPITCHSLIDE runonce effect
						// start first note on first tick and framedly runonce on seconds note tick
						// use volume and instrument of last note
						if( t->chan == 10 ) {
							i   = el->par[note] + ((el->par[octave])*12);
							n   = pat_gm_drumnote(i) + 23;
							ins = pat_gmtosmp(pat_gm_drumnr(i));
							i   = n; // cannot change instrument here..
						}
						else {
							i   = ef->par[note] + ((ef->par[octave])*12);
							n   = pat_modnote(i);
							ins = el->par[smpno];
							i   = pat_modnote(el->par[note] + ((el->par[octave])*12));
						}
						vol = el->par[volume]/2;
						if( vol > 64 ) vol = 64;
						m->instr  = ins;
						m->note   = n; // <- normal note
						m->volcmd = VOLCMD_VOLUME;
						m->vol    = vol;
						m->param  = ((i > n)?i-n:n-i);
						if( m->param < 16 ) {
							if( m->param ) {
								m->command = CMD_XFINEPORTAUPDOWN;
								m->param  |= (i > n)? 0x10: 0x20;
							}
							else {	// retrigger same note...
								m->command = CMD_RETRIG;
								m->param   = modticks(el->tracktick - tt1);
							}
						}
						else
							m->command = (i > n)? CMD_PORTAMENTOUP: CMD_PORTAMENTODOWN;
					}
				}
				else { // no new notes, keep on trilling...
					if( trillbits & (1<<ch) ) {
						m = &pattern[pat][row * channels + ch];
						m->command = CMD_VIBRATO;
						m->param   = 0;	// inherited from first effect
						m->instr   = 0;
						m->note    = 0;
						m->volcmd  = 0;
						m->vol     = 0;
					}
				}
				if( m->param == 0 && m->command == CMD_NONE ) {
					if( tempo ) {
						m->command = CMD_TEMPO;
						m->param   = tempo;
						tempo      = 0;
					}
					else {
						if( patbrk ) {
							m->command = CMD_PATTERNBREAK;
							patbrk     = 0;
						}
					}
				}
				ch++;
			}
			if( tempo || patbrk ) return 1;
		}
	}
	return 0;
}

#endif

static int ABC_Key(const char *p)
{
	int i,j;
	char c[8];
	const char *q;
	while( isspace(*p) ) p++;
	i = 0;
	q = p;
	for( i=0; i<8 && *p && *p != ']'; p++ ) {
		if( isspace(*p) ) {
			while( isspace(*p) ) p++;
			if( strncasecmp(p, "min", 3) && strncasecmp(p, "maj", 3) )
				break;
		}
		c[i] = *p;
		i++;
	}
	c[i] = '\0';
	if( !strcmp(c,"Hp") || !strcmp(c,"HP") ) // highland pipes
		strcpy(c,"Bm");	// two sharps at c and f
	if( !strcasecmp(c+1, "minor") ) i=2;
	if( !strcasecmp(c+2, "minor") ) i=3;
	if( !strcasecmp(c+1, "major") ) i=1;
	if( !strcasecmp(c+2, "major") ) i=2;
	if( !strcasecmp(c+1, "min") ) i=2;
	if( !strcasecmp(c+2, "min") ) i=3;
	if( !strcasecmp(c+1, "maj") ) i=1;
	if( !strcasecmp(c+2, "maj") ) i=2;
	for( ; i<6; i++ )
		c[i] = ' ';
	c[i] = '\0';
	for( i=0; keySigs[i]; i++ ) {
		for( j=10; j<46; j+=6 )
			if( !strncasecmp(keySigs[i]+j, c, 6) )
				return i;
	}
	abc_message("Failure: Unrecognised K: field %s", q);
	return 7;
}

static char *abc_skip_word(char *p)
{
	while( isspace(*p) ) p++;
	while( *p && !isspace(*p) && *p != ']') p++;
	while( isspace(*p) ) p++;
	return p;
}

static ULONG abc_tracktime(ABCTRACK *tp)
{
	ULONG tracktime;
	if( tp->tail ) tracktime = tp->tail->tracktick;
	else tracktime = 0;
	if( tracktime < global_songstart ) 
		tracktime = global_songstart;
	return tracktime;
}

static void abc_addchordname(const char *s, int len, int *notes)
// adds chord name and note set to list of known chords
{
	int i, j;
	if(strlen(s) > 7) {
		abc_message("Failure: Chord name cannot exceed 7 characters, %s", s);
		return;
	}
	if(len > 6) {
		abc_message("Failure: Named chord cannot have more than 6 notes, %s", s);
		return;
	}
	for( i=0; i < chordsnamed; i++ ) {
		if(strcmp(s, chordname[i]) == 0) {
			/* change chord */
			chordlen[i] = len;
			for(j = 0; j < len; j++) chordnotes[i][j] = notes[j];
			return;
		}
	}
	if(chordsnamed > MAXCHORDNAMES - 1)
		abc_message("Failure: Too many Guitar Chord Names used, %s", s);
	else {
		strcpy(chordname[chordsnamed], s);
		chordlen[chordsnamed] = len;
		for(j = 0; j < len; j++) chordnotes[chordsnamed][j] = notes[j];
		chordsnamed++;
	}
}

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

static int abc_MIDI_getnumber(const char *p)
{
	int n;
	while( isspace(*p) ) p++;
	abc_getnumber(p, &n);
	if( n < 0 )   n = 0;
	if( n > 127 ) n = 127;
	return n;
}

static int abc_MIDI_getprog(const char *p)
{
	int n;
	while( isspace(*p) ) p++;
	abc_getnumber(p, &n);
	if( n < 1 )   n = 1;
	if( n > 128 ) n = 128;
	return n;
}

// MIDI drone <instr0> <pitch1> <pitch2> <vel1> <vel2>
static void abc_MIDI_drone(const char *p, int *gm, int *ptch, int *vol)
{
	int i;
	while( isspace(*p) ) p++;
	p += abc_getnumber(p, &i);
	i++;	// adjust for 1..128
	if( i>0 && i < 129 )
		*gm = i;
	else
		*gm = 71;	// bassoon
	while( isspace(*p) ) p++;
	p += abc_getnumber(p, &i);
	if( i>0 && i < 127 )
		ptch[0] = i;
	else
		ptch[0] = 45;
	while( isspace(*p) ) p++;
	p += abc_getnumber(p, &i);
	if( i>0 && i < 127 )
		ptch[1] = i;
	else
		ptch[1] = 33;
	while( isspace(*p) ) p++;
	p += abc_getnumber(p, &i);
	if( i>0 && i < 127 )
		vol[0] = i;
	else
		vol[0] = 80;
	while( isspace(*p) ) p++;
	p += abc_getnumber(p, &i);
	if( i>0 && i < 127 )
		vol[1] = i;
	else
		vol[1] = 80;
}

static void abc_chan_to_tracks(ABCHANDLE *h, int tno, int ch)
{
	ABCTRACK *tp;
	if( tno>0 && tno<33 ) {
		for( tp=h->track; tp; tp=tp->next ) {
			if( tp->vno == tno && (tp->vpos < GCHORDBPOS || tp->vpos > DRONEPOS2) )
				tp->chan = ch;
		}
	}
}

// %%MIDI channel int1
// channel numbers are 1-16
static void abc_MIDI_channel(const char *p, ABCTRACK *tp, ABCHANDLE *h)
{
	int i1, i2;
	i1 = tp? tp->vno: 1;
	for( ; *p && isspace(*p); p++ ) ;
	if( isdigit(*p) ) {
		p += abc_getnumber(p, &i2);
		if( i2 >= 1 && i2 <= 16 )
			abc_chan_to_tracks(h, i1, i2); // we start at 1
	}
}

static void abc_instr_to_tracks(ABCHANDLE *h, int tno, int gm)
{
	ABCTRACK *tp;
	if( tno>0 && tno<33 && gm>0 && gm<129 ) {
		for( tp=h->track; tp; tp=tp->next ) {
			if( tp->vno == tno && (tp->vpos < GCHORDBPOS || tp->vpos > DRONEPOS2) )
				tp->instr = gm;
		}
	}
}

// %%MIDI program [int1] <int2>
// instrument numbers are 0-127
static void abc_MIDI_program(const char *p, ABCTRACK *tp, ABCHANDLE *h)
{
	int i1, i2;
	i1 = tp? tp->vno: 1;
	for( ; *p && isspace(*p); p++ ) ;
	if( isdigit(*p) ) {
		p += abc_getnumber(p, &i2);
		for( ; *p && isspace(*p); p++ ) ;
		if( isdigit(*p) ) {
			i1 = i2;
			p += abc_getnumber(p, &i2);
		}
		abc_instr_to_tracks(h, i1, i2 + 1); // we start at 1
	}
}

static void abc_mute_voice(ABCHANDLE *h, ABCTRACK *tp, int m)
{
	ABCTRACK *t;
	for( t=h->track; t; t=t->next ) {
		if( t->vno == tp->vno ) t->mute = m;
	}
}

// %%MIDI voice [<ID>] [instrument=<integer> [bank=<integer>]] [mute]
// instrument numbers are 1-128
static void abc_MIDI_voice(const char *p, ABCTRACK *tp, ABCHANDLE *h)
{
	int i1, i2;
	for( ; *p && isspace(*p); p++ ) ;
	if( strncmp(p,"instrument=",11) && strncmp(p,"mute",4) ) {
		tp = abc_locate_track(h, p, 0);
		for( ; *p && !isspace(*p); p++ ) ;
		for( ; *p && isspace(*p); p++ ) ;
	}
	i1 = tp? tp->vno: 1;
	i2 = 0;
	if( !strncmp(p,"instrument=",11) && isdigit(p[11]) ) {
		p += 11;
		p += abc_getnumber(p, &i2);
		for( ; *p && isspace(*p); p++ ) ;
		if( !strncmp(p,"bank=",5) && isdigit(p[5]) ) {
			for( ; *p && !isspace(*p); p++ ) ;
			for( ; *p && isspace(*p); p++ ) ;
		}
	}
	if( tp ) abc_mute_voice(h,tp,0);
	if( !strncmp(p,"mute",4) && (p[4]=='\0' || p[4]=='%' || isspace(p[4])) ) {
		if( tp ) abc_mute_voice(h,tp,1);
	}
	abc_instr_to_tracks(h, i1, i2); // starts already at 1 (draft 4.0)
}

// %%MIDI chordname <string> <int1> <int2> ... <int6>
static void abc_MIDI_chordname(const char *p)
{
	char name[20];
	int i, notes[6];

	for( ; *p && isspace(*p); p++ ) ;
	i = 0;
	while ((i < 19) && (*p != ' ') && (*p != '\0')) {
		name[i] = *p;
		p = p + 1;
		i = i + 1;
	}
	name[i] = '\0';
	if(*p != ' ') {
		abc_message("Failure: Bad format for chordname command, %s", p);
	}
	else {
		i = 0;
		while ((i <= 6) && isspace(*p)) {
			for( ; *p && isspace(*p); p++ ) ;
			p += abc_getnumber(p, &notes[i]);
			i = i + 1;
		}
		abc_addchordname(name, i, notes);
	}
}

// %%MIDI drum <string> <inst 1> ... <inst n> <vol 1> ... <vol n>
// instrument numbers are 0-127
static int abc_MIDI_drum(const char *p, ABCHANDLE *h)
{
	char *q;
	int i,n,m;
	while( isspace(*p) ) p++;
	if( !strncmp(p,"on",2) && (isspace(p[2]) || p[2] == '\0') ) return 2;
	if( !strncmp(p,"off",3) && (isspace(p[3]) || p[3] == '\0') ) return 1;
	n = 0;
	for( q = h->drum; *p && !isspace(*p); p++ ) {
		if( !strchr("dz0123456789",*p) ) break;
		*q++ = *p;
		if( !isdigit(*p) ) {
			if( !isdigit(p[1]) ) *q++ = '1';
			n++; // count the silences too....
		}
	}
	*q = '\0';
	q = h->drumins;
	for( i = 0; i<n; i++ ) {
		if( h->drum[i*2] == 'd' ) {
			while( isspace(*p) ) p++;
			if( !isdigit(*p) ) {
				m = 0;
				while( !isspace(*p) ) p++;
			}
			else
				p += abc_getnumber(p,&m);
			q[i] = m + 1; // we start at 1
		}
		else q[i] = 0;
	}
	q = h->drumvol;
	for( i = 0; i<n; i++ ) {
		if( h->drum[i*2] == 'd' ) {
			while( isspace(*p) ) p++;
			if( !isdigit(*p) ) {
				m = 0;
				while( !isspace(*p) ) p++;
			}
			else
				p += abc_getnumber(p,&m);
			q[i] = m;
		}
		else q[i] = 0;
	}
	return 0;
}

// %%MIDI gchord <string>
static int abc_MIDI_gchord(const char *p, ABCHANDLE *h)
{
	char *q;
	while( isspace(*p) ) p++;
	if( !strncmp(p,"on",2) && (isspace(p[2]) || p[2] == '\0') ) return 2;
	if( !strncmp(p,"off",3) && (isspace(p[3]) || p[3] == '\0') ) return 1;
	for( q = h->gchord; *p && !isspace(*p); p++ ) {
		if( !strchr("fbcz0123456789ghijGHIJ",*p) ) break;
		*q++ = *p;
		if( !isdigit(*p) && !isdigit(p[1]) ) *q++ = '1';
	}
	*q = '\0';
	return 0;
}

static void abc_metric_gchord(ABCHANDLE *h, int mlen, int mdiv)
{
	switch( 16 * mlen + mdiv ) {
		case 0x24:
		case 0x44:
		case 0x22:
			abc_MIDI_gchord("fzczfzcz", h);
			break;
		case 0x64:
		case 0x32:
			abc_MIDI_gchord("fzczczfzczcz", h);
			break;
		case 0x34:
		case 0x38:
			abc_MIDI_gchord("fzczcz", h);
			break;
		case 0x68:
			abc_MIDI_gchord("fzcfzc", h);
			break;
		case 0x98:
			abc_MIDI_gchord("fzcfzcfzc", h);
			break;
		case 0xc8:
			abc_MIDI_gchord("fzcfzcfzcfzc", h);
			break;
		default:
			if( mlen % 3 == 0 )
				abc_MIDI_gchord("fzcfzcfzcfzcfzcfzcfzcfzcfzc", h);
			else
				abc_MIDI_gchord("fzczfzczfzczfzczfzczfzczfzcz", h);
			if( mdiv == 8 )	h->gchord[mlen*2] = '\0';
			else h->gchord[mlen*4] = '\0';
			break;
	}
}

static void abc_MIDI_legato(const char *p, ABCTRACK *tp)
{
	for( ; *p && isspace(*p); p++ ) ;
	if( !strncmp(p,"off",3) ) tp->legato = 0;
	else tp->legato = 1;
}

static void abc_M_field(const char *p, int *mlen, int *mdiv)
{
	if( !strncmp(p,"none",4) ) {
		*mlen = 1;
		*mdiv = 1;
		return;
	}
	if( !strncmp(p,"C|",2) ) {
		*mlen = 2;
		*mdiv = 2;
		return;
	}
	if( *p == 'C' ) {
		*mlen = 4;
		*mdiv = 4;
		return;
	}
	p += abc_getexpr(p,mlen);
	sscanf(p," / %d", mdiv);
}

static int abc_drum_steps(const char *dch)
{
	const char *p;
	int i=0;
	for( p=dch; *p; p++ ) {
		if( isdigit(*p) ) i += *p - '0';;
	}
	return i;
}

static void abc_add_drum(ABCHANDLE *h, ULONG tracktime, ULONG bartime)
{
	ABCEVENT *e;
	ABCTRACK *tp;
	ULONG etime, ctime , rtime, stime;
	int i, g, steps, gnote, gsteps, nnum;
	steps = abc_drum_steps(h->drum);
	ctime = h->barticks;
	// look up the last event in tpr drumtrack
	tp = abc_locate_track(h, h->tpr->v, DRUMPOS);
	e = tp->tail;
	etime = e? e->tracktick: bartime;
	if( etime > tracktime ) return;
	if( etime < bartime ) rtime = h->barticks - ((bartime - etime) % h->barticks);
	else rtime = (etime - bartime) % h->barticks;
	stime = ctime*steps;
	rtime *= steps;
	rtime += stime;
	gsteps = strlen(h->drum)/2;
	g = 0;
	while( rtime > stime ) {
		rtime -= ctime*(h->drum[g*2+1] - '0');
		if( ++g == gsteps ) g = 0;
	}
	stime = (tracktime - etime) * steps;
	rtime = 0;
	while( rtime < stime ) {
		gnote = h->drum[g*2];
		i = h->drum[g*2+1] - '0';
		if(gnote=='d') {
			tp->instr = pat_gm_drumnr(h->drumins[g]-1);
			nnum      = pat_gm_drumnote(h->drumins[g]);
			abc_add_drumnote(h, tp, etime + rtime/steps, nnum, h->drumvol[g]);
			abc_add_noteoff(h,tp,etime + ( rtime + ctime * i )/steps);
		}
		if( ++g == gsteps ) g = 0;
		rtime += ctime * i;
	}
}

static int abc_gchord_steps(const char *gch)
{
	const char *p;
	int i=0;
	for( p=gch; *p; p++ )
		if( isdigit(*p) ) i += *p - '0';
	return i;
}

static void abc_add_gchord(ABCHANDLE *h, ULONG tracktime, ULONG bartime)
{
	ABCEVENT *e, *c;
	ABCTRACK *tp;
	ULONG etime, ctime , rtime, stime;
	int i, g, steps, gnote, gcnum, gsteps, nnum, glen;
	// look up the last chord event in tpc
	c = 0;
	for( e = h->tpc->head; e; e = e->next )
		if( e->flg == 1 && e->cmd == cmdchord )
			c = e;
	if( !c ) return;
	gcnum = c->par[chordnum];
	steps = abc_gchord_steps(h->gchord);
	ctime = h->barticks;
	etime = 0;
	for( i = GCHORDBPOS; i < DRUMPOS; i++ ) {
		tp = abc_locate_track(h, h->tpc->v, i);
		e = tp->tail;
		if( !e ) e = c;
		stime = e->tracktick;
		if( stime > etime )	etime = stime;
	}
	if( etime > tracktime ) return;
	if( etime < bartime ) rtime = h->barticks - ((bartime - etime) % h->barticks);
	else rtime = (etime - bartime) % h->barticks;
	stime = ctime * steps;
	rtime *= steps;
	rtime += stime;
	gsteps = strlen(h->gchord);
	g = 0;
	while( rtime > stime ) {
		glen = h->gchord[2*g+1] - '0';
		rtime -= ctime * glen;
		if( ++g == gsteps ) g = 0;
	}
	stime = (tracktime - etime) * steps;
	rtime = 0;
	while( rtime < stime ) {
		gnote = h->gchord[2*g];
		glen  = h->gchord[2*g+1] - '0';
		if( ++g == gsteps ) g = 0;
		nnum = 0;
		switch(gnote) {
			case 'b':
				tp = abc_locate_track(h, h->tpc->v, GCHORDFPOS);
				tp->instr = h->abcbassprog;
				nnum = c->par[chordnote]+chordnotes[gcnum][0]+24;
				abc_add_chordnote(h, tp, etime + rtime/steps, nnum, h->abcbassvol);
				abc_add_noteoff(h,tp,etime + ( rtime + ctime * glen )/steps);
			case 'c':
				for( i = 1; i < chordlen[gcnum]; i++ ) {
					tp = abc_locate_track(h, h->tpc->v, i+GCHORDFPOS);
					tp->instr = h->abcchordprog;
					nnum = c->par[chordnote]+chordnotes[gcnum][i]+24;
					abc_add_chordnote(h, tp, etime + rtime/steps, nnum, h->abcchordvol);
					abc_add_noteoff(h,tp,etime + ( rtime + ctime * glen )/steps);
				}
				rtime += ctime * glen;
				break;
			case 'f':
				tp = abc_locate_track(h, h->tpc->v, GCHORDFPOS);
				tp->instr = h->abcbassprog;
				nnum = c->par[chordbase]+12;
				abc_add_chordnote(h, tp, etime + rtime/steps, nnum, h->abcbassvol);
				rtime += ctime * glen;
				abc_add_noteoff(h,tp,etime + rtime/steps);
				break;
			case 'g':
			case 'h':
			case 'i':
			case 'j':
			case 'G':
			case 'H':
			case 'I':
			case 'J':
				i = toupper(gnote) - 'G';
				nnum = 0;
				if( i < chordlen[gcnum] ) {
					tp = abc_locate_track(h, h->tpc->v, GCHORDFPOS+i+1);
					tp->instr = h->abcchordprog;
					nnum = c->par[chordnote]+chordnotes[gcnum][i]+24;
					if( isupper(gnote) ) nnum -= 12;
					abc_add_chordnote(h, tp, etime + rtime/steps, nnum, h->abcchordvol);
				}
				rtime += ctime * glen;
				if( nnum ) abc_add_noteoff(h,tp,etime + rtime/steps);
				break;
			case 'z':
				rtime += ctime * glen;
				break;
		}
	}
}

// %%MIDI beat a b c n
//
// controls the way note velocities are selected. The first note in a bar has
// velocity a. Other "strong" notes have velocity b and all the rest have velocity
// c. a, b and c must be in the range 0-128. The parameter n determines which
// notes are "strong". If the time signature is x/y, then each note is given
// a position number k = 0, 1, 2 .. x-1 within each bar. Note that the units for
// n are not the unit note length. If k is a multiple of n, then the note is 
// "strong". The volume specifiers !ppp! to !fff! are equivalent to the 
// following :
//
// !ppp! = %%MIDI beat 30 20 10 1
// !pp!  = %%MIDI beat 45 35 20 1
// !p!   = %%MIDI beat 60 50 35 1
// !mp!  = %%MIDI beat 75 65 50 1
// !mf!  = %%MIDI beat 90 80 65 1
// !f!   = %%MIDI beat 105 95 80 1
// !ff!  = %%MIDI beat 120 110 95 1
// !fff! = %%MIDI beat 127 125 110 1
static void abc_MIDI_beat(ABCHANDLE *h, const char *p)
{
	int i,j;
	h->beat[0] = 127;
	h->beat[1] = 125;
	h->beat[2] = 110;
	h->beat[3] = 1;
	for( j=0; j<4; j++ ) {
		while( isspace(*p) ) p++;
		if( *p ) {
			p += abc_getnumber(p, &i);
			if( i < 0   ) i = 0;
			if( i > 127 ) i = 127;
			h->beat[j] = i;
		}
	}
	if( h->beat[3] == 0 ) h->beat[3] = 1; // BB Ruud says: do not let you make mad
}

// 
// %%MIDI beatstring <string of f, m and p>
// 
// This provides an alternative way of specifying where the strong and weak
// stresses fall within a bar. 'f' means velocity a (normally strong), 'm' 
// means velocity b (medium velocity) and 'p' means velocity c (soft velocity).
// For example, if the time signature is 7/8 with stresses on the first, fourth 
// and sixth notes in the bar, we could use the following 
//
// %%MIDI beatstring fppmpmp
static void abc_MIDI_beatstring(ABCHANDLE *h, const char *p)
{
	while( isspace(*p) ) p++;
	if( h->beatstring ) _mm_free(h->allochandle, h->beatstring);
	if( strlen(p) )
		h->beatstring = DupStr(h->allochandle,p,strlen(p)+1);
	else
		h->beatstring = NULL;
}

static int abc_beat_vol(ABCHANDLE *h, int abcvol, int barpos)
{
	int vol;
	if( h->beatstring ) {
		vol = (h->beat[2] * 9) / 10;
		if( barpos < (int)strlen(h->beatstring) ) {
			switch(h->beatstring[barpos]) {
				case 'f':
					vol = h->beat[0];
					break;
				case 'm':
					vol = h->beat[1];
					break;
				case 'p':
					vol = h->beat[2];
					break;
				default:
					break;
			}
		}
	}
	else {
		if( (barpos % h->beat[3]) == 0 ) {
			if( barpos )
				vol = h->beat[1];
			else
				vol = h->beat[0];
		}
		else
			vol = h->beat[2];
	}
	vol *= abcvol;
	vol /= 128;
	return vol;
}

static void abc_init_partpat(BYTE partp[27][2])
{
	int i;
	for( i=0; i<27; i++ ) {
		partp[i][0] = 0xff;
		partp[i][1] = 0;
	}
}

static int abc_partpat_to_orderlist(BYTE partp[27][2], const char *abcparts, ABCHANDLE *h, BYTE **list, int orderlen)
{
	int t, partsused;
	const char *p;
	BYTE *orderlist = *list;
	static int ordersize = 0;
	if( *list == NULL ) {
		ordersize = 128;
		orderlist = (BYTE *)_mm_calloc(h->ho, ordersize, sizeof(BYTE));
		*list = orderlist;
	}
	if( abcparts ) {
		partsused = 0;
		for( p = abcparts; *p; p++ ) {
			for( t = partp[*p - 'A'][0]; t < partp[*p - 'A'][1]; t++ ) {
				if( orderlen == ordersize ) {
					ordersize <<= 1;
					orderlist = (BYTE *)_mm_recalloc(h->ho, orderlist, ordersize, sizeof(BYTE));
					*list = orderlist;
				}
				orderlist[orderlen] = t;
				orderlen++;
				partsused++;
			}
		}
		if( partsused ) return orderlen;
	}
	// some fool wrote a P: string in the header but didn't use P: in the body
	for( t = partp[26][0]; t < partp[26][1]; t++ ) {
		if( orderlen == ordersize ) {
			ordersize <<= 1;
			orderlist = (BYTE *)_mm_recalloc(h->ho, orderlist, ordersize, sizeof(BYTE));
			*list = orderlist;
		}
		orderlist[orderlen] = t;
		orderlen++;
	}
	return orderlen;
}

static void abc_globalslide(ABCHANDLE *h, ULONG tracktime, int slide)
{
	ABCTRACK *tp;
	ABCEVENT *e;
	int hslide;
	hslide = h->track? h->track->slidevol: slide;
	for( tp=h->track; tp; tp = tp->next ) {
		if( slide ) {
			tp->slidevoltime = tracktime;
			if( slide == 2 )
				tp->slidevol = 0;
		}
		if( tp->slidevol > -2 && slide < 2 )
			tp->slidevol = slide;
	}
	if( h->track && h->track->tail 
	&& hslide != slide && slide == -2
	&& h->track->tail->tracktick >= tracktime ) {
		// need to update jumptypes in mastertrack from tracktime on...
		for( e=h->track->head; e; e=e->next )	{
			if( e->flg == 1 && e->cmd == cmdjump && e->tracktick >= tracktime ) {
				switch( e->par[jumptype] ) {
					case jumpnormal:
					case jumpfade:
						e->par[jumptype] = jumpfade;
						break;
					case jumpdacapo:
					case jumpdcfade:
						e->par[jumptype] = jumpdcfade;
						break;
					case jumpdasegno:
					case jumpdsfade:
						e->par[jumptype] = jumpdsfade;
						break;
				}
			}
		}
	}
}

static void abc_recalculate_tracktime(ABCHANDLE *h) {
	ABCTRACK *ttp;
	h->tracktime = 0;
	for( ttp=h->track; ttp; ttp=ttp->next )
		if( ttp->tail && ttp->tail->tracktick > h->tracktime )
			h->tracktime = ttp->tail->tracktick;
}

static void abc_MIDI_command(ABCHANDLE *h, char *p, char delim) {
	int t;
	// interpret some of the possibilitys
	if( !strncmp(p,"bassprog",8)    && isspace(p[8]) ) h->abcbassprog = abc_MIDI_getprog(p+8)+1;
	if( !strncmp(p,"bassvol",7)     && isspace(p[7]) ) h->abcbassvol = abc_MIDI_getnumber(p+7);
	if( !strncmp(p,"beat",4)        && isspace(p[4]) ) abc_MIDI_beat(h, p+4);
	if( !strncmp(p,"beatstring",10) && isspace(p[10]) ) abc_MIDI_beatstring(h, p+4);
	if( !strncmp(p,"chordname",9)   && isspace(p[9]) ) abc_MIDI_chordname(p+9);
	if( !strncmp(p,"chordprog",9)   && isspace(p[9]) ) h->abcchordprog = abc_MIDI_getprog(p+9)+1;
	if( !strncmp(p,"chordvol",8)    && isspace(p[8]) ) h->abcchordvol = abc_MIDI_getnumber(p+8);
	if( !strncmp(p,"drone",5)       && isspace(p[5]) ) abc_MIDI_drone(p+5, &h->dronegm, h->dronepitch, h->dronevol);
	if( !strncmp(p,"droneoff",8)    && (p[8]=='\0' || p[8]==delim || isspace(p[8])) ) h->droneon = 0;
	if( !strncmp(p,"droneon",7)     && (p[7]=='\0' || p[7]==delim || isspace(p[7])) ) h->droneon = 1;
	t = h->drumon;
	if( !strncmp(p,"drum",4)        && isspace(p[4]) ) {
		h->drumon = abc_MIDI_drum(p+4, h);
		if( h->drumon ) --h->drumon;
		else h->drumon = t;
	}
	if( !strncmp(p,"drumoff",7)     && (p[7]=='\0' || p[7]==delim || isspace(p[7])) ) h->drumon = 0;
	if( !strncmp(p,"drumon",6)      && (p[6]=='\0' || p[6]==delim || isspace(p[6])) ) h->drumon = 1;
	if( t != h->drumon ) {
		if( h->drumon && !h->tpr ) h->tpr = h->track;
		if( h->tpr ) abc_add_drum_sync(h, h->tpr, h->tracktime); // don't start drumming from the beginning of time!
		if( h->tpr && !h->drumon ) h->tpr = NULL;
	}
	t = h->gchordon;
	if( !strncmp(p,"gchord",6)      && (p[6]=='\0' || p[6]==delim || isspace(p[6])) ) {
		h->gchordon = abc_MIDI_gchord(p+6, h);
		if( h->gchordon ) --h->gchordon;
		else h->gchordon = t;
	} 
	if( !strncmp(p,"gchordoff",9)   && (p[9]=='\0' || p[9]==delim || isspace(p[9])) ) h->gchordon = 0;
	if( !strncmp(p,"gchordon",8)    && (p[8]=='\0' || p[8]==delim || isspace(p[8])) ) h->gchordon = 1;
	if( t != h->gchordon ) {
		if( h->tpc ) abc_add_gchord_syncs(h, h->tpc, h->tracktime);
	}
	if( !strncmp(p,"channel",7)     && isspace(p[7]) )
		abc_MIDI_channel(p+8, h->tp = abc_check_track(h, h->tp), h);
	if( !strncmp(p,"program",7)     && isspace(p[7]) )
		abc_MIDI_program(p+8, h->tp = abc_check_track(h, h->tp), h);
	if( !strncmp(p,"voice",5)       && isspace(p[5]) )
		abc_MIDI_voice(p+6, h->tp = abc_check_track(h, h->tp), h);
	if( !strncmp(p,"legato",6)      && (p[6]=='\0' || p[6]==delim || isspace(p[6])) )
		abc_MIDI_legato(p+6, h->tp = abc_check_track(h, h->tp));
}

// continuate line that ends with a backslash, can't do this in abc_gets because voice lines
// can have comment lines in between that must be parsed properly, for example:
//  [V:1] cdef gabc' |\ << continuation backslash
//  %%MIDI program 25
//        c'bag fedc |
// informational lines can have this too, so it is rather convoluted code...
static char *abc_continuated(ABCHANDLE *h, MMFILE *mmf, char *p) {
	char *pm, *p1, *p2 = 0;
	int continued;
	pm = p;
	while( pm[strlen(pm)-1]=='\\' ) {
		p1 = strdup(pm);
		if( p2 ) free(p2);
		continued = 1;
		while( continued ) {
			continued = 0;
			pm = abc_gets(h, mmf);
			if( !pm ) {
				abc_message("line not properly continued\n%s", p1);
				return p1;
			}
			while( *pm && isspace(*pm) ) ++pm;
			if( !strncmp(pm,"%%",2) ) {
				for( p2 = pm+2; *p2 && isspace(*p2); p2++ ) ;
				if( !strncmp(p2,"MIDI",4) && (p2[4]=='=' || isspace(p2[4])) ) {
					for( p2+=5; *p2 && isspace(*p2); p2++ ) ;
					if( *p2 == '=' )
						for( p2+=1; *p2 && isspace(*p2); p2++ ) ;
					abc_MIDI_command(h,p2,'%');
				}
				continued = 1;
			}
		}
		p2 = (char *)malloc(strlen(p1)+strlen(pm));
		if( !p2 ) {
			abc_message("macro line too long\n%s", p1);
			return p1;
		}
		p1[strlen(p1)-1] = '\0'; // strip off the backslash
		strcpy(p2,p1);
		strcat(p2,pm);
		pm = p2;
		free(p1);
	}
	return pm;
}

// =====================================================================================
#ifdef NEWMIKMOD
BOOL ABC_Load(ABCHANDLE *h, UNIMOD *of, MMSTREAM *mmfile)
#else
BOOL CSoundFile::ReadABC(const BYTE *lpStream, DWORD dwMemLength)
#endif
{
	static int avoid_reentry = 0;
#ifdef NEWMIKMOD
#define m_nDefaultTempo	of->inittempo
#else
	ABCHANDLE *h;
	UINT numpat;
	MMFILE mm, *mmfile;
#endif
	UINT t;
	char	*line, *p, *pp, ch, ch0=0;
	char barsig[52];	// for propagated accidental key signature within bar
	char *abcparts;
	BYTE partpat[27][2], *orderlist;
	int orderlen;
	enum { NOWHERE, INBETWEEN, INHEAD, INBODY, INSKIPFORX, INSKIPFORQUOTE } abcstate;
	ABCEVENT_JUMPTYPE j;
	ABCEVENT_X_EFFECT abceffect;
	int abceffoper;
	int abcxcount=0, abcxwanted=0, abcxnumber=1;
	int abckey, abcrate, abcchord, abcvol, abcbeatvol, abcnoslurs, abcnolegato, abcfermata, abcarpeggio, abcto;
	int abctempo;
	int cnotelen=0, cnotediv=0, snotelen, snotediv, mnotelen, mnotediv, notelen, notediv; 
	// c for chords, s for standard L: setting, m for M: barlength
	int abchornpipe, brokenrithm, tupletp, tupletq, tupletr;
	int ktempo;
	ULONG abcgrace=0, bartime, thistime=0;
	ABCTRACK *tpd, *ttp;
	ABCMACRO *mp;
	int mmsp;
#ifdef NEWMIKMOD
	MMSTREAM *mmstack[MAXABCINCLUDES];
	h->ho = _mmalloc_create("Load_ABC_ORDERLIST", NULL);
#else
	MMFILE *mmstack[MAXABCINCLUDES];
	if( !TestABC(lpStream, dwMemLength) ) return FALSE;
	h = ABC_Init();
	if( !h ) return FALSE;
	mmfile = &mm;
	mm.mm = (char *)lpStream;
	mm.sz = dwMemLength;
	mm.pos = 0;
#endif
	while( avoid_reentry ) sleep(1);
	avoid_reentry = 1;
	pat_resetsmp();
	pat_init_patnames();
	m_nDefaultTempo = 0;
	global_voiceno = 0;
	abckey = 0;
	h->tracktime = 0;
	global_songstart = 0;
	h->speed = 6;
	abcrate = 240;
	global_tempo_factor = 2;
	global_tempo_divider = 1;
	abctempo = 0;
	ktempo = 0;
	abceffect = none;
	abceffoper = 0;
	abcvol = 120;
	h->abcchordvol  = abcvol;
	h->abcbassvol   = abcvol;
	h->abcchordprog = 25; // acoustic guitar
	h->abcbassprog  = 33; // acoustic bass
	abcparts = 0;
	abcnoslurs = 1;
	abcnolegato = 1;
	abcfermata = 0;
	abcarpeggio = 0;
	abcto = 0;
	snotelen = 0;
	snotediv = 0;
	mnotelen = 1;
	mnotediv = 1;
	abchornpipe = 0;
	brokenrithm = 0;
	tupletp = 0;
	tupletq = 0;
	tupletr = 0;
	h->ktrans = 0;
	h->drumon = 0;
	h->gchordon = 1;
	h->droneon = 0;
	h->tracktime = 0;
	bartime = 0;
	h->tp = NULL;
	h->tpc = NULL;
	h->tpr = NULL;
	tpd = NULL;
	h->dronegm       = 71;
	h->dronepitch[0] = 45;
	h->dronepitch[1] = 33;
	h->dronevol[0]   = 80;
	h->dronevol[1]   = 80;
	abc_new_umacro(h, "v = +downbow+");
	abc_new_umacro(h, "u = +upbow+");
	abc_new_umacro(h, "O = +coda+");
	abc_new_umacro(h, "S = +segno+");
	abc_new_umacro(h, "P = +uppermordent+");
	abc_new_umacro(h, "M = +lowermordent+");
	abc_new_umacro(h, "L = +emphasis+");
	abc_new_umacro(h, "H = +fermata+");
	abc_new_umacro(h, "T = +trill+");
	abc_new_umacro(h, "~ = +roll+");
	abc_setup_chordnames();
	abc_init_partpat(partpat);
	abc_MIDI_beat(h, ""); // reset beat array
	abc_MIDI_beatstring(h, ""); // reset beatstring
	orderlist  = NULL;
	orderlen   = 0;
	mmsp       = 1;
	mmstack[0] = mmfile;
	mmfseek(mmfile,0,SEEK_SET);
	abcstate = NOWHERE;
	if( h->pickrandom ) {
		abcstate = INSKIPFORX;
		abcxcount = 0;
		mmfseek(mmfile,0,SEEK_SET);
		while( (line=abc_gets(h, mmfile)) ) {
			for( p=line; isspace(*p); p++ ) ;
			if( !strncmp(p,"X:",2) ) abcxcount++;
		}
		if( abcxcount == 0 )
			abcstate = NOWHERE;
		else
			abcxwanted = (h->pickrandom - 1) % abcxcount;
		abcxcount = 0;
		mmfseek(mmfile,0,SEEK_SET);
	}
	while( mmsp > 0 ) {
		mmsp--;
		while((line=abc_gets(h, mmstack[mmsp]))) {
			for( p=line; isspace(*p); p++ ) ;
			switch(abcstate) {
				case INSKIPFORX:
					if( !strncmp(p,"X:",2) ) {
						if( abcxcount++ != abcxwanted )
							break;
					}
					// fall through
				case INBETWEEN:
					if( !strncmp(p,"X:",2) ) {
						abcstate = INHEAD;
#ifdef NEWMIKMOD
						of->songname = NULL;
#else
						memset(m_szNames[0], 0, 32);
#endif
						for( p+=2; isspace(*p); p++ ) ;
						abcxnumber = atoi(p);
						abchornpipe = 0;
						h->droneon = 0;
						h->dronegm       = 71;
						h->dronepitch[0] = 45;
						h->dronepitch[1] = 33;
						h->dronevol[0]   = 80;
						h->dronevol[1]   = 80;
						for( ttp = h->track; ttp; ttp=ttp->next ) {
							ttp->vno = 0;	// mark track unused
							ttp->capostart = NULL;
						}
						h->tp  = NULL; // forget old voices
						h->tpc = NULL;
						h->tpr = NULL;
						global_voiceno = 0;
						abc_set_parts(&abcparts, 0);
						abcgrace = 0;
						h->ktrans = 0;
						ktempo = 0;
						h->gchordon = 1;
						h->drumon = 0;
						global_songstart = h->tracktime;
						abc_MIDI_beat(h, ""); // reset beat array
						abc_MIDI_beatstring(h, ""); // reset beatstring
						strcpy(h->gchord, ""); // reset gchord string
						abcnolegato = 1; // reset legato switch
					}
					break;
				case NOWHERE:
					if( p[0] != '\0' && p[1] == ':' ) {
						abcstate = INHEAD;
						abc_set_parts(&abcparts, 0);
						strcpy(h->gchord, "");
						if( h->drumon && h->tpr ) abc_add_drum_sync(h, h->tpr, h->tracktime);
						if( h->tpc && !h->gchordon ) abc_add_gchord_syncs(h, h->tpc, h->tracktime);
						h->gchordon = 1;
						h->drumon = 0;
					}
					else
						break;
				case INHEAD:
					if( !strncmp(p,"L:",2) ) {
						sscanf(p+2," %d / %d", &snotelen, &snotediv);
						break;
					}
					if( !strncmp(p,"M:",2) ) {
						abc_M_field(p+2, &mnotelen, &mnotediv);
						break;
					}
					if( !strncmp(p,"P:",2) ) {
						abc_set_parts(&abcparts, p+2);
						break;
					}
					if( !strncmp(p,"Q:",2) ) {
						abctempo = abc_extract_tempo(p+2,0);
						ktempo = 1;
						if( h->track ) {
							// make h->tracktime start of a new age...
							abc_add_partbreak(h, h->track, h->tracktime);
							abc_add_tempo_event(h, h->track, h->tracktime, abctempo);
						}
						if( m_nDefaultTempo == 0 ) m_nDefaultTempo = abctempo;
						break;
					}
					if( !strncmp(p,"T:",2) ) {
						char buf[200];
						if( strchr(p,'%') ) *strchr(p,'%') = '\0';
						for( t=strlen(p)-1; isspace(p[t]); t-- )
							p[t]='\0';
						for( t=2; isspace(p[t]); t++ ) ;
#ifdef NEWMIKMOD
						if( of->songname )
							strcpy(buf,of->songname);
						else
							strcpy(buf,"");
#else
						strcpy(buf,m_szNames[0]);
#endif
						if( strlen(buf) + strlen(p+t) > 199 ) p[t+199-strlen(buf)] = '\0'; // chop it of
						if( strlen(buf) ) strcat(buf," "); // add a space
						strcat(buf, p+t);
#ifdef NEWMIKMOD
						of->songname = DupStr(of->allochandle, buf, strlen(buf));
#else
						if( strlen(buf) > 31 ) buf[31] = '\0'; // chop it of
						strcpy(m_szNames[0], buf);
#endif
						break;
					}
					if( !strncmp(p,"R:",2) ) {
						for( p+=2; isspace(*p); p++ ) ;
						if( !strncmp(p,"hornpipe",8) && (isspace(p[8]) || p[8]=='\0') ) abchornpipe = 1;
						else abchornpipe = 0;
						break;
					}
					if( !strncmp(p,"V:",2) ) {
						for( t=2; p[t]==' '; t++ ) ;
						h->tp = abc_locate_track(h, p+t, 0);
						abcvol = h->tp->volume;
						abcnolegato = !h->tp->legato;
						if( !abcnolegato ) abcnoslurs = 0;
						break;
					}
					if( !strncmp(p,"K:",2) ) {
						abcstate = INBODY;
						abckey = ABC_Key(p+2);
						sprintf(barsig, "%s%s", sig[abckey], sig[abckey]);	// reset the key signature
						p = abc_skip_word(p+2);
						h->ktrans = abc_transpose(p);
						*p = '%'; // force skip rest of line
						if( snotelen == 0 ) {	// calculate default notelen from meter M:
							if( mnotediv == 0 ) mnotediv = mnotelen = 1;	// do'nt get nuked
							snotelen = 100 * mnotelen / mnotediv;
							if( snotelen > 74 ) 
								snotediv = 8;
							else
								snotediv = 16;
							snotelen = 1;
						}
						abceffect = none;
						abceffoper = 0;
						if( !(snotelen == 1 && snotediv == 8) ) abchornpipe = 0; // no matter what they said at R:
						brokenrithm = 0;
						global_part = ' ';
						abcgrace = 0;
						abcnoslurs = abcnolegato;
						abcto = 0;
						h->tpc = NULL; // reset chord track
						tpd = NULL; // reset drone track
						h->tpr = NULL; // reset drum  track
						if( !strlen(h->gchord) ) abc_metric_gchord(h, mnotelen, mnotediv);
						h->barticks = notelen_notediv_to_ticks(h->speed, mnotelen, mnotediv);
						if( abctempo && !ktempo ) {	// did not set tempo in this songpiece so reset to abcrate
							abctempo = 0;
							global_tempo_factor = 2;
							global_tempo_divider = 1;
							if( h->track ) {
								// make h->tracktime start of a new age...
								abc_add_partbreak(h, h->track, h->tracktime);
								abc_add_tempo_event(h, h->track, h->tracktime, abcrate);
							}
							if( m_nDefaultTempo == 0 ) m_nDefaultTempo = abcrate;
						}
						abc_init_partpat(partpat);
						partpat[26][0] = abc_patno(h, h->tracktime);
						partpat[26][1] = 0;
						abc_globalslide(h, h->tracktime, 2); // reset all volumeslides
						break;
					}
					if( !strlen(p) )
						abcstate = INBETWEEN;
					break;
				case INSKIPFORQUOTE:
						while( (ch=*p++) && (ch != '"') )
							;
						if( !ch ) break;
						abcstate = INBODY;
						// fall through
				case INBODY:
					if( !strlen(p) && h->track ) { // end of this song
						abcstate = h->pickrandom? INSKIPFORX: INBETWEEN;
						// last but not least shut off all pending events
						abc_recalculate_tracktime(h);
						for( ttp=h->track; ttp; ttp=ttp->next )
							abc_add_noteoff(h,ttp,h->tracktime);
						abc_add_partbreak(h, h->track, h->tracktime);
						t = abc_patno(h, h->tracktime);
						if( abc_pattracktime(h, h->tracktime) % abcticks(64 * h->speed) ) t++;
						if(	global_part == ' ' ) {
							partpat[26][1] = t;
							if( abcparts ) {
								for( t=0; t<26; t++ )
									if( partpat[t][0] < partpat[t][1] ) break;
								if( t == 26 ) {
									abc_message("parts (%s) set but not used", abcparts);
									abc_set_parts(&abcparts, 0);	// forget the parts array
								}
							}
						}
						else
							partpat[global_part - 'A'][1] = t;
						if( !abcparts ) abc_song_to_parts(h, &abcparts, partpat);
						orderlen = abc_partpat_to_orderlist(partpat, abcparts, h, &orderlist, orderlen);
					}
					if( !strncmp(p,"V:",2) ) {
						for( t=2; p[t]==' '; t++ ) ;
						h->tp = abc_locate_track(h, p+t, 0);
						sprintf(barsig, "%s%s", sig[abckey], sig[abckey]);	// reset the key signature
						abcgrace = 0;
						brokenrithm = 0;
						h->tracktime = abc_tracktime(h->tp);
						bartime = h->tracktime; // it is not friendly to break voices in the middle of a track...
						abcnolegato = !h->tp->legato;
						if( !abcnolegato ) abcnoslurs = 0;
						*p = '%';	// make me skip the rest of the line....
					}
					if( !strncmp(p,"K:",2) ) {
						abckey = ABC_Key(p+2);
						sprintf(barsig, "%s%s", sig[abckey], sig[abckey]);	// reset the key signature
						p = abc_skip_word(p+2);
						h->ktrans = abc_transpose(p);
						*p = '%';	// make me skip the rest of the line....
					}
					if( !strncmp(p,"L:",2) ) {
						sscanf(p+2," %d / %d", &snotelen, &snotediv);
						*p = '%';	// make me skip the rest of the line....
					}
					if( !strncmp(p,"M:",2) ) {
						abc_M_field(p+2, &mnotelen, &mnotediv);
						h->barticks = notelen_notediv_to_ticks(h->speed, mnotelen, mnotediv);
						*p = '%';	// make me skip the rest of the line....
					}
					if( !strncmp(p,"Q:",2) ) {
						abctempo = abc_extract_tempo(p+2,ch0=='\\');
						if( !h->track ) {
							h->tp = abc_check_track(h, h->track);
							h->tp->vno = 0; // mark reuseable (temporarely, until first notes come up)
						}
						abc_add_tempo_event(h, h->track, h->tracktime, abctempo);
						*p = '%';	// make me skip the rest of the line....
					}
					if( !strncmp(p,"T:",2) ) {
						char buf[200];
						if( strchr(p,'%') ) *strchr(p,'%') = '\0';
						for( t=strlen(p)-1; isspace(p[t]); t-- )
							p[t]='\0';
						for( t=2; isspace(p[t]); t++ ) ;
#ifdef NEWMIKMOD
						if( of->songname )
							strcpy(buf,of->songname);
						else
							strcpy(buf,"");
#else
						strcpy(buf,m_szNames[0]);
#endif
						if( strlen(buf) + strlen(p+t) > 198 ) p[t+198-strlen(buf)] = '\0'; // chop it of
						if( strlen(buf) ) strcat(buf," "); // add a space
						strcat(buf, p+t);
#ifdef NEWMIKMOD
						of->songname = DupStr(of->allochandle, buf, strlen(buf));
#else
						if( strlen(buf) > 31 ) buf[31] = '\0'; // chop it of
						strcpy(m_szNames[0], buf);
#endif
						*p = '%';	// make me skip the rest of the line....
					}
					break;
			}
			if( !strncmp(p,"m:",2) ) {
				if( abcstate != INSKIPFORX ) {
					char *pm;
					pm = abc_continuated(h, mmstack[mmsp], p);
					abc_new_macro(h, pm+2);
					if( pm != p ) {
						free(pm);
						if( h->tp ) abcnolegato = !h->tp->legato;
						if( !abcnolegato ) abcnoslurs = 0;
					}
				}
				*p = '%'; // skip rest of line
			}
			if( !strncmp(p,"U:",2) ) {
				abc_new_umacro(h, p+2);
				*p = '%'; // skip rest of line
			}
			if( !strncmp(p,"w:",2) ) {	// inline lyrics
				*p = '%'; // skip rest of line
			}
			if( !strncmp(p,"W:",2) ) {	// lyrics at end of song body
				*p = '%'; // skip rest of line
			}
			if( !strncmp(p,"d:",2) ) {	// oldstyle decorations
				abc_message("warning: old style decorations not handled\n%s", p);
				*p = '%'; // skip rest of line
			}
			if( !strncmp(p,"s:",2) ) {	// newstyle decorations (symbols)
				abc_message("warning: new style decorations not handled\n%s", p);
				*p = '%'; // skip rest of line
			}
			if( !strncmp(p,"I:",2) && abcstate != INSKIPFORX ) { // handle like oldstyle '%%command' lines
				p[0]= '%';
				p[1]= '%';
			}
			if( !strncmp(p,"%%",2) ) {
				for( p+=2; *p && isspace(*p); p++ ) ;
				if( !strncmp(p,"abc-include",11) && isspace(p[11]) ) {
					for( t=12; isspace(p[t]); t++ ) ;
					if( p[t] ) {
						mmsp++;
						if( mmsp == MAXABCINCLUDES ) {
							mmsp--;
							abc_message("failure: too many abc-include's, %s", &p[t]);
						} else {
							mmstack[mmsp] = mmfopen(&p[t], "r");
							if( !mmstack[mmsp] ) {
								mmsp--;
								abc_message("failure: abc-include file %s not found", &p[t]);
							}
						}
					}
					else abc_message("failure: abc-include missing file name, %s", p);
				}
				if( !strncmp(p,"MIDI",4) && (p[4]=='=' || isspace(p[4])) && abcstate != INSKIPFORX ) {
					for( p+=5; *p && isspace(*p); p++ ) ;
					if( *p == '=' )
						for( p+=1; *p && isspace(*p); p++ ) ;
					abc_MIDI_command(h,p,'%');
					if( h->tp ) abcnolegato = !h->tp->legato;
					if( !abcnolegato ) abcnoslurs = 0;
				}
				if(*p) *p = '%'; // skip rest of line
			}
			if( abcstate == INBODY ) {
				if( *p == 'P' && p[1] == ':' ) {	// a line with a part indication
					if( abcparts != NULL ) {
						// make h->tracktime start of a new age...
						if( !h->track ) {
							h->tp = abc_check_track(h, h->track);
							h->tp->vno = 0; // mark reuseable (temporarely, until first notes come up)
						}
						h->tracktime = h->track? abc_tracktime(h->track): 0; // global parts are voice independent
						abc_add_partbreak(h, h->track, h->tracktime);
						t = abc_patno(h, h->tracktime);
						if(	global_part == ' ' ) {
							partpat[26][1] = t;
							if( abcparts ) {
								for( t=0; t<26; t++ )
									if( partpat[t][0] < partpat[t][1] ) break;
								if( t == 26 ) {
									abc_message("parts (%s) set but not used", abcparts);
									abc_set_parts(&abcparts, 0);	// forget the parts array
								}
							}
						}
						else
							partpat[global_part - 'A'][1] = t;
						// give every new coming abcevent the desired part indication
						while( p[2]==' ' || p[2]=='.' ) p++;	// skip blancs and dots
						if( isupper(p[2]) )
							global_part = p[2];
						else 
							global_part = ' ';
						if(	global_part == ' ' )
							partpat[26][0] = t;
						else
							partpat[global_part - 'A'][0] = t;
					}
					*p = '%';	// make me skip the rest of the line....
				}
				if( h->droneon && !tpd ) {
					tpd = h->track;
					if( tpd ) {
						tpd = abc_locate_track(h, tpd->v, DRONEPOS1);
						tpd->instr = h->dronegm;
						abc_add_dronenote(h, tpd, h->tracktime, h->dronepitch[0], h->dronevol[0]);
						tpd = abc_locate_track(h, tpd->v, DRONEPOS2);
						tpd->instr = h->dronegm;
						abc_add_dronenote(h, tpd, h->tracktime, h->dronepitch[1], h->dronevol[1]);
					}
				}
				if( tpd && !h->droneon ) {
					tpd = abc_locate_track(h, tpd->v, DRONEPOS1);
					abc_add_noteoff(h, tpd, h->tracktime);
					tpd = abc_locate_track(h, tpd->v, DRONEPOS2);
					abc_add_noteoff(h, tpd, h->tracktime);
					tpd = NULL;
				}
				if( h->drumon && !h->tpr ) {
					h->tpr = h->track;
					if( h->tpr ) abc_add_drum_sync(h, h->tpr, h->tracktime); // don't start drumming from the beginning of time!
				}
				if( h->tpr && !h->drumon ) h->tpr = NULL;
				if( *p != '%' ) {	// skip uninteresting lines
					// plough thru the songline gathering mos....
					ch0 = ' ';
					pp = 0;
					while( (ch = *p++) ) {
						if( isalpha(ch) && *p != ':' ) { // maybe a macro
							for( mp=h->umacro; mp; mp=mp->next ) {
								if( ch == mp->name[0] ) {
									pp = p;
									p = mp->subst;
									ch = *p++;
									break;
								}
							}
						}
						switch(ch) {
							case '%':
								abcto = 0;
								while( *p ) p++;
								break;
							case '[':	// chord follows or some inline field
								abcto = 0;
								if( *p=='|' ) break; // [| a thick-thin bar line, loop around and let case '|' handle it
								if( !strncmp(p,"V:",2) ) {	// inline voice change
									for( t=2; isspace(p[t]); t++ ) ;
									h->tp = abc_locate_track(h, p+t, 0);
									for( ; *p && *p != ']'; p++ ) ;
									abcgrace = 0;
									brokenrithm = 0;
									sprintf(barsig, "%s%s", sig[abckey], sig[abckey]);	// reset the key signature
									h->tracktime = abc_tracktime(h->tp);
									bartime = h->tracktime; // it is not wise to break voices in the middle of a track...
									abcvol = h->tp->volume;
									abcnolegato = !h->tp->legato;
									if( !abcnolegato ) abcnoslurs = 0;
									break;
								}
								if( !strncmp(p,"K:",2) ) {
									abckey = ABC_Key(p+2);
									sprintf(barsig, "%s%s", sig[abckey], sig[abckey]);	// reset the key signature
									p = abc_skip_word(p+2);
									h->ktrans = abc_transpose(p);
									for( ; *p && *p != ']'; p++ ) ;
									break;
								}
								if( !strncmp(p,"M:",2) ) {
									abc_M_field(p+2, &mnotelen, &mnotediv);
									for( ; *p && *p != ']'; p++ ) ;
									h->barticks = notelen_notediv_to_ticks(h->speed, mnotelen, mnotediv);
									break;
								}
								if( !strncmp(p,"P:",2) ) {	// a [P:X] field inline
									if( abcparts != NULL ) {
										// make h->tracktime start of a new age...
										abc_add_partbreak(h, h->track, h->tracktime);
										t = abc_patno(h, h->tracktime);
										if(	global_part == ' ' )
											partpat[26][1] = t;
										else
											partpat[global_part - 'A'][1] = t;
										// give every new coming abcevent the desired part indication
										while( isspace(p[2]) || p[2]=='.' ) p++;	// skip blancs and dots
										if( isupper(p[2]) )
											global_part = p[2];
										else 
											global_part = ' ';
										if(	global_part == ' ' )
											partpat[26][0] = t;
										else
											partpat[global_part - 'A'][0] = t;
									}
									for( ; *p && *p != ']'; p++ ) ;
									break;
								}
								if( !strncmp(p,"Q:",2) ) {
									abctempo = abc_extract_tempo(p+2,1);
									for( ; *p && *p != ']'; p++ ) ;
									abc_add_tempo_event(h, h->track, h->tracktime, abctempo);
									break;
								}
								if( !strncmp(p,"I:",2) ) { // interpret some of the possibilitys
									for( p += 2; isspace(*p); p++ ) ;
									if( !strncmp(p,"MIDI",4) && (p[4]=='=' || isspace(p[4])) ) { // interpret some of the possibilitys
										for( p += 4; isspace(*p); p++ ) ;
										if( *p == '=' )
											for( p += 1; isspace(*p); p++ ) ;											
										abc_MIDI_command(h, p, ']');
										if( h->tp ) abcnolegato = !h->tp->legato;
										if( !abcnolegato ) abcnoslurs = 0;
									}
									for( ; *p && *p != ']'; p++ ) ; // skip rest of inline field
								}
								if( *p && p[1] == ':' ) { // some other kind of inline field
									for( ; *p && *p != ']'; p++ ) ;
									break;
								}
								if( *p && strchr("abcdefgABCDEFG^_=",*p) ) {
									int cnl[8],cnd[8],vnl,nl0=0,nd0=0;	// for chords with notes of varying length
									abcchord = 0;
									vnl = 0;
									h->tp = abc_check_track(h, h->tp);
									abc_track_clear_tiedvpos(h);
									abcbeatvol = abc_beat_vol(h, abcvol, (h->tracktime - bartime)/notelen_notediv_to_ticks(h->speed,1,mnotediv));
									while( (ch=*p++) && (ch != ']') ) {
										h->tp = abc_locate_track(h, h->tp->v, abcchord? abcchord+DRONEPOS2: 0);
										p += abc_add_noteon(h, ch, p, h->tracktime, barsig, abcbeatvol, abceffect, abceffoper);
										p += abc_notelen(p, &notelen, &notediv);
										if( *p == '-' ) {
											p++;
											if( h->tp->tail->flg != 1 )
											h->tp->tienote = h->tp->tail;
										}
										if( abcchord<8 ) {
											cnl[abcchord] = notelen;
											cnd[abcchord] = notediv;
										}
										if( abcchord==0 ) {
											cnotelen = notelen;
											cnotediv = notediv;
											nl0 = notelen;
											nd0 = notediv;
										}
										else {
											if( cnotelen != notelen || cnotediv != notediv ) {
												vnl = 1;
												// update to longest duration
												if( cnotelen * notediv < notelen * cnotediv ) {
													cnotelen = notelen;
													cnotediv = notediv;
													abc_track_untie_short_chordnotes(h);
												}
												if( cnotelen * notediv > notelen * cnotediv ) {
													if( h->tp->tienote ) {
														abc_message("short notes in chord can not be tied:\n%s", h->line);
														h->tp->tienote = 0; // short chord notes cannot be tied...
													}
												}
												// update to shortest duration
												if( nl0 * notediv > notelen * nd0 ) {
													nl0 = notelen;
													nd0 = notediv;
												}
											}
										}
										abcchord++;
									}
									p += abc_notelen(p, &notelen, &notediv);
									if( (ch = *p) == '-' ) p++;	// tied chord...
									if( abcarpeggio ) {	// update starttime in the noteon events...
										thistime = notelen_notediv_to_ticks(h->speed, nl0*notelen*snotelen, nd0*notediv*snotediv)/abcchord;
										if( thistime > abcticks(h->speed) ) thistime = abcticks(h->speed);
										for( nl0=1; nl0<abcchord; nl0++ ) {
											h->tp = abc_locate_track(h, h->tp->v, nl0+DRONEPOS2);
											h->tp->tail->tracktick = h->tracktime + thistime * nl0;
										}
									}	
									notelen *= cnotelen;
									notediv *= cnotediv;
									tupletr = abc_tuplet(&notelen, &notediv, tupletp, tupletq, tupletr);
									while( isspace(*p) ) p++;	// allow spacing in broken rithm notation
									p += abc_brokenrithm(p, &notelen, &notediv, &brokenrithm, abchornpipe);
									thistime = notelen_notediv_to_ticks(h->speed, notelen*snotelen, notediv*snotediv);
									if( abcfermata ) {
										thistime <<= 1;
										abcfermata = 0;
									}
									if( thistime > abcgrace ) {
										thistime -= abcgrace;
										abcgrace = 0;
									}
									else {
										abcgrace -= thistime;
										thistime = abcticks(h->speed);
										abcgrace += abcticks(h->speed);
									}
									h->tracktime += thistime; 
									while( abcchord>0 ) {
										abcchord--;
										h->tp = abc_locate_track(h, h->tp->v, abcchord? abcchord+DRONEPOS2: 0);
										if( vnl && (abcchord < 8) && (cnl[abcchord] != cnotelen || cnd[abcchord] != cnotediv) ) {
											abc_add_noteoff(h, h->tp, 
												h->tracktime - thistime
											 	+ (thistime * cnl[abcchord] * cnotediv)/(cnd[abcchord] * cnotelen) );
										}
										else {
											if( ch=='-' && h->tp->tail->flg != 1 )
												h->tp->tienote = h->tp->tail;	// copy noteon event to tienote in track
											if( thistime > abcticks(h->speed) )
												abc_add_noteoff(h, h->tp, h->tracktime - abcnoslurs);
											else
												abc_add_noteoff(h, h->tp, h->tracktime);
										}
									}
									if( h->gchordon && (h->tp == h->tpc) )	
										abc_add_gchord(h, h->tracktime, bartime);
									if( h->drumon && (h->tp == h->tpr) )	
										abc_add_drum(h, h->tracktime, bartime);
									abcarpeggio = 0;
									if( abceffoper != 255 ) abceffect = none;
									break;
								}
								if( isdigit(*p) ) {	// different endings in repeats [i,j,n-r,s,...
									h->tp = abc_check_track(h, h->tp);
									abc_add_partbreak(h, h->tp, h->tracktime);
									p += abc_getnumber(p, &notelen);
									abc_add_variant_start(h, h->tp, h->tracktime, notelen);
									while( *p==',' || *p=='-' ) {
										if( *p==',' ) {
											p++;
											p += abc_getnumber(p, &notelen);
											abc_add_variant_choise(h->tp, notelen);
										}
										else {
											p++;
											p += abc_getnumber(p, &notediv);
											while( notelen < notediv ) {
												notelen++;
												abc_add_variant_choise(h->tp, notelen);
											}
										}
									}
									break;
								}
								// collect the notes in the chord
								break;
							case '(':	// slurs follow or some tuplet (duplet, triplet etc.)
								abcto = 0;
								if( isdigit(*p) ) {
									p += abc_getnumber(p,&tupletp);
									tupletr = tupletp;	// ABC draft 2.0 (4.13): if r is not given it defaults to p
									switch( tupletp ) {	// ABC draft 2.0 (4.13): q defaults depending on p and time signature
										case 2: case 4: case 8:
											tupletq = 3;
											break;
										case 3: case 6:
											tupletq = 2;
											break;
										default:
											if( snotediv == 8 )
												tupletq = 3;
											else
												tupletq = 2;
											break;
									}
									if( *p==':' ) {
										p++;
										if( isdigit(*p) ) p += abc_getnumber(p,&tupletq);
										if( *p==':' ) {
											p++;
											if( isdigit(*p) ) p += abc_getnumber(p,&tupletr);
										}
									}
								}
								else 
									abcnoslurs=0;
								break;
							case ')':	// end of slurs
								abcto = 0;
								abcnoslurs = abcnolegato;
								break;
							case '{':	// grace notes follow
								abcto = 0;
								h->tp = abc_check_track(h, h->tp);
								abc_track_clear_tiedvpos(h);
								abcgrace = 0;
								abcbeatvol = abc_beat_vol(h, abcvol, (h->tracktime - bartime)/notelen_notediv_to_ticks(h->speed,1,mnotediv));
								while( (ch=*p++) && (ch != '}') ) {
									p += abc_add_noteon(h, ch, p, h->tracktime+abcgrace, barsig, abcbeatvol, none, 0);
									p += abc_notelen(p, &notelen, &notediv);
									if( *p=='-' ) {
										p++;
										if( h->tp->tail->flg != 1 )
										h->tp->tienote = h->tp->tail;
									}
									notediv *= 4;	// grace notes factor 4 shorter (1/8 => 1/32)
									abcgrace += notelen_notediv_to_ticks(h->speed, notelen*snotelen, notediv*snotediv);
									abc_add_noteoff(h, h->tp, h->tracktime + abcgrace);
								}
								h->tracktime += abcgrace;
								abc_add_sync(h, h->tp, h->tracktime);
								if( h->gchordon && (h->tp == h->tpc) )	
									abc_add_gchord(h, h->tracktime, bartime);
								if( h->drumon && (h->tp == h->tpr) )	
									abc_add_drum(h, h->tracktime, bartime);
								break;
							case '|':	// bar symbols
								abcto = 0;
								if( h->gchordon && h->tp && (h->tp == h->tpc) )
									abc_add_gchord(h, h->tracktime, bartime);
								if( h->drumon && (h->tp == h->tpr) )	
									abc_add_drum(h, h->tracktime, bartime);
								sprintf(barsig, "%s%s", sig[abckey], sig[abckey]);	// reset the key signature
								bartime = h->tracktime;
								if( h->tp && h->tp->vpos ) h->tp = abc_locate_track(h, h->tp->v, 0); // reset from voice overlay
								if( isdigit(*p) ) {	// different endings in repeats |i,j,n-r,s,...
									h->tp = abc_check_track(h, h->tp);
									abc_add_partbreak(h, h->tp, h->tracktime);
									p += abc_getnumber(p, &notelen);
									abc_add_variant_start(h, h->tp, h->tracktime, notelen);
									while( *p==',' || *p=='-' ) {
										if( *p==',' ) {
											p++;
											p += abc_getnumber(p, &notelen);
											abc_add_variant_choise(h->tp, notelen);
										}
										else {
											p++;
											p += abc_getnumber(p, &notediv);
											while( notelen < notediv ) {
												notelen++;
												abc_add_variant_choise(h->tp, notelen);
											}
										}
									}
									break;
								}
								if( *p==':' ) {	// repeat start
									p++;
									h->tp = abc_check_track(h, h->tp);
									abc_add_partbreak(h, h->tp, h->tracktime);
									abc_add_setloop(h, h->tp, h->tracktime);
								}
								break;
							case '&': // voice overlay
								abcto = 0;
								h->tracktime = bartime;
								h->tp = abc_check_track(h, h->tp);
								t = h->tp->vpos;
								h->tp = abc_locate_track(h, h->tp->v, t? t+1: DRONEPOS2+1);
								break;
							case ']':	// staff break, end of song
								abcto = 0;
								break;
							case ':': // repeat jump
								abcto = 0;
								h->tp = abc_check_track(h, h->tp);
								j = (h->tp->slidevol == -2)? jumpfade: jumpnormal;
								abc_add_setjumploop(h, h->tp, h->tracktime, j);
								abc_add_partbreak(h, h->tp, h->tracktime);
								if( *p==':' ) {	// repeat start without intermediate bar symbol
									p++;
									abc_add_setloop(h, h->tp, h->tracktime);
								}
								break;
							case '"':	// chord notation
								if( !strchr("_^<>@", *p) && !isdigit(*p) ) { // if it's not a annotation string
									h->tp = abc_check_track(h, h->tp);
									if( !h->tpc ) h->tpc = abc_locate_track(h, h->tp->v, 0);
 									if( h->tp == h->tpc ) abc_add_chord(p, h, h->tpc, h->tracktime); // only do chords for one voice
								}
								abcto = 0;
								while( (ch=*p++) && (ch != '"') ) {
									if( !strncasecmp(p,"fade",4) && h->track && h->track->slidevol > -2 )
										abc_globalslide(h, h->tracktime, -2); // set volumeslide to fade away...
									if( !strncasecmp(p,"to coda",7) ) {
										h->tp = abc_check_track(h, h->tp);
										abc_add_partbreak(h, h->tp, h->tracktime);
										abc_add_tocoda(h, h->tp, h->tracktime);
										p+=7;
										abcto = -1;
									}
									else
										if( !isspace(*p) ) abcto = 0;
									if( !strncasecmp(p,"to",2) && (isspace(p[2]) || p[2] == '"') ) abcto = 1;
								}
								if( !ch ) abcstate = INSKIPFORQUOTE;
								break;
							case '\\':	// skip the rest of this line, should be the end of the line anyway
								while( (ch=*p++) )
									;
								ch = '\\'; // remember for invoice tempo changes....
								break;
							case '!':	// line break, or deprecated old style decoration
							case '+': // decorations new style
								if( !strncmp(p,"coda",4) && p[4] == ch ) {
									h->tp = abc_check_track(h, h->tp);
									if( abcto ) {
										if( abcto > 0 ) {
											abc_add_partbreak(h, h->tp, h->tracktime);
											abc_add_tocoda(h, h->tp, h->tracktime);
										}
									}
									else {
										abc_add_partbreak(h, h->tp, h->tracktime);
										abc_add_coda(h, h->tp, h->tracktime);
									}
									p += 5;
									abcto = 0;
									break;
								}
								abcto = 0;
								if( !strncmp(p,"arpeggio",8) && p[8] == ch ) {
									abcarpeggio = 1;
									p += 9;
									break;
								}
								if( !strncmp(p,"crescendo(",10) && p[10] == ch ) {
									h->tp = abc_check_track(h, h->tp);
									abc_globalslide(h, h->tracktime, 1);
									p += 11;
									break;
								}
								if( !strncmp(p,"crescendo)",10) && p[10] == ch ) {
									h->tp = abc_check_track(h, h->tp);
									abc_globalslide(h, h->tracktime, 0);
									p += 11;
									break;
								}
								if( !strncmp(p,"<(",2) && p[2] == ch ) {
									h->tp = abc_check_track(h, h->tp);
									abc_globalslide(h, h->tracktime, 1);
									p += 3;
									break;
								}
								if( !strncmp(p,"<)",2) && p[2] == ch ) {
									h->tp = abc_check_track(h, h->tp);
									abc_globalslide(h, h->tracktime, 0);
									p += 3;
									break;
								}
								if( !strncmp(p,"dimimuendo(",11) && p[11] == ch ) {
									h->tp = abc_check_track(h, h->tp);
									abc_globalslide(h, h->tracktime, -1);
									p += 12;
									break;
								}
								if( !strncmp(p,"diminuendo)",11) && p[11] == ch ) {
									h->tp = abc_check_track(h, h->tp);
									abc_globalslide(h, h->tracktime, 0);
									p += 12;
									break;
								}
								if( !strncmp(p,">(",2) && p[2] == ch ) {
									h->tp = abc_check_track(h, h->tp);
									abc_globalslide(h, h->tracktime, -1);
									p += 3;
									break;
								}
								if( !strncmp(p,">)",2) && p[2] == ch ) {
									h->tp = abc_check_track(h, h->tp);
									abc_globalslide(h, h->tracktime, 0);
									p += 3;
									break;
								}
								if( !strncmp(p,"upbow",5) && p[5] == ch ) {
									abceffect = bow;
									abceffoper = 1;
									p += 6;
									break;
								}
								if( !strncmp(p,"downbow",7) && p[7] == ch ) {
									abceffect = bow;
									abceffoper = 0;
									p += 8;
									break;
								}
								if( !strncmp(p,"trill",5) && p[5] == ch ) {
									abceffect = trill;
									abceffoper = 0;
									p += 6;
									break;
								}
								if( !strncmp(p,"trill(",6) && p[6] == ch ) {
									abceffect = trill;
									abceffoper = 255;
									p += 7;
									break;
								}
								if( !strncmp(p,"trill)",6) && p[6] == ch ) {
									abceffect = none;
									abceffoper = 0;
									p += 7;
									break;
								}
								if( !strncmp(p,"accent",6) && p[6] == ch ) {
									abceffect = accent;
									abceffoper = 0;
									p += 7;
									break;
								}
								if( !strncmp(p,"emphasis",8) && p[8] == ch ) {
									abceffect = accent;
									abceffoper = 0;
									p += 9;
									break;
								}
								if( !strncmp(p,">",1) && p[1] == ch ) {
									abceffect = accent;
									abceffoper = 0;
									p += 2;
									break;
								}
								if( !strncmp(p,"fermata",7) && p[7] == ch ) {
									abcfermata = 1;
									p += 8;
									break;
								}
								if( !strncmp(p,"fine",4) && p[4] == ch ) {
									h->tp = abc_check_track(h, h->tp);
									abc_add_partbreak(h, h->tp, h->tracktime);
									abc_add_fine(h, h->tp, h->tracktime);
									p += 5;
									break;
								}
								if( !strncmp(p,"segno",5) && p[5] == ch ) {
									h->tp = abc_check_track(h, h->tp);
									abc_add_partbreak(h, h->tp, h->tracktime);
									abc_add_segno(h, h->tp, h->tracktime);
									p += 6;
									break;
								}
								if( !strncmp(p,"tocoda",6) && p[6] == ch ) {
									h->tp = abc_check_track(h, h->tp);
									abc_add_partbreak(h, h->tp, h->tracktime);
									abc_add_tocoda(h, h->tp, h->tracktime);
									p += 7;
									break;
								}
								if( !strncmp(p,"D.C.",4) && p[4] == ch ) {
									h->tp = abc_check_track(h, h->tp);
									j = (h->tp->slidevol == -2)? jumpdcfade: jumpdacapo;
									abc_add_setjumploop(h, h->tp, h->tracktime, j);
									abc_add_partbreak(h, h->tp, h->tracktime);
									p += 5;
									break;
								}
								if( !strncmp(p,"D.S.",4) && p[4] == ch ) {
									h->tp = abc_check_track(h, h->tp);
									j = (h->tp->slidevol == -2)? jumpdsfade: jumpdasegno;
									abc_add_setjumploop(h, h->tp, h->tracktime, j);
									abc_add_partbreak(h, h->tp, h->tracktime);
									p += 5;
									break;
								}
								if( !strncmp(p,"dacapo",6) && p[6] == ch ) {
									h->tp = abc_check_track(h, h->tp);
									j = (h->tp->slidevol == -2)? jumpdcfade: jumpdacapo;
									abc_add_setjumploop(h, h->tp, h->tracktime, j);
									abc_add_partbreak(h, h->tp, h->tracktime);
									p += 7;
									break;
								}
								if( !strncmp(p,"dacoda",6) && p[6] == ch ) {
									h->tp = abc_check_track(h, h->tp);
									j = (h->tp->slidevol == -2)? jumpdcfade: jumpdacapo;
									abc_add_setjumploop(h, h->tp, h->tracktime, j);
									abc_add_partbreak(h, h->tp, h->tracktime);
									p += 7;
									break;
								}
								if( ch == '!' ) {
									for( t=0; p[t] && strchr("|[:]!",p[t])==0 && !isspace(p[t]); t++ ) ;
									if( p[t] == '!' ) {	// volume and other decorations, deprecated
										h->tp = abc_check_track(h, h->tp);
										abcvol = abc_parse_decorations(h, h->tp, p);
										p = &p[t+1];
									}
								}
								else {
									h->tp = abc_check_track(h, h->tp);
									abcvol = abc_parse_decorations(h, h->tp, p);
									while( (ch=*p++) && (ch != '+') )
										;
								}
								break;
							case '`': // back quotes are for readability
								break;
							case '.':	// staccato marks
								break;
							default:	// some kinda note must follow
								if( strchr("abcdefgABCDEFG^_=X",ch) ) {
									h->tp = abc_check_track(h, h->tp);
									abc_track_clear_tiedvpos(h);
									abcbeatvol = abc_beat_vol(h, abcvol, (h->tracktime - bartime)/notelen_notediv_to_ticks(h->speed,1,mnotediv));
									p += abc_add_noteon(h, ch, p, h->tracktime, barsig, abcbeatvol, abceffect, abceffoper);
									if( abceffoper != 255 ) abceffect = none;
									p += abc_notelen(p, &notelen, &notediv);
									if( *p=='-' ) {
										p++;
										if( h->tp->tail->flg != 1 )
										h->tp->tienote = h->tp->tail;
									}
									tupletr = abc_tuplet(&notelen, &notediv, tupletp, tupletq, tupletr);
									while( isspace(*p) ) p++;	// allow spacing in broken rithm notation
									p += abc_brokenrithm(p, &notelen, &notediv, &brokenrithm, abchornpipe);
									thistime = notelen_notediv_to_ticks(h->speed, notelen*snotelen, notediv*snotediv);
									if( abcfermata ) {
										thistime <<= 1;
										abcfermata = 0;
									}
									if( thistime > abcgrace ) {
										thistime -= abcgrace;
										abcgrace = 0;
									}
									else {
										abcgrace -= thistime;
										thistime = abcticks(h->speed);
										abcgrace += abcticks(h->speed);
									}
									h->tracktime += thistime;
									if( thistime > abcticks(h->speed) )
										abc_add_noteoff(h, h->tp, h->tracktime - abcnoslurs - (( ch0 == '.')? thistime / 2: 0));
									else
										abc_add_noteoff(h, h->tp, h->tracktime);
									abc_add_sync(h, h->tp, h->tracktime);
									if( h->gchordon && (h->tp == h->tpc) )
										abc_add_gchord(h, h->tracktime, bartime);
									if( h->drumon && (h->tp == h->tpr) )	
										abc_add_drum(h, h->tracktime, bartime);
									abcarpeggio = 0;
									break;
								}
								if( strchr("zx",ch) ) {
									h->tp = abc_check_track(h, h->tp);
									abc_track_clear_tiednote(h);
									p += abc_notelen(p, &notelen, &notediv);
									tupletr = abc_tuplet(&notelen, &notediv, tupletp, tupletq, tupletr);
									while( isspace(*p) ) p++;	// allow spacing in broken rithm notation
									p += abc_brokenrithm(p, &notelen, &notediv, &brokenrithm, abchornpipe);
									thistime = notelen_notediv_to_ticks(h->speed, notelen*snotelen, notediv*snotediv);
									if( abcfermata ) {
										thistime <<= 1;
										abcfermata = 0;
									}
									if( thistime > abcgrace ) {
										thistime -= abcgrace;
										abcgrace = 0;
									}
									else {
										abcgrace -= thistime;
										thistime = abcticks(h->speed);
										abcgrace += abcticks(h->speed);
									}
									h->tracktime += thistime; 
									abc_add_sync(h, h->tp, h->tracktime);
									if( h->gchordon && (h->tp == h->tpc) )
										abc_add_gchord(h, h->tracktime, bartime);
									if( h->drumon && (h->tp == h->tpr) )	
										abc_add_drum(h, h->tracktime, bartime);
									abcarpeggio = 0;
									break;
								}
								if( strchr("Z",ch) ) {
									h->tp = abc_check_track(h, h->tp);
									abc_track_clear_tiednote(h);
									p += abc_notelen(p, &notelen, &notediv);
									thistime = notelen_notediv_to_ticks(h->speed, notelen*mnotelen, notediv*mnotediv);
									if( abcfermata ) {
										thistime <<= 1;
										abcfermata = 0;
									}
									if( thistime > abcgrace ) {
										thistime -= abcgrace;
										abcgrace = 0;
									}
									else {
										abcgrace -= thistime;
										thistime = abcticks(h->speed);
										abcgrace += abcticks(h->speed);
									}
									h->tracktime += thistime; 
									sprintf(barsig, "%s%s", sig[abckey], sig[abckey]);	// reset the key signature
									abc_add_sync(h, h->tp, h->tracktime);
									if( h->gchordon && (h->tp == h->tpc) )
										abc_add_gchord(h, h->tracktime, bartime);
									if( h->drumon && (h->tp == h->tpr) )	
										abc_add_drum(h, h->tracktime, bartime);
									abcarpeggio = 0;
									break;
								}
								if( isalpha(ch) && *p==':' ) {
									// some unprocessed field line?
									while( *p ) p++;	// skip it
									break; 
								}
								break;
						}
						ch0 = ch;	// remember previous char, can be staccato dot...
						if( pp ) {	// did we have a U: macro substitution?
							if( !*p ) {
								p = pp;
								pp = 0;
							}
						}
					}
				}
			}
		}
		if( mmsp ) mmfclose(mmstack[mmsp]);
	}
	ABC_CleanupMacros(h);	// we dont need them anymore
	if( !h->track ) {
		char buf[10];
		sprintf(buf,"%d",abcxnumber);
		abc_message("abc X:%s has no body", buf);
		h->track = abc_check_track(h, h->track); // for sanity...
	}
	if( abcstate == INBODY ) {
		// last but not least shut off all pending events
		abc_recalculate_tracktime(h);
		for( ttp=h->track; ttp; ttp=ttp->next )
			abc_add_noteoff(h,ttp,h->tracktime);
		abc_add_partbreak(h, h->track, h->tracktime);
		t = abc_patno(h, h->tracktime);
		if( abc_pattracktime(h, h->tracktime) % abcticks(64 * h->speed) ) t++;
		if(	global_part == ' ' ) {
			partpat[26][1] = t;
			if( abcparts ) {
				for( t=0; t<26; t++ )
					if( partpat[t][0] < partpat[t][1] ) break;
				if( t == 26 ) {
					abc_message("parts (%s) set but not used", abcparts);
					abc_set_parts(&abcparts, 0);	// forget the parts array
				}
			}
		}
		else
			partpat[global_part - 'A'][1] = t;
		if( !abcparts ) abc_song_to_parts(h, &abcparts, partpat);
		orderlen = abc_partpat_to_orderlist(partpat, abcparts, h, &orderlist, orderlen);
	}
	abc_synchronise_tracks(h);	// distribute all control events
	abc_recalculate_tracktime(h);
/*
	
	abctrack:
		tracktick		long
		note				byte
		octave			byte
		instrument	byte
		effects			byte
	
	tick = tracktick modulo speed
	row  = (tracktick div speed) modulo 64
	pat  = (tracktick div speed) div 64
	ord  = calculated

*/
	if( (p=getenv(ABC_ENV_DUMPTRACKS)) ) {
		printf("P:%s\n",abcparts);
		for( t=0; t<26; t++ )
			if( partpat[t][1] >= partpat[t][0] )
				printf("  %c ",t+'A');
		if( partpat[26][1] >= partpat[26][0] )
			printf("All");
		printf("\n");
		for( t=0; t<27; t++ )
			if( partpat[t][1] >= partpat[t][0] )
				printf("%3d ",partpat[t][0]);
		printf("\n");
		for( t=0; t<27; t++ )
			if( partpat[t][1] >= partpat[t][0] )
				printf("%3d ",partpat[t][1]);
		printf("\n");
		for( t=0; (int)t<orderlen; t++ )
			printf("%3d ",t);
		printf("\n");
		for( t=0; (int)t<orderlen; t++ )
			printf("%3d ",orderlist[t]);
		printf("\n");
		abc_dumptracks(h,p);
	}
	// set module variables
	if( abctempo == 0 ) abctempo = abcrate;
	if( m_nDefaultTempo == 0 ) m_nDefaultTempo = abctempo;
#ifdef NEWMIKMOD
	of->memsize     = PTMEM_LAST;      // Number of memory slots to reserve!
	of->modtype     = _mm_strdup(of->allochandle, ABC_Version);
	of->numpat      = 1+(modticks(h->tracktime) / h->speed / 64);
	of->numpos      = orderlen;
	of->reppos      = 0;
	of->initspeed   = h->speed;
	of->numchn      = abc_numtracks(h);
	of->numtrk      = of->numpat * of->numchn;
	of->initvolume  = 64;
	of->pansep      = 128;
	// orderlist
	if(!AllocPositions(of, orderlen)) {
		avoid_reentry = 0;
		return FALSE;
	}
	for(t=0; t<orderlen; t++)
		of->positions[t] = orderlist[t];
	_mmalloc_close(h->ho);	// get rid of orderlist memory
#else
	m_nType         = MOD_TYPE_ABC;
	numpat          = 1+(modticks(h->tracktime) / h->speed / 64);
	m_nDefaultSpeed = h->speed;
	m_nChannels     = abc_numtracks(h);
	m_dwSongFlags   = SONG_LINEARSLIDES;
	m_nMinPeriod    = 28 << 2;
	m_nMaxPeriod    = 1712 << 3;
	// orderlist
	for(t=0; t < (UINT)orderlen; t++)
		Order[t] = orderlist[t];
	free(orderlist);	// get rid of orderlist memory
#endif
#ifdef NEWMIKMOD
	// ==============================
	// Load the pattern info now!
	if(!AllocTracks(of)) return 0;
	if(!AllocPatterns(of)) return 0;
	of->ut = utrk_init(of->numchn, h->allochandle);
	utrk_memory_reset(of->ut);
	utrk_local_memflag(of->ut, PTMEM_PORTAMENTO, TRUE, FALSE);
	ABC_ReadPatterns(of, h, of->numpat);
	// load instruments after building the patterns (chan == 10 track handling)
	if( !PAT_Load_Instruments(of) ) {
		avoid_reentry = 0;
		return FALSE;
	}
	// ============================================================
	// set panning positions
	for(t=0; t<of->numchn; t++) {
		of->panning[t] = PAN_LEFT+((t+2)%5)*((PAN_RIGHT - PAN_LEFT)/5);     // 0x30 = std s3m val
	}
#else
	// ==============================
	// Load the pattern info now!
	if( ABC_ReadPatterns(Patterns, PatternSize, h, numpat, m_nChannels) ) {
		// :^(  need one more channel to handle the global events ;^b
		m_nChannels++;
		h->tp = abc_locate_track(h, "", 99);
		abc_add_sync(h, h->tp, h->tracktime);
		for( t=0; t<numpat; t++ ) {
			FreePattern(Patterns[t]);
			Patterns[t] = NULL;
		}
		ABC_ReadPatterns(Patterns, PatternSize, h, numpat, m_nChannels);
	}
	// load instruments after building the patterns (chan == 10 track handling)
	if( !PAT_Load_Instruments(this) ) {
		avoid_reentry = 0;
		return FALSE;
	}
	// ============================================================
	// set panning positions
	for(t=0; t<m_nChannels; t++) {
		ChnSettings[t].nPan = 0x30+((t+2)%5)*((0xD0 - 0x30)/5);     // 0x30 = std s3m val
		ChnSettings[t].nVolume = 64;
	}
#endif
	avoid_reentry = 0; // it is safe now, I'm finished
	abc_set_parts(&abcparts, 0);	// free the parts array
#ifndef NEWMIKMOD
	ABC_Cleanup(h);	// we dont need it anymore
#endif
	return 1;
}

#ifdef NEWMIKMOD
// =====================================================================================
CHAR *ABC_LoadTitle(MMSTREAM *mmfile)
// =====================================================================================
{
	char s[128];
	int i;
	// get the first line with T:songtitle
	_mm_fseek(mmfile,0,SEEK_SET);
	while(abc_fgets(mmfile,s,128)) {
		if( s[0]=='T' && s[1]==':' ) {
			for( i=2; s[i]==' '; i++ ) ;
			return(DupStr(NULL, s+i,strlen(s+i)));
		}
	}
	return NULL;
}

MLOADER load_abc =
{
    "ABC",
    "ABC draft 2.0",
    0x30,
    NULL,
    ABC_Test,
    (void *(*)(void))ABC_Init,
    (void (*)(ML_HANDLE *))ABC_Cleanup,
    /* Every single loader seems to need one of these! */
    (BOOL (*)(ML_HANDLE *, UNIMOD *, MMSTREAM *))ABC_Load,
    ABC_LoadTitle
};
#endif
