aboutsummaryrefslogtreecommitdiff
path: root/lib/libmodplug/src/load_abc.cpp
diff options
context:
space:
mode:
authorceros7 <ceros7@svn>2010-01-22 07:22:36 +0000
committerceros7 <ceros7@svn>2010-01-22 07:22:36 +0000
commit88b2c26dec662fa71d9f4a4ec7a1c47114b82325 (patch)
treeeda35d2769597506f8dcfab9075f9b9e0c89803a /lib/libmodplug/src/load_abc.cpp
parent5d73e3ee8ca59f26829326861821fa09bc00c1b8 (diff)
Merge remote branch 'origin/gpl-compat'
git-svn-id: https://xbmc.svn.sourceforge.net/svnroot/xbmc/trunk@27060 568bbfeb-2a22-0410-94d2-cc84cf5bfa90
Diffstat (limited to 'lib/libmodplug/src/load_abc.cpp')
-rw-r--r--lib/libmodplug/src/load_abc.cpp5141
1 files changed, 5141 insertions, 0 deletions
diff --git a/lib/libmodplug/src/load_abc.cpp b/lib/libmodplug/src/load_abc.cpp
new file mode 100644
index 0000000000..2fe8ea2bc9
--- /dev/null
+++ b/lib/libmodplug/src/load_abc.cpp
@@ -0,0 +1,5141 @@
+/*
+
+ 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>
+#include <unistd.h> // for sleep
+
+#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;
+ uint32_t tracktick;
+ union {
+ uint8_t par[6];
+ struct {
+ uint8_t flg;
+ uint8_t cmd;
+ uint32_t lpar; // for variant selections, bit pattern
+ };
+ };
+ uint8_t part;
+ uint8_t tiednote;
+} ABCEVENT;
+
+typedef struct _ABCTRACK
+{
+ struct _ABCTRACK *next;
+ ABCEVENT *head;
+ ABCEVENT *tail;
+ ABCEVENT *capostart;
+ ABCEVENT *tienote;
+ int transpose;
+ int octave_shift;
+ uint32_t slidevoltime; // for crescendo and diminuendo
+ int slidevol; // -2:fade away, -1:diminuendo, 0:none, +1:crescendo
+ uint8_t vno; // 0 is track is free for use, from previous song in multi-songbook
+ uint8_t vpos; // 0 is main voice, other is subtrack for gchords, gchords or drumnotes
+ uint8_t tiedvpos;
+ uint8_t mute;
+ uint8_t chan; // 10 is percussion channel, any other is melodic channel
+ uint8_t volume;
+ uint8_t instr; // current instrument for this track
+ uint8_t 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;
+ uint8_t 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
+ uint32_t 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;
+ uint32_t tracktime;
+} ABCHANDLE;
+
+static int global_voiceno, global_octave_shift, global_tempo_factor, global_tempo_divider;
+static char global_part;
+static uint32_t 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, uint32_t tracktime);
+static void abc_add_setjumploop(ABCHANDLE *h, ABCTRACK *tp, uint32_t tracktime, ABCEVENT_JUMPTYPE j);
+static uint32_t abc_pattracktime(ABCHANDLE *h, uint32_t tracktime);
+static int abc_patno(ABCHANDLE *h, uint32_t tracktime);
+
+#ifndef HAVE_SETENV
+static void setenv(const char *name, const char *value, int overwrite)
+{
+ int len = strlen(name)+1+strlen(value)+1;
+ char *str = (char *)alloca(len);
+ sprintf(str, "%s=%s", name, value);
+ putenv(str);
+}
+#endif
+
+
+static int abc_isvalidchar(char c) {
+ return(isalpha(c) || isdigit(c) || isspace(c) || c == '%' || c == ':');
+}
+
+
+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 uint32_t modticks(uint32_t abcticks)
+{
+ return abcticks / RESOLUTION;
+}
+
+static uint32_t abcticks(uint32_t modticks)
+{
+ return modticks * RESOLUTION;
+}
+
+static uint32_t notelen_notediv_to_ticks(int speed, int len, int div)
+{
+ uint32_t 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, uint32_t 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, uint32_t 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, uint32_t 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, uint32_t 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, uint32_t 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, uint32_t 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;
+ uint32_t 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;
+ uint32_t 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;
+ uint32_t 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, uint32_t 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, uint32_t 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, uint32_t 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, uint32_t tracktime, int vol)
+{
+ uint32_t 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, uint32_t 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, uint32_t 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, uint32_t 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, uint32_t 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, uint32_t 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, uint32_t 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, uint32_t 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, uint32_t 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, uint32_t 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, uint32_t 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, uint32_t 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, uint32_t 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, uint32_t pt1, uint32_t pt2)
+{
+ ABCEVENT *e, *ec;
+ uint32_t 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 uint32_t abc_pattracktime(ABCHANDLE *h, uint32_t tracktime)
+{
+ ABCEVENT *e;
+ uint32_t 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, uint32_t tracktime)
+{
+ return modticks(abc_pattracktime(h, tracktime)) / 64 / h->speed;
+}
+
+static void abc_stripoff(ABCHANDLE *h, ABCTRACK *tp, uint32_t 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, uint32_t fromtime, uint32_t 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 uint32_t abc_fade_tracks(ABCHANDLE *h, char *abcparts, uint32_t ptt[27])
+{
+ ABCTRACK *tp;
+ ABCEVENT *e0;
+ char *p;
+ int vol;
+ uint32_t pt1, pt2;
+ uint32_t 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])
+{
+ uint32_t starttick;
+ ABCEVENT *e;
+ int i, fading, loop, normal, partno, partsegno, partloop, partcoda, parttocoda, partfine, skip, x, y;
+ int vmask[27],nextp[27];
+ uint32_t 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];
+ bool hasK = false, hasX = false;
+ // 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] == 0) continue; // blank line.
+ if (id[0] == '%' && id[1] != '%') continue; // comment line.
+
+ if (!abc_isvalidchar(id[0]) || !abc_isvalidchar(id[1])) {
+ return(0); // probably not an ABC.
+ }
+ if(id[0]=='K'
+ && id[1]==':'
+ && (isalpha(id[2]) || isspace(id[2])) ) hasK = true;
+ if (id[0]=='X'
+ && id[1]== ':'
+ && (abc_isvalidchar(id[2])) ) hasX = true;
+ if (hasK && hasX) { return 1; printf("valid\n"); }
+ }
+ 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,"-%ld",retval->pickrandom+1);
+#else
+ retval->pickrandom = atoi(p+1)-1; // xmms preloads the file
+ sprintf(buf,"-%ld",retval->pickrandom+2);
+#endif
+ setenv(ABC_ENV_NORANDOMPICK, buf, 1);
+ }
+ }
+ else {
+ srandom(time(0)); // initialize random generator with seed
+ retval->pickrandom = 1+(int)(10000.0*random()/(RAND_MAX+1.0));
+ // can handle pickin' from songbooks with 10.000 songs
+#ifdef NEWMIKMOD
+ sprintf(buf,"-%ld",retval->pickrandom+1); // next in sequence
+#else
+ sprintf(buf,"-%ld",retval->pickrandom); // xmms preloads the file
+#endif
+ setenv(ABC_ENV_NORANDOMPICK, buf, 1);
+ }
+ 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;
+ uint32_t 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;
+ uint32_t 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 uint32_t abc_tracktime(ABCTRACK *tp)
+{
+ uint32_t 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, const 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 const int list_Maj[3] = { 0, 4, 7 };
+ static const int list_m[3] = { 0, 3, 7 };
+ static const int list_7[4] = { 0, 4, 7, 10 };
+ static const int list_m7[4] = { 0, 3, 7, 10 };
+ static const int list_maj7[4] = { 0, 4, 7, 11 };
+ static const int list_M7[4] = { 0, 4, 7, 11 };
+ static const int list_6[4] = { 0, 4, 7, 9 };
+ static const int list_m6[4] = { 0, 3, 7, 9 };
+ static const int list_aug[3] = { 0, 4, 8 };
+ static const int list_plus[3] = { 0, 4, 8 };
+ static const int list_aug7[4] = { 0, 4, 8, 10 };
+ static const int list_dim[3] = { 0, 3, 6 };
+ static const int list_dim7[4] = { 0, 3, 6, 9 };
+ static const int list_9[5] = { 0, 4, 7, 10, 2 };
+ static const int list_m9[5] = { 0, 3, 7, 10, 2 };
+ static const int list_maj9[5] = { 0, 4, 7, 11, 2 };
+ static const int list_M9[5] = { 0, 4, 7, 11, 2 };
+ static const int list_11[6] = { 0, 4, 7, 10, 2, 5 };
+ static const int list_dim9[5] = { 0, 4, 7, 10, 13 };
+ static const int list_sus[3] = { 0, 5, 7 };
+ static const int list_sus9[3] = { 0, 2, 7 };
+ static const int list_7sus[4] = { 0, 5, 7, 10 };
+ static const int list_7sus4[4] = { 0, 5, 7, 10 };
+ static const int list_7sus9[4] = { 0, 2, 7, 10 };
+ static const int list_9sus4[5] = { 0, 5, 10, 14, 19 };
+ static const int list_5[2] = { 0, 7 };
+ static const 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, uint32_t tracktime, uint32_t bartime)
+{
+ ABCEVENT *e;
+ ABCTRACK *tp;
+ uint32_t 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, uint32_t tracktime, uint32_t bartime)
+{
+ ABCEVENT *e, *c;
+ ABCTRACK *tp;
+ uint32_t 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, uint32_t 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 uint8_t *lpStream, DWORD dwMemLength)
+#endif
+{
+ static int avoid_reentry = 0;
+#ifdef NEWMIKMOD
+#define m_nDefaultTempo of->inittempo
+#else
+ ABCHANDLE *h;
+ uint32_t numpat;
+ MMFILE mm, *mmfile;
+#endif
+ uint32_t t;
+ char *line, *p, *pp, ch, ch0=0;
+ char barsig[52]; // for propagated accidental key signature within bar
+ char *abcparts;
+ uint8_t 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;
+ uint32_t 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 < (uint32_t)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