Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Branch: master
Fetching contributors…

Cannot retrieve contributors at this time

5190 lines (5012 sloc) 143.493 kB
/*
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>
#ifndef _WIN32
#include <unistd.h> // for sleep
#endif // _WIN32
#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 *)malloc(len);
sprintf(str, "%s=%s", name, value);
putenv(str);
free(str);
}
#endif
static int abc_isvalidchar(char c) {
return(isalpha(c) || isdigit(c) || isspace(c) || c == '%' || c == ':');
}
static const char *abc_skipspace(const char *p)
{
while (*p && isspace(*p))
p++;
return p;
}
static void abc_extractkeyvalue(char *key, size_t key_max,
char *value, size_t value_max, const char *src)
{
while (*src && isspace(*src))
src++;
size_t key_size;
for (key_size = 0; key_size < key_max - 1 && *src;) {
if (*src == '=') {
src++;
break;
}
key[key_size++] = *src++;
}
while (key_size > 0 && isspace(key[key_size - 1]))
key_size--;
key[key_size] = '\0';
while (*src && isspace(*src))
src++;
size_t value_size;
for (value_size = 0; value_size < value_max - 1 && *src;)
value[value_size++] = *src++;
while (value_size > 0 && isspace(value[value_size - 1]))
value_size--;
value[value_size] = '\0';
}
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;
if (div == 0) return 0;
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
#if defined(WIN32) && defined(_mm_free)
#undef _mm_free
#endif
#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' && !mmfeof(mmfile) && 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;
char key[256], value[256];
abc_extractkeyvalue(key, sizeof(key), value, sizeof(value), m);
retval = (ABCMACRO *)_mm_calloc(h->macrohandle, 1,sizeof(ABCTRACK));
retval->name = DupStr(h->macrohandle, key, strlen(key));
retval->n = strrchr(retval->name, 'n'); // for transposing macro's
retval->subst = DupStr(h->macrohandle, value, strlen(value));
retval->next = h->macro;
h->macro = retval;
}
// =============================================================================
static void abc_new_umacro(ABCHANDLE *h, const char *m)
// =============================================================================
{
ABCMACRO *retval, *mp;
char key[256], value[256];
abc_extractkeyvalue(key, sizeof(key), value, sizeof(value), m);
if( strlen(key) > 1 || strchr("~HIJKLMNOPQRSTUVWXY",toupper(key[0])) == 0 ) return;
while( char *q = strchr(key, '!') )
*q = '+'; // translate oldstyle to newstyle
if( !strcmp(key,"+nil+") ) { // delete a macro
mp = NULL;
for( retval=h->umacro; retval; retval = retval->next ) {
if( retval->name[0] == key[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, key, 1);
retval->subst = DupStr(h->macrohandle, value, strlen(value));
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; i+1 < sizeof(vc) && *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;
if( !el->next )
tp->tail = el;
_mm_free(h->trackhandle,ep);
ep = el->next;
}
else {
tp->head = ep->next;
if( !tp->head )
tp->tail = NULL;
_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 if (tp->next) {
h->track = tp->next;
_mm_free(h->trackhandle,tp);
tp = h->track;
} else {
break;
}
}
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; i < sizeof(s) - 1 && 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 += 1 + 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( tempo <= 0 )
tempo = 120;
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( k == 0 )
k = 1;
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);
break;
}
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 < 25 && i < 254 ) { // 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( i != bufsz-2 && 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);
if (l <= 0 ||n <= 0 || strstr(s, target))
return;
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;
if( m->n ) {
k = m->n - m->name;
for( i=0; i<14; i++ ) {
char t[strlen(m->name) + 1];
strcpy(t, m->name);
t[k] = "CDEFGABcdefgab"[i];
l = strlen(m->subst);
char s[2 * l + 1];
char *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++ = ',';
else if( i+b > 13 )
*p++ = '\'';
}
else *p++ = a;
}
*p = '\0';
abc_substitute(h, t, s);
}
}
else
abc_substitute(h, m->name, m->subst);
}
static char *abc_gets(ABCHANDLE *h, MMFILE *mmfile)
{
int i;
ABCMACRO *mp;
if( !h->len ) {
h->len = 64; // initial line size, adequate for most abc's
h->line = (char *)_mm_calloc(h->allochandle, h->len, sizeof(char));
}
if( abc_fgetbytes(mmfile, h->line, h->len) ) {
while( (i=strlen(h->line)) > (int)(h->len - 3) ) {
// line too short, double it
h->line = (char *)_mm_recalloc(h->allochandle, h->line, h->len<<1, sizeof(char));
if( h->line[i-1] != '\n' )
abc_fgetbytes(mmfile, &h->line[i], h->len);
h->len <<= 1;
}
h->line[i-1] = '\0'; // strip off newline
for( mp=h->macro; mp; mp=mp->next )
abc_preprocess(h,mp);
return h->line;
}
return NULL;
}
static int abc_parse_decorations(ABCHANDLE *h, ABCTRACK *tp, const char *p)
{
int vol=0;
if( !strncmp(p,"mp",2) ) vol = 75;
if( !strncmp(p,"mf",2) ) vol = 90;
if( !strncmp(p,"sfz",3) ) vol = 100;
if( *p == 'p' ) {
vol = 60;
while( *p++ == 'p' ) vol -= 15;
if( vol < 1 ) vol = 1;
}
if( *p == 'f' ) {
vol = 105;
while( *p++ == 'f' ) vol += 15;
if( vol > 135 ) vol = 127; // ffff
if( vol > 127 ) vol = 125; // fff
}
if( vol ) {
tp->volume = vol;
if( tp == h->track ) { // copy volume over to all voice tracks
for( ; tp; tp=tp->next ) {
if( tp->vpos == 0 || tp->vpos > DRONEPOS2 ) tp->volume = vol;
}
tp = h->track;
}
}
return tp->volume;
}
// =====================================================================================
#ifdef NEWMIKMOD
BOOL ABC_Test(MMSTREAM *mmfile)
#else
BOOL CSoundFile::TestABC(const BYTE *lpStream, DWORD dwMemLength)
#endif
// =====================================================================================
{
char id[128];
// scan file for first K: line (last in header)
#ifdef NEWMIKMOD
_mm_fseek(mmfile,0,SEEK_SET);
while(abc_fgets(mmfile,id,128)) {
#else
MMFILE mmfile;
mmfile.mm = (char *)lpStream;
mmfile.sz = dwMemLength;
mmfseek(&mmfile,0,SEEK_SET);
int ppos = mmfile.pos;
while(abc_fgets(&mmfile,id,128)) {
#endif
if (id[0] == 0 && mmfile.pos < ppos + 120) return(0); //probably binary
if (id[0] == 0) continue; // blank 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])) ) return 1;
}
return 0;
}
// =====================================================================================
static ABCHANDLE *ABC_Init(void)
{
ABCHANDLE *retval;
char *p;
char buf[10];
#ifdef NEWMIKMOD
MM_ALLOC *allochandle;
allochandle = _mmalloc_create("Load_ABC", NULL);
retval = (ABCHANDLE *)_mm_calloc(allochandle, 1,sizeof(ABCHANDLE));
if( !retval ) return NULL;
retval->allochandle = allochandle;
allochandle = _mmalloc_create("Load_ABC_macros", NULL);
retval->macrohandle = allochandle;
allochandle = _mmalloc_create("Load_ABC_tracks", NULL);
retval->trackhandle = allochandle;
#else
retval = (ABCHANDLE *)calloc(1,sizeof(ABCHANDLE));
if( !retval ) return NULL;
#endif
retval->track = NULL;
retval->macro = NULL;
retval->umacro = NULL;
retval->beatstring = NULL;
retval->pickrandom = 0;
retval->len = 0;
retval->line = NULL;
strcpy(retval->gchord, "");
retval->barticks = 0;
p = getenv(ABC_ENV_NORANDOMPICK);
if( p ) {
if( isdigit(*p) )
retval->pickrandom = atoi(p);
if( *p == '-' ) {
#ifdef NEWMIKMOD
retval->pickrandom = atoi(p+1);
sprintf(buf,"-%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((uint32_t)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] = {0};
const char *q;
while( isspace(*p) ) p++;
i = 0;
q = p;
for( i=0; i<8 && *p && *p != ']'; p++ ) {
if( isspace(*p) ) {
while( isspace(*p) ) p++;
if( strncasecmp(p, "min", 3) && strncasecmp(p, "maj", 3) )
break;
}
c[i] = *p;
i++;
}
c[i] = '\0';
if( !strcmp(c,"Hp") || !strcmp(c,"HP") ) // highland pipes
strcpy(c,"Bm"); // two sharps at c and f
if( !strcasecmp(c+1, "minor") ) i=2;
if( !strcasecmp(c+2, "minor") ) i=3;
if( !strcasecmp(c+1, "major") ) i=1;
if( !strcasecmp(c+2, "major") ) i=2;
if( !strcasecmp(c+1, "min") ) i=2;
if( !strcasecmp(c+2, "min") ) i=3;
if( !strcasecmp(c+1, "maj") ) i=1;
if( !strcasecmp(c+2, "maj") ) i=2;
for( ; i<6; i++ )
c[i] = ' ';
c[i] = '\0';
for( i=0; keySigs[i]; i++ ) {
for( j=10; j<46; j+=6 )
if( !strncasecmp(keySigs[i]+j, c, 6) )
return i;
}
abc_message("Failure: Unrecognised K: field %s", q);
return 7;
}
static char *abc_skip_word(char *p)
{
while( isspace(*p) ) p++;
while( *p && !isspace(*p) && *p != ']') p++;
while( isspace(*p) ) p++;
return p;
}
static uint32_t abc_tracktime(ABCTRACK *tp)
{
uint32_t tracktime;
if( tp->tail ) tracktime = tp->tail->tracktick;
else tracktime = 0;
if( tracktime < global_songstart )
tracktime = global_songstart;
return tracktime;
}
static void abc_addchordname(const char *s, int len, const int *notes)
// adds chord name and note set to list of known chords
{
int i, j;
if(strlen(s) > 7) {
abc_message("Failure: Chord name cannot exceed 7 characters, %s", s);
return;
}
if(len > 6) {
abc_message("Failure: Named chord cannot have more than 6 notes, %s", s);
return;
}
for( i=0; i < chordsnamed; i++ ) {
if(strcmp(s, chordname[i]) == 0) {
/* change chord */
chordlen[i] = len;
for(j = 0; j < len; j++) chordnotes[i][j] = notes[j];
return;
}
}
if(chordsnamed > MAXCHORDNAMES - 1)
abc_message("Failure: Too many Guitar Chord Names used, %s", s);
else {
strcpy(chordname[chordsnamed], s);
chordlen[chordsnamed] = len;
for(j = 0; j < len; j++) chordnotes[chordsnamed][j] = notes[j];
chordsnamed++;
}
}
static void abc_setup_chordnames()
// set up named guitar chords
{
static const int list_Maj[3] = { 0, 4, 7 };
static const int list_m[3] = { 0, 3, 7 };
static const int list_7[4] = { 0, 4, 7, 10 };
static const int list_m7[4] = { 0, 3, 7, 10 };
static const int list_maj7[4] = { 0, 4, 7, 11 };
static const int list_M7[4] = { 0, 4, 7, 11 };
static const int list_6[4] = { 0, 4, 7, 9 };
static const int list_m6[4] = { 0, 3, 7, 9 };
static const int list_aug[3] = { 0, 4, 8 };
static const int list_plus[3] = { 0, 4, 8 };
static const int list_aug7[4] = { 0, 4, 8, 10 };
static const int list_dim[3] = { 0, 3, 6 };
static const int list_dim7[4] = { 0, 3, 6, 9 };
static const int list_9[5] = { 0, 4, 7, 10, 2 };
static const int list_m9[5] = { 0, 3, 7, 10, 2 };
static const int list_maj9[5] = { 0, 4, 7, 11, 2 };
static const int list_M9[5] = { 0, 4, 7, 11, 2 };
static const int list_11[6] = { 0, 4, 7, 10, 2, 5 };
static const int list_dim9[5] = { 0, 4, 7, 10, 13 };
static const int list_sus[3] = { 0, 5, 7 };
static const int list_sus9[3] = { 0, 2, 7 };
static const int list_7sus[4] = { 0, 5, 7, 10 };
static const int list_7sus4[4] = { 0, 5, 7, 10 };
static const int list_7sus9[4] = { 0, 2, 7, 10 };
static const int list_9sus4[5] = { 0, 5, 10, 14, 19 };
static const int list_5[2] = { 0, 7 };
static const int list_13[6] = { 0, 4, 7, 10, 16, 21 };
chordsnamed = 0;
abc_addchordname("", 3, list_Maj);
abc_addchordname("m", 3, list_m);
abc_addchordname("7", 4, list_7);
abc_addchordname("m7", 4, list_m7);
abc_addchordname("maj7", 4, list_maj7);
abc_addchordname("M7", 4, list_M7);
abc_addchordname("6", 4, list_6);
abc_addchordname("m6", 4, list_m6);
abc_addchordname("aug", 3, list_aug);
abc_addchordname("+", 3, list_plus);
abc_addchordname("aug7", 4, list_aug7);
abc_addchordname("7+", 4, list_aug7);
abc_addchordname("dim", 3, list_dim);
abc_addchordname("dim7", 4, list_dim7);
abc_addchordname("9", 5, list_9);
abc_addchordname("m9", 5, list_m9);
abc_addchordname("maj9", 5, list_maj9);
abc_addchordname("M9", 5, list_M9);
abc_addchordname("11", 6, list_11);
abc_addchordname("dim9", 5, list_dim9);
abc_addchordname("sus", 3, list_sus);
abc_addchordname("sus9", 3, list_sus9);
abc_addchordname("7sus", 4, list_7sus);
abc_addchordname("7sus4", 4, list_7sus4);
abc_addchordname("7sus9", 4, list_7sus9);
abc_addchordname("9sus4", 5, list_9sus4);
abc_addchordname("5", 2, list_5);
abc_addchordname("13", 6, list_13);
}
static int abc_MIDI_getnumber(const char *p)
{
int n;
while( isspace(*p) ) p++;
abc_getnumber(p, &n);
if( n < 0 ) n = 0;
if( n > 127 ) n = 127;
return n;
}
static int abc_MIDI_getprog(const char *p)
{
int n;
while( isspace(*p) ) p++;
abc_getnumber(p, &n);
if( n < 1 ) n = 1;
if( n > 128 ) n = 128;
return n;
}
// MIDI drone <instr0> <pitch1> <pitch2> <vel1> <vel2>
static void abc_MIDI_drone(const char *p, int *gm, int *ptch, int *vol)
{
int i;
while( isspace(*p) ) p++;
p += abc_getnumber(p, &i);
i++; // adjust for 1..128
if( i>0 && i < 129 )
*gm = i;
else
*gm = 71; // bassoon
while( isspace(*p) ) p++;
p += abc_getnumber(p, &i);
if( i>0 && i < 127 )
ptch[0] = i;
else
ptch[0] = 45;
while( isspace(*p) ) p++;
p += abc_getnumber(p, &i);
if( i>0 && i < 127 )
ptch[1] = i;
else
ptch[1] = 33;
while( isspace(*p) ) p++;
p += abc_getnumber(p, &i);
if( i>0 && i < 127 )
vol[0] = i;
else
vol[0] = 80;
while( isspace(*p) ) p++;
p += abc_getnumber(p, &i);
if( i>0 && i < 127 )
vol[1] = i;
else
vol[1] = 80;
}
static void abc_chan_to_tracks(ABCHANDLE *h, int tno, int ch)
{
ABCTRACK *tp;
if( tno>0 && tno<33 ) {
for( tp=h->track; tp; tp=tp->next ) {
if( tp->vno == tno && (tp->vpos < GCHORDBPOS || tp->vpos > DRONEPOS2) )
tp->chan = ch;
}
}
}
// %%MIDI channel int1
// channel numbers are 1-16
static void abc_MIDI_channel(const char *p, ABCTRACK *tp, ABCHANDLE *h)
{
int i1, i2;
i1 = tp? tp->vno: 1;
for( ; *p && isspace(*p); p++ ) ;
if( isdigit(*p) ) {
p += abc_getnumber(p, &i2);
if( i2 >= 1 && i2 <= 16 )
abc_chan_to_tracks(h, i1, i2); // we start at 1
}
}
static void abc_instr_to_tracks(ABCHANDLE *h, int tno, int gm)
{
ABCTRACK *tp;
if( tno>0 && tno<33 && gm>0 && gm<129 ) {
for( tp=h->track; tp; tp=tp->next ) {
if( tp->vno == tno && (tp->vpos < GCHORDBPOS || tp->vpos > DRONEPOS2) )
tp->instr = gm;
}
}
}
// %%MIDI program [int1] <int2>
// instrument numbers are 0-127
static void abc_MIDI_program(const char *p, ABCTRACK *tp, ABCHANDLE *h)
{
int i1, i2;
i1 = tp? tp->vno: 1;
for( ; *p && isspace(*p); p++ ) ;
if( isdigit(*p) ) {
p += abc_getnumber(p, &i2);
for( ; *p && isspace(*p); p++ ) ;
if( isdigit(*p) ) {
i1 = i2;
p += abc_getnumber(p, &i2);
}
abc_instr_to_tracks(h, i1, i2 + 1); // we start at 1
}
}
static void abc_mute_voice(ABCHANDLE *h, ABCTRACK *tp, int m)
{
ABCTRACK *t;
for( t=h->track; t; t=t->next ) {
if( t->vno == tp->vno ) t->mute = m;
}
}
// %%MIDI voice [<ID>] [instrument=<integer> [bank=<integer>]] [mute]
// instrument numbers are 1-128
static void abc_MIDI_voice(const char *p, ABCTRACK *tp, ABCHANDLE *h)
{
int i1, i2;
for( ; *p && isspace(*p); p++ ) ;
if( strncmp(p,"instrument=",11) && strncmp(p,"mute",4) ) {
tp = abc_locate_track(h, p, 0);
for( ; *p && !isspace(*p); p++ ) ;
for( ; *p && isspace(*p); p++ ) ;
}
i1 = tp? tp->vno: 1;
i2 = 0;
if( !strncmp(p,"instrument=",11) && isdigit(p[11]) ) {
p += 11;
p += abc_getnumber(p, &i2);
for( ; *p && isspace(*p); p++ ) ;
if( !strncmp(p,"bank=",5) && isdigit(p[5]) ) {
for( ; *p && !isspace(*p); p++ ) ;
for( ; *p && isspace(*p); p++ ) ;
}
}
if( tp ) abc_mute_voice(h,tp,0);
if( !strncmp(p,"mute",4) && (p[4]=='\0' || p[4]=='%' || isspace(p[4])) ) {
if( tp ) abc_mute_voice(h,tp,1);
}
abc_instr_to_tracks(h, i1, i2); // starts already at 1 (draft 4.0)
}
// %%MIDI chordname <string> <int1> <int2> ... <int6>
static void abc_MIDI_chordname(const char *p)
{
char name[20];
int i, notes[6];
for( ; *p && isspace(*p); p++ ) ;
i = 0;
while ((i < 19) && (*p != ' ') && (*p != '\0')) {
name[i] = *p;
p = p + 1;
i = i + 1;
}
name[i] = '\0';
if(*p != ' ') {
abc_message("Failure: Bad format for chordname command, %s", p);
}
else {
i = 0;
while ((i <= 6) && isspace(*p)) {
for( ; *p && isspace(*p); p++ ) ;
p += abc_getnumber(p, &notes[i]);
i = i + 1;
}
abc_addchordname(name, i, notes);
}
}
// %%MIDI drum <string> <inst 1> ... <inst n> <vol 1> ... <vol n>
// instrument numbers are 0-127
static int abc_MIDI_drum(const char *p, ABCHANDLE *h)
{
char *q;
int i,n,m;
while( isspace(*p) ) p++;
if( !strncmp(p,"on",2) && (isspace(p[2]) || p[2] == '\0') ) return 2;
if( !strncmp(p,"off",3) && (isspace(p[3]) || p[3] == '\0') ) return 1;
n = 0;
for( q = h->drum; *p && !isspace(*p); p++ ) {
if( !strchr("dz0123456789",*p) ) break;
*q++ = *p;
if( !isdigit(*p) ) {
if( !isdigit(p[1]) ) *q++ = '1';
n++; // count the silences too....
}
}
*q = '\0';
q = h->drumins;
for( i = 0; i<n; i++ ) {
if( h->drum[i*2] == 'd' ) {
while( isspace(*p) ) p++;
if( !isdigit(*p) ) {
m = 0;
while( !isspace(*p) ) p++;
}
else
p += abc_getnumber(p,&m);
q[i] = m + 1; // we start at 1
}
else q[i] = 0;
}
q = h->drumvol;
for( i = 0; i<n; i++ ) {
if( h->drum[i*2] == 'd' ) {
while( isspace(*p) ) p++;
if( !isdigit(*p) ) {
m = 0;
while( !isspace(*p) ) p++;
}
else
p += abc_getnumber(p,&m);
q[i] = m;
}
else q[i] = 0;
}
return 0;
}
// %%MIDI gchord <string>
static int abc_MIDI_gchord(const char *p, ABCHANDLE *h)
{
char *q;
while( isspace(*p) ) p++;
if( !strncmp(p,"on",2) && (isspace(p[2]) || p[2] == '\0') ) return 2;
if( !strncmp(p,"off",3) && (isspace(p[3]) || p[3] == '\0') ) return 1;
for( q = h->gchord; *p && !isspace(*p); p++ ) {
if( !strchr("fbcz0123456789ghijGHIJ",*p) ) break;
*q++ = *p;
if( !isdigit(*p) && !isdigit(p[1]) ) *q++ = '1';
}
*q = '\0';
return 0;
}
static void abc_metric_gchord(ABCHANDLE *h, int mlen, int mdiv)
{
switch( 16 * mlen + mdiv ) {
case 0x24:
case 0x44:
case 0x22:
abc_MIDI_gchord("fzczfzcz", h);
break;
case 0x64:
case 0x32:
abc_MIDI_gchord("fzczczfzczcz", h);
break;
case 0x34:
case 0x38:
abc_MIDI_gchord("fzczcz", h);
break;
case 0x68:
abc_MIDI_gchord("fzcfzc", h);
break;
case 0x98:
abc_MIDI_gchord("fzcfzcfzc", h);
break;
case 0xc8:
abc_MIDI_gchord("fzcfzcfzcfzc", h);
break;
default:
if( mlen % 3 == 0 )
abc_MIDI_gchord("fzcfzcfzcfzcfzcfzcfzcfzcfzc", h);
else
abc_MIDI_gchord("fzczfzczfzczfzczfzczfzczfzcz", h);
if( mdiv == 8 ) h->gchord[mlen*2] = '\0';
else h->gchord[mlen*4] = '\0';
break;
}
}
static void abc_MIDI_legato(const char *p, ABCTRACK *tp)
{
for( ; *p && isspace(*p); p++ ) ;
if( !strncmp(p,"off",3) ) tp->legato = 0;
else tp->legato = 1;
}
static void abc_M_field(const char *p, int *mlen, int *mdiv)
{
if( !strncmp(p,"none",4) ) {
*mlen = 1;
*mdiv = 1;
return;
}
if( !strncmp(p,"C|",2) ) {
*mlen = 2;
*mdiv = 2;
return;
}
if( *p == 'C' ) {
*mlen = 4;
*mdiv = 4;
return;
}
p += abc_getexpr(p,mlen);
sscanf(p," / %d", mdiv);
}
static int abc_drum_steps(const char *dch)
{
const char *p;
int i=0;
for( p=dch; *p; p++ ) {
if( isdigit(*p) ) i += *p - '0';;
}
return i;
}
static void abc_add_drum(ABCHANDLE *h, uint32_t tracktime, uint32_t bartime)
{
ABCEVENT *e;
ABCTRACK *tp;
uint32_t etime, ctime , rtime, stime;
int i, g, steps, gnote, gsteps, nnum;
steps = abc_drum_steps(h->drum);
ctime = h->barticks;
// look up the last event in tpr drumtrack
tp = abc_locate_track(h, h->tpr->v, DRUMPOS);
e = tp->tail;
etime = e? e->tracktick: bartime;
if( etime > tracktime ) return;
if( etime < bartime ) rtime = h->barticks - ((bartime - etime) % h->barticks);
else rtime = (etime - bartime) % h->barticks;
stime = ctime*steps;
rtime *= steps;
rtime += stime;
gsteps = strlen(h->drum)/2;
g = 0;
while( rtime > stime ) {
rtime -= ctime*(h->drum[g*2+1] - '0');
if( ++g == gsteps ) g = 0;
}
stime = (tracktime - etime) * steps;
rtime = 0;
while( rtime < stime ) {
gnote = h->drum[g*2];
i = h->drum[g*2+1] - '0';
if(gnote=='d') {
tp->instr = pat_gm_drumnr(h->drumins[g]-1);
nnum = pat_gm_drumnote(h->drumins[g]);
abc_add_drumnote(h, tp, etime + rtime/steps, nnum, h->drumvol[g]);
abc_add_noteoff(h,tp,etime + ( rtime + ctime * i )/steps);
}
if( ++g == gsteps ) g = 0;
rtime += ctime * i;
}
}
static int abc_gchord_steps(const char *gch)
{
const char *p;
int i=0;
for( p=gch; *p; p++ )
if( isdigit(*p) ) i += *p - '0';
return i;
}
static void abc_add_gchord(ABCHANDLE *h, uint32_t tracktime, uint32_t bartime)
{
ABCEVENT *e, *c;
ABCTRACK *tp;
uint32_t etime, ctime , rtime, stime;
int i, g, steps, gnote, gcnum, gsteps, nnum, glen;
// look up the last chord event in tpc
c = 0;
for( e = h->tpc->head; e; e = e->next )
if( e->flg == 1 && e->cmd == cmdchord )
c = e;
if( !c ) return;
gcnum = c->par[chordnum];
steps = abc_gchord_steps(h->gchord);
ctime = h->barticks;
etime = 0;
for( i = GCHORDBPOS; i < DRUMPOS; i++ ) {
tp = abc_locate_track(h, h->tpc->v, i);
e = tp->tail;
if( !e ) e = c;
stime = e->tracktick;
if( stime > etime ) etime = stime;
}
if( etime > tracktime ) return;
if( etime < bartime ) rtime = h->barticks - ((bartime - etime) % h->barticks);
else rtime = (etime - bartime) % h->barticks;
stime = ctime * steps;
rtime *= steps;
rtime += stime;
gsteps = strlen(h->gchord);
g = 0;
while( rtime > stime ) {
glen = h->gchord[2*g+1] - '0';
rtime -= ctime * glen;
if( ++g == gsteps ) g = 0;
}
stime = (tracktime - etime) * steps;
rtime = 0;
while( rtime < stime ) {
gnote = h->gchord[2*g];
glen = h->gchord[2*g+1] - '0';
if( ++g == gsteps ) g = 0;
nnum = 0;
switch(gnote) {
case 'b':
tp = abc_locate_track(h, h->tpc->v, GCHORDFPOS);
tp->instr = h->abcbassprog;
nnum = c->par[chordnote]+chordnotes[gcnum][0]+24;
abc_add_chordnote(h, tp, etime + rtime/steps, nnum, h->abcbassvol);
abc_add_noteoff(h,tp,etime + ( rtime + ctime * glen )/steps);
case 'c':
for( i = 1; i < chordlen[gcnum]; i++ ) {
tp = abc_locate_track(h, h->tpc->v, i+GCHORDFPOS);
tp->instr = h->abcchordprog;
nnum = c->par[chordnote]+chordnotes[gcnum][i]+24;
abc_add_chordnote(h, tp, etime + rtime/steps, nnum, h->abcchordvol);
abc_add_noteoff(h,tp,etime + ( rtime + ctime * glen )/steps);
}
rtime += ctime * glen;
break;
case 'f':
tp = abc_locate_track(h, h->tpc->v, GCHORDFPOS);
tp->instr = h->abcbassprog;
nnum = c->par[chordbase]+12;
abc_add_chordnote(h, tp, etime + rtime/steps, nnum, h->abcbassvol);
rtime += ctime * glen;
abc_add_noteoff(h,tp,etime + rtime/steps);
break;
case 'g':
case 'h':
case 'i':
case 'j':
case 'G':
case 'H':
case 'I':
case 'J':
i = toupper(gnote) - 'G';
nnum = 0;
if( i < chordlen[gcnum] ) {
tp = abc_locate_track(h, h->tpc->v, GCHORDFPOS+i+1);
tp->instr = h->abcchordprog;
nnum = c->par[chordnote]+chordnotes[gcnum][i]+24;
if( isupper(gnote) ) nnum -= 12;
abc_add_chordnote(h, tp, etime + rtime/steps, nnum, h->abcchordvol);
}
rtime += ctime * glen;
if( nnum ) abc_add_noteoff(h,tp,etime + rtime/steps);
break;
case 'z':
rtime += ctime * glen;
break;
}
}
}
// %%MIDI beat a b c n
//
// controls the way note velocities are selected. The first note in a bar has
// velocity a. Other "strong" notes have velocity b and all the rest have velocity
// c. a, b and c must be in the range 0-128. The parameter n determines which
// notes are "strong". If the time signature is x/y, then each note is given
// a position number k = 0, 1, 2 .. x-1 within each bar. Note that the units for
// n are not the unit note length. If k is a multiple of n, then the note is
// "strong". The volume specifiers !ppp! to !fff! are equivalent to the
// following :
//
// !ppp! = %%MIDI beat 30 20 10 1
// !pp! = %%MIDI beat 45 35 20 1
// !p! = %%MIDI beat 60 50 35 1
// !mp! = %%MIDI beat 75 65 50 1
// !mf! = %%MIDI beat 90 80 65 1
// !f! = %%MIDI beat 105 95 80 1
// !ff! = %%MIDI beat 120 110 95 1
// !fff! = %%MIDI beat 127 125 110 1
static void abc_MIDI_beat(ABCHANDLE *h, const char *p)
{
int i,j;
h->beat[0] = 127;
h->beat[1] = 125;
h->beat[2] = 110;
h->beat[3] = 1;
for( j=0; j<4; j++ ) {
while( isspace(*p) ) p++;
if( *p ) {
p += abc_getnumber(p, &i);
if( i < 0 ) i = 0;
if( i > 127 ) i = 127;
h->beat[j] = i;
}
}
if( h->beat[3] == 0 ) h->beat[3] = 1; // BB Ruud says: do not let you make mad
}
//
// %%MIDI beatstring <string of f, m and p>
//
// This provides an alternative way of specifying where the strong and weak
// stresses fall within a bar. 'f' means velocity a (normally strong), 'm'
// means velocity b (medium velocity) and 'p' means velocity c (soft velocity).
// For example, if the time signature is 7/8 with stresses on the first, fourth
// and sixth notes in the bar, we could use the following
//
// %%MIDI beatstring fppmpmp
static void abc_MIDI_beatstring(ABCHANDLE *h, const char *p)
{
while( isspace(*p) ) p++;
if( h->beatstring ) _mm_free(h->allochandle, h->beatstring);
if( strlen(p) )
h->beatstring = DupStr(h->allochandle,p,strlen(p)+1);
else
h->beatstring = NULL;
}
static int abc_beat_vol(ABCHANDLE *h, int abcvol, int barpos)
{
int vol;
if( h->beatstring ) {
vol = (h->beat[2] * 9) / 10;
if( barpos < (int)strlen(h->beatstring) ) {
switch(h->beatstring[barpos]) {
case 'f':
vol = h->beat[0];
break;
case 'm':
vol = h->beat[1];
break;
case 'p':
vol = h->beat[2];
break;
default:
break;
}
}
}
else {
if( (barpos % h->beat[3]) == 0 ) {
if( barpos )
vol = h->beat[1];
else
vol = h->beat[0];
}
else
vol = h->beat[2];
}
vol *= abcvol;
vol /= 128;
return vol;
}
static void abc_init_partpat(BYTE partp[27][2])
{
int i;
for( i=0; i<27; i++ ) {
partp[i][0] = 0xff;
partp[i][1] = 0;
}
}
static int abc_partpat_to_orderlist(BYTE partp[27][2], const char *abcparts, ABCHANDLE *h, BYTE **list, int orderlen)
{
int t, partsused;
const char *p;
BYTE *orderlist = *list;
static int ordersize = 0;
if( *list == NULL ) {
ordersize = 128;
orderlist = (BYTE *)_mm_calloc(h->ho, ordersize, sizeof(BYTE));
*list = orderlist;
}
if( abcparts ) {
partsused = 0;
for( p = abcparts; *p; p++ ) {
for( t = partp[*p - 'A'][0]; t < partp[*p - 'A'][1]; t++ ) {
if( orderlen == ordersize ) {
ordersize <<= 1;
orderlist = (BYTE *)_mm_recalloc(h->ho, orderlist, ordersize, sizeof(BYTE));
*list = orderlist;
}
orderlist[orderlen] = t;
orderlen++;
partsused++;
}
}
if( partsused ) return orderlen;
}
// some fool wrote a P: string in the header but didn't use P: in the body
for( t = partp[26][0]; t < partp[26][1]; t++ ) {
if( orderlen == ordersize ) {
ordersize <<= 1;
orderlist = (BYTE *)_mm_recalloc(h->ho, orderlist, ordersize, sizeof(BYTE));
*list = orderlist;
}
orderlist[orderlen] = t;
orderlen++;
}
return orderlen;
}
static void abc_globalslide(ABCHANDLE *h, uint32_t tracktime, int slide)
{
ABCTRACK *tp;
ABCEVENT *e;
int hslide;
hslide = h->track? h->track->slidevol: slide;
for( tp=h->track; tp; tp = tp->next ) {
if( slide ) {
tp->slidevoltime = tracktime;
if( slide == 2 )
tp->slidevol = 0;
}
if( tp->slidevol > -2 && slide < 2 )
tp->slidevol = slide;
}
if( h->track && h->track->tail
&& hslide != slide && slide == -2
&& h->track->tail->tracktick >= tracktime ) {
// need to update jumptypes in mastertrack from tracktime on...
for( e=h->track->head; e; e=e->next ) {
if( e->flg == 1 && e->cmd == cmdjump && e->tracktick >= tracktime ) {
switch( e->par[jumptype] ) {
case jumpnormal:
case jumpfade:
e->par[jumptype] = jumpfade;
break;
case jumpdacapo:
case jumpdcfade:
e->par[jumptype] = jumpdcfade;
break;
case jumpdasegno:
case jumpdsfade:
e->par[jumptype] = jumpdsfade;
break;
}
}
}
}
}
static void abc_recalculate_tracktime(ABCHANDLE *h) {
ABCTRACK *ttp;
h->tracktime = 0;
for( ttp=h->track; ttp; ttp=ttp->next )
if( ttp->tail && ttp->tail->tracktick > h->tracktime )
h->tracktime = ttp->tail->tracktick;
}
static void abc_MIDI_command(ABCHANDLE *h, char *p, char delim) {
int t;
// interpret some of the possibilitys
if( !strncmp(p,"bassprog",8) && isspace(p[8]) ) h->abcbassprog = abc_MIDI_getprog(p+8)+1;
if( !strncmp(p,"bassvol",7) && isspace(p[7]) ) h->abcbassvol = abc_MIDI_getnumber(p+7);
if( !strncmp(p,"beat",4) && isspace(p[4]) ) abc_MIDI_beat(h, p+4);
if( !strncmp(p,"beatstring",10) && isspace(p[10]) ) abc_MIDI_beatstring(h, p+4);
if( !strncmp(p,"chordname",9) && isspace(p[9]) ) abc_MIDI_chordname(p+9);
if( !strncmp(p,"chordprog",9) && isspace(p[9]) ) h->abcchordprog = abc_MIDI_getprog(p+9)+1;
if( !strncmp(p,"chordvol",8) && isspace(p[8]) ) h->abcchordvol = abc_MIDI_getnumber(p+8);
if( !strncmp(p,"drone",5) && isspace(p[5]) ) abc_MIDI_drone(p+5, &h->dronegm, h->dronepitch, h->dronevol);
if( !strncmp(p,"droneoff",8) && (p[8]=='\0' || p[8]==delim || isspace(p[8])) ) h->droneon = 0;
if( !strncmp(p,"droneon",7) && (p[7]=='\0' || p[7]==delim || isspace(p[7])) ) h->droneon = 1;
t = h->drumon;
if( !strncmp(p,"drum",4) && isspace(p[4]) ) {
h->drumon = abc_MIDI_drum(p+4, h);
if( h->drumon ) --h->drumon;
else h->drumon = t;
}
if( !strncmp(p,"drumoff",7) && (p[7]=='\0' || p[7]==delim || isspace(p[7])) ) h->drumon = 0;
if( !strncmp(p,"drumon",6) && (p[6]=='\0' || p[6]==delim || isspace(p[6])) ) h->drumon = 1;
if( t != h->drumon ) {
if( h->drumon && !h->tpr ) h->tpr = h->track;
if( h->tpr ) abc_add_drum_sync(h, h->tpr, h->tracktime); // don't start drumming from the beginning of time!
if( h->tpr && !h->drumon ) h->tpr = NULL;
}
t = h->gchordon;
if( !strncmp(p,"gchord",6) && (p[6]=='\0' || p[6]==delim || isspace(p[6])) ) {
h->gchordon = abc_MIDI_gchord(p+6, h);
if( h->gchordon ) --h->gchordon;
else h->gchordon = t;
}
if( !strncmp(p,"gchordoff",9) && (p[9]=='\0' || p[9]==delim || isspace(p[9])) ) h->gchordon = 0;
if( !strncmp(p,"gchordon",8) && (p[8]=='\0' || p[8]==delim || isspace(p[8])) ) h->gchordon = 1;
if( t != h->gchordon ) {
if( h->tpc ) abc_add_gchord_syncs(h, h->tpc, h->tracktime);
}
if( !strncmp(p,"channel",7) && isspace(p[7]) )
abc_MIDI_channel(p+8, h->tp = abc_check_track(h, h->tp), h);
if( !strncmp(p,"program",7) && isspace(p[7]) )
abc_MIDI_program(p+8, h->tp = abc_check_track(h, h->tp), h);
if( !strncmp(p,"voice",5) && isspace(p[5]) )
abc_MIDI_voice(p+6, h->tp = abc_check_track(h, h->tp), h);
if( !strncmp(p,"legato",6) && (p[6]=='\0' || p[6]==delim || isspace(p[6])) )
abc_MIDI_legato(p+6, h->tp = abc_check_track(h, h->tp));
}
// continuate line that ends with a backslash, can't do this in abc_gets because voice lines
// can have comment lines in between that must be parsed properly, for example:
// [V:1] cdef gabc' |\ << continuation backslash
// %%MIDI program 25
// c'bag fedc |
// informational lines can have this too, so it is rather convoluted code...
static char *abc_continuated(ABCHANDLE *h, MMFILE *mmf, char *p) {
char *pm, *p1, *p2 = 0;
int continued;
pm = p;
while( pm[strlen(pm)-1]=='\\' ) {
p1 = strdup(pm);
if( p2 ) free(p2);
continued = 1;
while( continued ) {
continued = 0;
pm = abc_gets(h, mmf);
if( !pm ) {
abc_message("line not properly continued\n%s", p1);
return p1;
}
while( *pm && isspace(*pm) ) ++pm;
if( !strncmp(pm,"%%",2) ) {
for( p2 = pm+2; *p2 && isspace(*p2); p2++ ) ;
if( !strncmp(p2,"MIDI",4) && (p2[4]=='=' || isspace(p2[4])) ) {
for( p2+=5; *p2 && isspace(*p2); p2++ ) ;
if( *p2 == '=' )
for( p2+=1; *p2 && isspace(*p2); p2++ ) ;
abc_MIDI_command(h,p2,'%');
}
continued = 1;
}
}
p2 = (char *)malloc(strlen(p1)+strlen(pm));
if( !p2 ) {
abc_message("macro line too long\n%s", p1);
return p1;
}
p1[strlen(p1)-1] = '\0'; // strip off the backslash
strcpy(p2,p1);
strcat(p2,pm);
pm = p2;
free(p1);
}
return pm;
}
// =====================================================================================
#ifdef NEWMIKMOD
BOOL ABC_Load(ABCHANDLE *h, UNIMOD *of, MMSTREAM *mmfile)
#else
BOOL CSoundFile::ReadABC(const uint8_t *lpStream, DWORD dwMemLength)
#endif
{
static int avoid_reentry = 0;
#ifdef NEWMIKMOD
#define m_nDefaultTempo of->inittempo
#else
ABCHANDLE *h;
uint32_t numpat;
MMFILE mm, *mmfile;
#endif
uint32_t t;
char *line, *p, *pp, ch, ch0=0;
char barsig[52]; // for propagated accidental key signature within bar
char *abcparts;
uint8_t partpat[27][2], *orderlist;
int orderlen;
enum { NOWHERE, INBETWEEN, INHEAD, INBODY, INSKIPFORX, INSKIPFORQUOTE } abcstate;
ABCEVENT_JUMPTYPE j;
ABCEVENT_X_EFFECT abceffect;
int abceffoper;
int abcxcount=0, abcxwanted=0, abcxnumber=1;
int abckey, abcrate, abcchord, abcvol, abcbeatvol, abcnoslurs, abcnolegato, abcfermata, abcarpeggio, abcto;
int abctempo;
int cnotelen=0, cnotediv=0, snotelen, snotediv, mnotelen, mnotediv, notelen, notediv;
// c for chords, s for standard L: setting, m for M: barlength
int abchornpipe, brokenrithm, tupletp, tupletq, tupletr;
int ktempo;
uint32_t abcgrace=0, bartime, thistime=0;
ABCTRACK *tpd, *ttp;
ABCMACRO *mp;
int mmsp;
#ifdef NEWMIKMOD
MMSTREAM *mmstack[MAXABCINCLUDES];
h->ho = _mmalloc_create("Load_ABC_ORDERLIST", NULL);
#else
MMFILE *mmstack[MAXABCINCLUDES];
if( !TestABC(lpStream, dwMemLength) ) return FALSE;
h = ABC_Init();
if( !h ) return FALSE;
mmfile = &mm;
mm.mm = (char *)lpStream;
mm.sz = dwMemLength;
mm.pos = 0;
#endif
while( avoid_reentry ) sleep(1);
avoid_reentry = 1;
pat_resetsmp();
pat_init_patnames();
m_nDefaultTempo = 0;
global_voiceno = 0;
abckey = 0;
h->tracktime = 0;
global_songstart = 0;
h->speed = 6;
abcrate = 240;
global_tempo_factor = 2;
global_tempo_divider = 1;
abctempo = 0;
ktempo = 0;
abceffect = none;
abceffoper = 0;
abcvol = 120;
h->abcchordvol = abcvol;
h->abcbassvol = abcvol;
h->abcchordprog = 25; // acoustic guitar
h->abcbassprog = 33; // acoustic bass
abcparts = 0;
abcnoslurs = 1;
abcnolegato = 1;
abcfermata = 0;
abcarpeggio = 0;
abcto = 0;
snotelen = 0;
snotediv = 0;
mnotelen = 1;
mnotediv = 1;
abchornpipe = 0;
brokenrithm = 0;
tupletp = 0;
tupletq = 0;
tupletr = 0;
h->ktrans = 0;
h->drumon = 0;
h->gchordon = 1;
h->droneon = 0;
h->tracktime = 0;
bartime = 0;
h->tp = NULL;
h->tpc = NULL;
h->tpr = NULL;
tpd = NULL;
h->dronegm = 71;
h->dronepitch[0] = 45;
h->dronepitch[1] = 33;
h->dronevol[0] = 80;
h->dronevol[1] = 80;
abc_new_umacro(h, "v = +downbow+");
abc_new_umacro(h, "u = +upbow+");
abc_new_umacro(h, "O = +coda+");
abc_new_umacro(h, "S = +segno+");
abc_new_umacro(h, "P = +uppermordent+");
abc_new_umacro(h, "M = +lowermordent+");
abc_new_umacro(h, "L = +emphasis+");
abc_new_umacro(h, "H = +fermata+");
abc_new_umacro(h, "T = +trill+");
abc_new_umacro(h, "~ = +roll+");
abc_setup_chordnames();
abc_init_partpat(partpat);
abc_MIDI_beat(h, ""); // reset beat array
abc_MIDI_beatstring(h, ""); // reset beatstring
orderlist = NULL;
orderlen = 0;
mmsp = 1;
mmstack[0] = mmfile;
mmfseek(mmfile,0,SEEK_SET);
abcstate = NOWHERE;
if( h->pickrandom ) {
abcstate = INSKIPFORX;
abcxcount = 0;
mmfseek(mmfile,0,SEEK_SET);
while( (line=abc_gets(h, mmfile)) ) {
for( p=line; isspace(*p); p++ ) ;
if( !strncmp(p,"X:",2) ) abcxcount++;
}
if( abcxcount == 0 )
abcstate = NOWHERE;
else
abcxwanted = (h->pickrandom - 1) % abcxcount;
abcxcount = 0;
mmfseek(mmfile,0,SEEK_SET);
}
while( mmsp > 0 ) {
mmsp--;
while((line=abc_gets(h, mmstack[mmsp]))) {
for( p=line; isspace(*p); p++ ) ;
switch(abcstate) {
case INSKIPFORX:
if( !strncmp(p,"X:",2) ) {
if( abcxcount++ != abcxwanted )
break;
}
// fall through
case INBETWEEN:
if( !strncmp(p,"X:",2) ) {
abcstate = INHEAD;
#ifdef NEWMIKMOD
of->songname = NULL;
#else
memset(m_szNames[0], 0, 32);
#endif
for( p+=2; isspace(*p); p++ ) ;
abcxnumber = atoi(p);
abchornpipe = 0;
h->droneon = 0;
h->dronegm = 71;
h->dronepitch[0] = 45;
h->dronepitch[1] = 33;
h->dronevol[0] = 80;
h->dronevol[1] = 80;
for( ttp = h->track; ttp; ttp=ttp->next ) {
ttp->vno = 0; // mark track unused
ttp->capostart = NULL;
}
h->tp = NULL; // forget old voices
h->tpc = NULL;
h->tpr = NULL;
global_voiceno = 0;
abc_set_parts(&abcparts, 0);
abcgrace = 0;
h->ktrans = 0;
ktempo = 0;
h->gchordon = 1;
h->drumon = 0;
global_songstart = h->tracktime;
abc_MIDI_beat(h, ""); // reset beat array
abc_MIDI_beatstring(h, ""); // reset beatstring
strcpy(h->gchord, ""); // reset gchord string
abcnolegato = 1; // reset legato switch
}
break;
case NOWHERE:
if( p[0] != '\0' && p[1] == ':' ) {
abcstate = INHEAD;
abc_set_parts(&abcparts, 0);
strcpy(h->gchord, "");
if( h->drumon && h->tpr ) abc_add_drum_sync(h, h->tpr, h->tracktime);
if( h->tpc && !h->gchordon ) abc_add_gchord_syncs(h, h->tpc, h->tracktime);
h->gchordon = 1;
h->drumon = 0;
}
else
break;
case INHEAD:
if( !strncmp(p,"L:",2) ) {
sscanf(p+2," %d / %d", &snotelen, &snotediv);
break;
}
if( !strncmp(p,"M:",2) ) {
abc_M_field(p+2, &mnotelen, &mnotediv);
break;
}
if( !strncmp(p,"P:",2) ) {
abc_set_parts(&abcparts, p+2);
break;
}
if( !strncmp(p,"Q:",2) ) {
abctempo = abc_extract_tempo(p+2,0);
ktempo = 1;
if( h->track ) {
// make h->tracktime start of a new age...
abc_add_partbreak(h, h->track, h->tracktime);
abc_add_tempo_event(h, h->track, h->tracktime, abctempo);
}
if( m_nDefaultTempo == 0 ) m_nDefaultTempo = abctempo;
break;
}
if( !strncmp(p,"T:",2) ) {
char buf[200];
if( strchr(p,'%') ) *strchr(p,'%') = '\0';
for( t=strlen(p)-1; isspace(p[t]); t-- )
p[t]='\0';
for( t=2; isspace(p[t]); t++ ) ;
#ifdef NEWMIKMOD
if( of->songname )
strcpy(buf,of->songname);
else
strcpy(buf,"");
#else
strcpy(buf,m_szNames[0]);
#endif
if( strlen(buf) + strlen(p+t) > 199 ) p[t+199-strlen(buf)] = '\0'; // chop it of
if( strlen(buf) ) strcat(buf," "); // add a space
strcat(buf, p+t);
#ifdef NEWMIKMOD
of->songname = DupStr(of->allochandle, buf, strlen(buf));
#else
if( strlen(buf) > 31 ) buf[31] = '\0'; // chop it of
strcpy(m_szNames[0], buf);
#endif
break;
}
if( !strncmp(p,"R:",2) ) {
for( p+=2; isspace(*p); p++ ) ;
if( !strncmp(p,"hornpipe",8) && (isspace(p[8]) || p[8]=='\0') ) abchornpipe = 1;
else abchornpipe = 0;
break;
}
if( !strncmp(p,"V:",2) ) {
for( t=2; p[t]==' '; t++ ) ;
h->tp = abc_locate_track(h, p+t, 0);
abcvol = h->tp->volume;
abcnolegato = !h->tp->legato;
if( !abcnolegato ) abcnoslurs = 0;
break;
}
if( !strncmp(p,"K:",2) ) {
abcstate = INBODY;
abckey = ABC_Key(p+2);