diff options
author | ceros7 <ceros7@svn> | 2010-01-22 07:22:36 +0000 |
---|---|---|
committer | ceros7 <ceros7@svn> | 2010-01-22 07:22:36 +0000 |
commit | 88b2c26dec662fa71d9f4a4ec7a1c47114b82325 (patch) | |
tree | eda35d2769597506f8dcfab9075f9b9e0c89803a /lib/libmodplug/src/load_abc.cpp | |
parent | 5d73e3ee8ca59f26829326861821fa09bc00c1b8 (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.cpp | 5141 |
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, ¬es[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, ¬elen, ¬ediv); + 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, ¬elen, ¬ediv); + 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(¬elen, ¬ediv, tupletp, tupletq, tupletr); + while( isspace(*p) ) p++; // allow spacing in broken rithm notation + p += abc_brokenrithm(p, ¬elen, ¬ediv, &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, ¬elen); + abc_add_variant_start(h, h->tp, h->tracktime, notelen); + while( *p==',' || *p=='-' ) { + if( *p==',' ) { + p++; + p += abc_getnumber(p, ¬elen); + abc_add_variant_choise(h->tp, notelen); + } + else { + p++; + p += abc_getnumber(p, ¬ediv); + 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, ¬elen, ¬ediv); + 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, ¬elen); + abc_add_variant_start(h, h->tp, h->tracktime, notelen); + while( *p==',' || *p=='-' ) { + if( *p==',' ) { + p++; + p += abc_getnumber(p, ¬elen); + abc_add_variant_choise(h->tp, notelen); + } + else { + p++; + p += abc_getnumber(p, ¬ediv); + 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, ¬elen, ¬ediv); + if( *p=='-' ) { + p++; + if( h->tp->tail->flg != 1 ) + h->tp->tienote = h->tp->tail; + } + tupletr = abc_tuplet(¬elen, ¬ediv, tupletp, tupletq, tupletr); + while( isspace(*p) ) p++; // allow spacing in broken rithm notation + p += abc_brokenrithm(p, ¬elen, ¬ediv, &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, ¬elen, ¬ediv); + tupletr = abc_tuplet(¬elen, ¬ediv, tupletp, tupletq, tupletr); + while( isspace(*p) ) p++; // allow spacing in broken rithm notation + p += abc_brokenrithm(p, ¬elen, ¬ediv, &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, ¬elen, ¬ediv); + 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 |