home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Games Machine 25
/
GNOME_DEMO.iso
/
amiga
/
music
/
mikmod.lzx
/
mikmod
/
load_xm.c
< prev
next >
Wrap
C/C++ Source or Header
|
2000-01-05
|
15KB
|
598 lines
/*
Name:
LOAD_XM.C
Description:
Fasttracker (XM) module loader
Portability:
All systems - all compilers (hopefully)
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
/* #include <malloc.h> */
#include "mikmod.h"
/**************************************************************************
**************************************************************************/
typedef struct XMHEADER{
char id[17]; /* ID text: 'Extended module: ' */
char songname[21]; /* Module name, padded with zeroes and 0x1a at the end */
char trackername[20]; /* Tracker name */
UWORD version; /* (word) Version number, hi-byte major and low-byte minor */
ULONG headersize; /* Header size */
UWORD songlength; /* (word) Song length (in patten order table) */
UWORD restart; /* (word) Restart position */
UWORD numchn; /* (word) Number of channels (2,4,6,8,10,...,32) */
UWORD numpat; /* (word) Number of patterns (max 256) */
UWORD numins; /* (word) Number of instruments (max 128) */
UWORD flags; /* (word) Flags: bit 0: 0 = Amiga frequency table (see below) 1 = Linear frequency table */
UWORD tempo; /* (word) Default tempo */
UWORD bpm; /* (word) Default BPM */
UBYTE orders[256]; /* (byte) Pattern order table */
} XMHEADER;
typedef struct XMINSTHEADER{
ULONG size; /* (dword) Instrument size */
char name[22]; /* (char) Instrument name */
UBYTE type; /* (byte) Instrument type (always 0) */
UWORD numsmp; /* (word) Number of samples in instrument */
ULONG ssize; /* */
} XMINSTHEADER;
typedef struct XMPATCHHEADER{
UBYTE what[96]; /* (byte) Sample number for all notes */
UBYTE volenv[48]; /* (byte) Points for volume envelope */
UBYTE panenv[48]; /* (byte) Points for panning envelope */
UBYTE volpts; /* (byte) Number of volume points */
UBYTE panpts; /* (byte) Number of panning points */
UBYTE volsus; /* (byte) Volume sustain point */
UBYTE volbeg; /* (byte) Volume loop start point */
UBYTE volend; /* (byte) Volume loop end point */
UBYTE pansus; /* (byte) Panning sustain point */
UBYTE panbeg; /* (byte) Panning loop start point */
UBYTE panend; /* (byte) Panning loop end point */
UBYTE volflg; /* (byte) Volume type: bit 0: On; 1: Sustain; 2: Loop */
UBYTE panflg; /* (byte) Panning type: bit 0: On; 1: Sustain; 2: Loop */
UBYTE vibflg; /* (byte) Vibrato type */
UBYTE vibsweep; /* (byte) Vibrato sweep */
UBYTE vibdepth; /* (byte) Vibrato depth */
UBYTE vibrate; /* (byte) Vibrato rate */
UWORD volfade; /* (word) Volume fadeout */
UWORD reserved[11]; /* (word) Reserved */
} XMPATCHHEADER;
typedef struct XMWAVHEADER{
ULONG length; /* (dword) Sample length */
ULONG loopstart; /* (dword) Sample loop start */
ULONG looplength; /* (dword) Sample loop length */
UBYTE volume; /* (byte) Volume */
SBYTE finetune; /* (byte) Finetune (signed byte -128..+127) */
UBYTE type; /* (byte) Type: Bit 0-1: 0 = No loop, 1 = Forward loop, */
/* 2 = Ping-pong loop; */
/* 4: 16-bit sampledata */
UBYTE panning; /* (byte) Panning (0-255) */
SBYTE relnote; /* (byte) Relative note number (signed byte) */
UBYTE reserved; /* (byte) Reserved */
char samplename[22]; /* (char) Sample name */
} XMWAVHEADER;
typedef struct XMPATHEADER{
ULONG size; /* (dword) Pattern header length */
UBYTE packing; /* (byte) Packing type (always 0) */
UWORD numrows; /* (word) Number of rows in pattern (1..256) */
UWORD packsize; /* (word) Packed patterndata size */
} XMPATHEADER;
typedef struct MTMNOTE{
UBYTE a,b,c;
} MTMNOTE;
typedef struct XMNOTE{
UBYTE note,ins,vol,eff,dat;
}XMNOTE;
XMNOTE *xmpat;
/**************************************************************************
**************************************************************************/
static XMHEADER *mh;
char XM_Version[]="XM";
BOOL XM_Test(void)
{
char id[17];
if(!fread(id,17,1,modfp)) return 0;
if(!memcmp(id,"Extended Module: ",17)) return 1;
return 0;
}
BOOL XM_Init(void)
{
mh=NULL;
if(!(mh=MyCalloc(1,sizeof(XMHEADER)))) return 0;
return 1;
}
void XM_Cleanup(void)
{
if(mh!=NULL) free(mh);
}
void XM_ReadNote(XMNOTE *n)
{
UBYTE cmp;
memset(n,0,sizeof(XMNOTE));
cmp=fgetc(modfp);
if(cmp&0x80){
if(cmp&1) n->note=fgetc(modfp);
if(cmp&2) n->ins=fgetc(modfp);
if(cmp&4) n->vol=fgetc(modfp);
if(cmp&8) n->eff=fgetc(modfp);
if(cmp&16) n->dat=fgetc(modfp);
}
else{
n->note=cmp;
n->ins=fgetc(modfp);
n->vol=fgetc(modfp);
n->eff=fgetc(modfp);
n->dat=fgetc(modfp);
}
}
UBYTE *XM_Convert(XMNOTE *xmtrack,UWORD rows)
{
int t;
UBYTE note,ins,vol,eff,dat;
UniReset();
for(t=0;t<rows;t++){
note=xmtrack->note;
ins=xmtrack->ins;
vol=xmtrack->vol;
eff=xmtrack->eff;
dat=xmtrack->dat;
if(note!=0){
UniNote(note-1);
}
if(ins!=0){
UniInstrument(ins-1);
}
/* printf("Vol:%d\n",vol); */
switch(vol>>4){
case 0x6: /* volslide down */
if(vol&0xf){
UniWrite(UNI_XMEFFECTA);
UniWrite(vol&0xf);
}
break;
case 0x7: /* volslide up */
if(vol&0xf){
UniWrite(UNI_XMEFFECTA);
UniWrite(vol<<4);
}
break;
/* volume-row fine volume slide is compatible with protracker
EBx and EAx effects i.e. a zero nibble means DO NOT SLIDE, as
opposed to 'take the last sliding value'.
*/
case 0x8: /* finevol down */
UniPTEffect(0xe,0xb0 | (vol&0xf));
break;
case 0x9: /* finevol up */
UniPTEffect(0xe,0xa0 | (vol&0xf));
break;
case 0xa: /* set vibrato speed */
UniPTEffect(0x4,vol<<4);
break;
case 0xb: /* vibrato */
UniPTEffect(0x4,vol&0xf);
break;
case 0xc: /* set panning */
UniPTEffect(0x8,vol<<4);
break;
case 0xd: /* panning slide left */
/* only slide when data nibble not zero: */
if(vol&0xf){
UniWrite(UNI_XMEFFECTP);
UniWrite(vol&0xf);
}
break;
case 0xe: /* panning slide right */
/* only slide when data nibble not zero: */
if(vol&0xf){
UniWrite(UNI_XMEFFECTP);
UniWrite(vol<<4);
}
break;
case 0xf: /* tone porta */
UniPTEffect(0x3,vol<<4);
break;
default:
if(vol>=0x10 && vol<=0x50){
UniPTEffect(0xc,vol-0x10);
}
}
/* if(eff>0xf) printf("Effect %d",eff); */
switch(eff){
case 'G'-55: /* G - set global volume */
break;
case 'H'-55: /* H - global volume slide */
break;
case 'K'-55: /* K - keyoff */
UniNote(96);
break;
case 'L'-55: /* L - set envelope position */
break;
case 'P'-55: /* P - panning slide */
UniWrite(UNI_XMEFFECTP);
UniWrite(dat);
break;
case 'R'-55: /* R - multi retrig note */
UniWrite(UNI_S3MEFFECTQ);
UniWrite(dat);
break;
case 'T'-55:
UniPTEffect(0x6,dat);
break;
case 'X'-55:
if((dat>>4)==1){ /* X1 extra fine porta up */
}
else{ /* X2 extra fine porta down */
}
break;
default:
if(eff==0xa){
UniWrite(UNI_XMEFFECTA);
UniWrite(dat);
}
else if(eff<=0xf) UniPTEffect(eff,dat);
break;
}
UniNewline();
xmtrack++;
}
return UniDup();
}
BOOL XM_Load(void)
{
INSTRUMENT *d;
SAMPLE *q;
int t,u,v,p,numtrk;
long next;
/* try to read module header */
_mm_read_str(mh->id,17,modfp);
_mm_read_str(mh->songname,21,modfp);
_mm_read_str(mh->trackername,20,modfp);
mh->version =_mm_read_I_UWORD(modfp);
mh->headersize =_mm_read_I_ULONG(modfp);
mh->songlength =_mm_read_I_UWORD(modfp);
mh->restart =_mm_read_I_UWORD(modfp);
mh->numchn =_mm_read_I_UWORD(modfp);
mh->numpat =_mm_read_I_UWORD(modfp);
mh->numins =_mm_read_I_UWORD(modfp);
mh->flags =_mm_read_I_UWORD(modfp);
mh->tempo =_mm_read_I_UWORD(modfp);
mh->bpm =_mm_read_I_UWORD(modfp);
_mm_read_UBYTES(mh->orders,256,modfp);
if(feof(modfp)){
myerr = ERROR_LOADING_HEADER;
return 0;
}
/* set module variables */
of.initspeed=mh->tempo;
of.inittempo=mh->bpm;
of.modtype=DupStr(mh->trackername,20);
of.numchn=mh->numchn;
of.numpat=mh->numpat;
of.numtrk=(UWORD)of.numpat*of.numchn; /* get number of channels */
of.songname=DupStr(mh->songname,20); /* make a cstr of songname */
of.numpos=mh->songlength; /* copy the songlength */
of.reppos=mh->restart;
of.numins=mh->numins;
of.flags|=UF_XMPERIODS;
if(mh->flags&1) of.flags|=UF_LINEAR;
memcpy(of.positions,mh->orders,256);
of.numpat=0;
for(t=0;t<of.numpos;t++){
if(of.positions[t]>of.numpat) of.numpat=of.positions[t];
}
of.numpat++;
/* printf("Modtype :%s\n",of.modtype);
printf("Version :%x\n",mh->version);
printf("Song :%s\n",of.songname);
printf("Speed :%d,%d\n",of.initspeed,of.inittempo);
printf("Channels:%d\n",of.numchn);
printf("Numins :%d\n",mh->numins);
*/
if(!AllocTracks()) return 0;
if(!AllocPatterns()) return 0;
numtrk=0;
for(t=0;t<mh->numpat;t++){
XMPATHEADER ph;
/* printf("Reading pattern %d\n",t); */
ph.size =_mm_read_I_ULONG(modfp);
ph.packing =_mm_read_UBYTE(modfp);
ph.numrows =_mm_read_I_UWORD(modfp);
ph.packsize =_mm_read_I_UWORD(modfp);
/* printf("headln: %ld\n",ph.size); */
/* printf("numrows: %d\n",ph.numrows); */
/* printf("packsize:%d\n",ph.packsize); */
of.pattrows[t]=ph.numrows;
/*
Gr8.. when packsize is 0, don't try to load a pattern.. it's empty.
This bug was discovered thanks to Khyron's module..
*/
if(!(xmpat=MyCalloc(ph.numrows*of.numchn,sizeof(XMNOTE)))) return 0;
if(ph.packsize>0){
for(u=0;u<ph.numrows;u++){
for(v=0;v<of.numchn;v++){
XM_ReadNote(&xmpat[(v*ph.numrows)+u]);
}
}
}
for(v=0;v<of.numchn;v++){
of.tracks[numtrk++]=XM_Convert(&xmpat[v*ph.numrows],ph.numrows);
}
free(xmpat);
}
if(!AllocInstruments()) return 0;
d=of.instruments;
for(t=0;t<of.numins;t++){
XMINSTHEADER ih;
/* read instrument header */
ih.size =_mm_read_I_ULONG(modfp);
_mm_read_str (ih.name, 22, modfp);
ih.type =_mm_read_UBYTE(modfp);
ih.numsmp =_mm_read_I_UWORD(modfp);
ih.ssize =_mm_read_I_ULONG(modfp);
/* printf("Size: %ld\n",ih.size);
printf("Name: %22.22s\n",ih.name);
printf("Samples:%d\n",ih.numsmp);
printf("sampleheadersize:%ld\n",ih.ssize);
*/
d->insname=DupStr(ih.name,22);
d->numsmp=ih.numsmp;
if(!AllocSamples(d)) return 0;
if(ih.numsmp>0){
XMPATCHHEADER pth;
XMWAVHEADER wh;
_mm_read_UBYTES (pth.what, 96, modfp);
_mm_read_UBYTES (pth.volenv, 48, modfp);
_mm_read_UBYTES (pth.panenv, 48, modfp);
pth.volpts =_mm_read_UBYTE(modfp);
pth.panpts =_mm_read_UBYTE(modfp);
pth.volsus =_mm_read_UBYTE(modfp);
pth.volbeg =_mm_read_UBYTE(modfp);
pth.volend =_mm_read_UBYTE(modfp);
pth.pansus =_mm_read_UBYTE(modfp);
pth.panbeg =_mm_read_UBYTE(modfp);
pth.panend =_mm_read_UBYTE(modfp);
pth.volflg =_mm_read_UBYTE(modfp);
pth.panflg =_mm_read_UBYTE(modfp);
pth.vibflg =_mm_read_UBYTE(modfp);
pth.vibsweep =_mm_read_UBYTE(modfp);
pth.vibdepth =_mm_read_UBYTE(modfp);
pth.vibrate =_mm_read_UBYTE(modfp);
pth.volfade =_mm_read_I_UWORD(modfp);
_mm_read_I_UWORDS(pth.reserved, 11, modfp);
memcpy(d->samplenumber,pth.what,96);
d->volfade=pth.volfade;
/* printf("Volfade %x\n",d->volfade); */
memcpy(d->volenv,pth.volenv,24);
d->volflg=pth.volflg;
d->volsus=pth.volsus;
d->volbeg=pth.volbeg;
d->volend=pth.volend;
d->volpts=pth.volpts;
/* printf("volume points : %d\n"
"volflg : %d\n"
"volbeg : %d\n"
"volend : %d\n"
"volsus : %d\n",
d->volpts,
d->volflg,
d->volbeg,
d->volend,
d->volsus);
*/
/* scale volume envelope: */
for(p=0;p<12;p++){
d->volenv[p].val<<=2;
/* printf("%d,%d,",d->volenv[p].pos,d->volenv[p].val); */
}
memcpy(d->panenv,pth.panenv,24);
d->panflg=pth.panflg;
d->pansus=pth.pansus;
d->panbeg=pth.panbeg;
d->panend=pth.panend;
d->panpts=pth.panpts;
/* printf("Panning points : %d\n"
"panflg : %d\n"
"panbeg : %d\n"
"panend : %d\n"
"pansus : %d\n",
d->panpts,
d->panflg,
d->panbeg,
d->panend,
d->pansus);
*/
/* scale panning envelope: */
for(p=0;p<12;p++){
d->panenv[p].val<<=2;
/* printf("%d,%d,",d->panenv[p].pos,d->panenv[p].val); */
}
/* for(u=0;u<256;u++){ */
/* printf("%2.2x ",fgetc(modfp)); */
/* } */
next=0;
for(u=0;u<ih.numsmp;u++){
q=&d->samples[u];
wh.length =_mm_read_I_ULONG (modfp);
wh.loopstart =_mm_read_I_ULONG (modfp);
wh.looplength =_mm_read_I_ULONG (modfp);
wh.volume =_mm_read_UBYTE (modfp);
wh.finetune =_mm_read_SBYTE (modfp);
wh.type =_mm_read_UBYTE (modfp);
wh.panning =_mm_read_UBYTE (modfp);
wh.relnote =_mm_read_SBYTE (modfp);
wh.reserved =_mm_read_UBYTE (modfp);
_mm_read_str(wh.samplename, 22, modfp);
/* printf("wav %d:%22.22s\n",u,wh.samplename); */
q->samplename =DupStr(wh.samplename,22);
q->length =wh.length;
q->loopstart =wh.loopstart;
q->loopend =wh.loopstart+wh.looplength;
q->volume =wh.volume;
q->c2spd =wh.finetune+128;
q->transpose =wh.relnote;
q->panning =wh.panning;
q->seekpos =next;
if(wh.type&0x10){
q->length>>=1;
q->loopstart>>=1;
q->loopend>>=1;
}
next+=wh.length;
/* printf("Type %u\n",wh.type); */
/* printf("Trans %d\n",wh.relnote); */
q->flags|=SF_OWNPAN;
if(wh.type&0x3) q->flags|=SF_LOOP;
if(wh.type&0x2) q->flags|=SF_BIDI;
if(wh.type&0x10) q->flags|=SF_16BITS;
q->flags|=SF_DELTA;
q->flags|=SF_SIGNED;
}
for(u=0;u<ih.numsmp;u++) d->samples[u].seekpos+=_mm_ftell(modfp);
_mm_fseek(modfp,next,SEEK_CUR);
}
d++;
}
return 1;
}
LOADER load_xm={
NULL,
"XM",
"Portable XM loader v0.4 - for your ears only / MikMak",
XM_Init,
XM_Test,
XM_Load,
XM_Cleanup
};