home *** CD-ROM | disk | FTP | other *** search
- /* MikMod sound library
- (c) 1998 Miodrag Vallat and others - see file AUTHORS for complete list
-
- This library is free software; you can redistribute it and/or modify
- it under the terms of the GNU Library General Public License as
- published by the Free Software Foundation; either version 2 of
- the License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
- /*==============================================================================
-
- $Id: load_xm.c,v 1.18 1998/12/07 06:00:24 miod Exp $
-
- Fasttracker (XM) module loader
-
- ==============================================================================*/
-
- #ifdef HAVE_CONFIG_H
- #include "config.h"
- #endif
-
- #include <string.h>
-
- #include <mikmod_internals.h>
-
- /*========== Module structure */
-
- typedef struct XMHEADER {
- CHAR id[17]; /* ID text: 'Extended module: ' */
- CHAR songname[21]; /* Module name */
- CHAR trackername[20]; /* Tracker name */
- UWORD version; /* Version number */
- ULONG headersize; /* Header size */
- UWORD songlength; /* Song length (in patten order table) */
- UWORD restart; /* Restart position */
- UWORD numchn; /* Number of channels (2,4,6,8,10,...,32) */
- UWORD numpat; /* Number of patterns (max 256) */
- UWORD numins; /* Number of instruments (max 128) */
- UWORD flags;
- UWORD tempo; /* Default tempo */
- UWORD bpm; /* Default BPM */
- UBYTE orders[256]; /* Pattern order table */
- } XMHEADER;
-
- typedef struct XMINSTHEADER {
- ULONG size; /* Instrument size */
- CHAR name[22]; /* Instrument name */
- UBYTE type; /* Instrument type (always 0) */
- UWORD numsmp; /* Number of samples in instrument */
- ULONG ssize;
- } XMINSTHEADER;
-
- #define XMENVCNT 24
- #define XMNOTECNT 8*OCTAVE
- typedef struct XMPATCHHEADER {
- UBYTE what[XMNOTECNT]; /* Sample number for all notes */
- UWORD volenv[XMENVCNT]; /* Points for volume envelope */
- UWORD panenv[XMENVCNT]; /* Points for panning envelope */
- UBYTE volpts; /* Number of volume points */
- UBYTE panpts; /* Number of panning points */
- UBYTE volsus; /* Volume sustain point */
- UBYTE volbeg; /* Volume loop start point */
- UBYTE volend; /* Volume loop end point */
- UBYTE pansus; /* Panning sustain point */
- UBYTE panbeg; /* Panning loop start point */
- UBYTE panend; /* Panning loop end point */
- UBYTE volflg; /* Volume type: bit 0: On; 1: Sustain; 2: Loop */
- UBYTE panflg; /* Panning type: bit 0: On; 1: Sustain; 2: Loop */
- UBYTE vibflg; /* Vibrato type */
- UBYTE vibsweep; /* Vibrato sweep */
- UBYTE vibdepth; /* Vibrato depth */
- UBYTE vibrate; /* Vibrato rate */
- UWORD volfade; /* Volume fadeout */
- UWORD reserved[11];
- } XMPATCHHEADER;
-
- typedef struct XMWAVHEADER {
- ULONG length; /* Sample length */
- ULONG loopstart; /* Sample loop start */
- ULONG looplength; /* Sample loop length */
- UBYTE volume; /* Volume */
- SBYTE finetune; /* Finetune (signed byte -128..+127) */
- UBYTE type; /* Loop type */
- UBYTE panning; /* Panning (0-255) */
- SBYTE relnote; /* Relative note number (signed byte) */
- UBYTE reserved; /* Reserved */
- CHAR samplename[22]; /* Sample name */
- UBYTE vibtype; /* Vibrato type */
- UBYTE vibsweep; /* Vibrato sweep */
- UBYTE vibdepth; /* Vibrato depth */
- UBYTE vibrate; /* Vibrato rate */
- } XMWAVHEADER;
-
- typedef struct XMPATHEADER {
- ULONG size; /* Pattern header length */
- UBYTE packing; /* Packing type (always 0) */
- UWORD numrows; /* Number of rows in pattern (1..256) */
- UWORD packsize; /* Packed patterndata size */
- } XMPATHEADER;
-
- typedef struct XMNOTE {
- UBYTE note,ins,vol,eff,dat;
- } XMNOTE;
-
- /*========== Loader variables */
-
- static XMNOTE *xmpat = NULL;
- static XMHEADER *mh = NULL;
-
- /*========== Loader code */
-
- BOOL XM_Test(void)
- {
- UBYTE id[38];
-
- if(!_mm_read_UBYTES(id,38,modfp)) return 0;
- if(memcmp(id,"Extended Module: ",17)) return 0;
- if(id[37]==0x1a) return 1;
- return 0;
- }
-
- BOOL XM_Init(void)
- {
- if(!(mh=(XMHEADER *)_mm_malloc(sizeof(XMHEADER)))) return 0;
- return 1;
- }
-
- void XM_Cleanup(void)
- {
- if(mh) free(mh);
- mh = NULL;
- }
-
- void XM_ReadNote(XMNOTE* n)
- {
- UBYTE cmp;
-
- memset(n,0,sizeof(XMNOTE));
- cmp=_mm_read_UBYTE(modfp);
-
- if(cmp&0x80) {
- if(cmp&1) n->note = _mm_read_UBYTE(modfp);
- if(cmp&2) n->ins = _mm_read_UBYTE(modfp);
- if(cmp&4) n->vol = _mm_read_UBYTE(modfp);
- if(cmp&8) n->eff = _mm_read_UBYTE(modfp);
- if(cmp&16) n->dat = _mm_read_UBYTE(modfp);
- } else {
- n->note = cmp;
- n->ins = _mm_read_UBYTE(modfp);
- n->vol = _mm_read_UBYTE(modfp);
- n->eff = _mm_read_UBYTE(modfp);
- n->dat = _mm_read_UBYTE(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) {
- if(note==8*OCTAVE+1) {
- UniWrite(UNI_KEYFADE);
- UniWrite(0);
- } else
- UniNote(note-1);
- }
- if(ins) UniInstrument(ins-1);
-
- 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 not zero) */
- if(vol&0xf) {
- UniWrite(UNI_XMEFFECTP);
- UniWrite(vol&0xf);
- }
- break;
- case 0xe: /* panning slide right (only slide when data 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);
- }
-
- switch(eff) {
- case 0x4: /* Effect 4: Vibrato */
- UniWrite(UNI_XMEFFECT4);
- UniWrite(dat);
- break;
- case 0xa:
- UniWrite(UNI_XMEFFECTA);
- UniWrite(dat);
- break;
- case 0xe: /* Extended effects */
- switch(dat>>4) {
- case 0x1: /* XM fine porta up */
- UniWrite(UNI_XMEFFECTE1);
- UniWrite(dat&0xf);
- break;
- case 0x2: /* XM fine porta down */
- UniWrite(UNI_XMEFFECTE2);
- UniWrite(dat&0xf);
- break;
- case 0xa: /* XM fine volume up */
- UniWrite(UNI_XMEFFECTEA);
- UniWrite(dat&0xf);
- break;
- case 0xb: /* XM fine volume down */
- UniWrite(UNI_XMEFFECTEB);
- UniWrite(dat&0xf);
- break;
- default:
- UniPTEffect(0x0e,dat);
- }
- break;
- case 'G'-55: /* G - set global volume */
- UniWrite(UNI_XMEFFECTG);
- UniWrite(dat>64?64:dat);
- break;
- case 'H'-55: /* H - global volume slide */
- UniWrite(UNI_XMEFFECTH);
- UniWrite(dat);
- break;
- case 'K'-55: /* K - keyOff and KeyFade */
- UniWrite(UNI_KEYFADE);
- UniWrite(dat);
- break;
- case 'L'-55: /* L - set envelope position */
- UniWrite(UNI_XMEFFECTL);
- UniWrite(dat);
- 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: /* T - Tremor */
- UniWrite(UNI_S3MEFFECTI);
- UniWrite(dat);
- break;
- case 'X'-55:
- switch(dat>>4) {
- case 1: /* X1 - Extra Fine Porta up */
- UniWrite(UNI_XMEFFECTX1);
- UniWrite(dat&0xf);
- break;
- case 2: /* X2 - Extra Fine Porta down */
- UniWrite(UNI_XMEFFECTX2);
- UniWrite(dat&0xf);
- break;
- }
- break;
- default:
- if(!(eff>>4)) {
- /* Convert pattern jump from Dec to Hex */
- if(eff==0xd)
- dat=(((dat&0xf0)>>4)*10)+(dat&0xf);
- /* finetune conversion to Lxx */
- if(eff==5)
- S3MIT_ProcessCmd(0xc,dat,1);
- else
- UniPTEffect(eff,dat);
- }
- break;
- }
- UniNewline();
- xmtrack++;
- }
- return UniDup();
- }
-
- BOOL XM_Load(BOOL curious)
- {
- #define XM_SMPINCR 64
- INSTRUMENT *d;
- SAMPLE *q;
- XMWAVHEADER *wh=NULL,*s=NULL;
- int t,u,v,p,numtrk;
- long next;
- ULONG *nextwav=NULL;
- UWORD wavcnt=0;
- BOOL dummypat=0;
-
- /* try to read module header */
- _mm_read_string(mh->id,17,modfp);
- _mm_read_string(mh->songname,21,modfp);
- _mm_read_string(mh->trackername,20,modfp);
- mh->version =_mm_read_I_UWORD(modfp);
- if((mh->version<0x100)||(mh->version>0x104)) {
- _mm_errno=MMERR_NOT_A_MODULE;
- return 0;
- }
- 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);
- if(!mh->bpm) {
- _mm_errno=MMERR_NOT_A_MODULE;
- return 0;
- }
- _mm_read_UBYTES(mh->orders,256,modfp);
-
- if(feof(modfp)) {
- _mm_errno = MMERR_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);
- of.numpos = mh->songlength; /* copy the songlength */
- of.reppos = mh->restart<mh->songlength?mh->restart:0;
- of.numins = mh->numins;
- of.flags |= UF_XMPERIODS | UF_INST;
- if(mh->flags&1) of.flags |= UF_LINEAR;
-
- memset(of.chanvol,64,of.numchn); /* store channel volumes */
-
- if(!AllocPositions(of.numpos+1)) return 0;
- for(t=0;t<of.numpos;t++)
- of.positions[t]=mh->orders[t];
-
- /* We have to check for any pattern numbers in the order list greater than
- the number of patterns total. If one or more is found, we set it equal to
- the pattern total and make a dummy pattern to workaround the problem */
- for(t=0;t<of.numpos;t++) {
- if(of.positions[t]>=of.numpat) {
- of.positions[t]=of.numpat;
- dummypat=1;
- }
- }
- if(dummypat) {
- of.numpat++;of.numtrk+=of.numchn;
- }
-
- if(!AllocTracks()) return 0;
- if(!AllocPatterns()) return 0;
- numtrk=0;
- for(t=0;t<mh->numpat;t++) {
- XMPATHEADER ph;
-
- ph.size =_mm_read_I_ULONG(modfp);
- ph.packing =_mm_read_UBYTE(modfp);
- if(ph.packing) {
- _mm_errno=MMERR_LOADING_PATTERN;
- return 0;
- }
- ph.numrows =_mm_read_I_UWORD(modfp);
- ph.packsize =_mm_read_I_UWORD(modfp);
-
- of.pattrows[t]=ph.numrows;
-
- if(ph.numrows) {
- if(!(xmpat=(XMNOTE*)_mm_calloc(ph.numrows*of.numchn,sizeof(XMNOTE))))
- return 0;
-
- /* when packsize is 0, don't try to load a pattern.. it's empty. */
- if(ph.packsize)
- for(u=0;u<ph.numrows;u++)
- for(v=0;v<of.numchn;v++)
- XM_ReadNote(&xmpat[(v*ph.numrows)+u]);
-
- if(feof(modfp)) {
- free(xmpat);
- _mm_errno=MMERR_LOADING_PATTERN;
- return 0;
- }
-
- for(v=0;v<of.numchn;v++)
- of.tracks[numtrk++]=XM_Convert(&xmpat[v*ph.numrows],ph.numrows);
-
- free(xmpat);
- } else {
- for(v=0;v<of.numchn;v++)
- of.tracks[numtrk++]=XM_Convert(NULL,ph.numrows);
- }
- }
-
- if(dummypat) {
- of.pattrows[t]=64;
- if(!(xmpat=(XMNOTE*)_mm_calloc(64*of.numchn,sizeof(XMNOTE)))) return 0;
- for(v=0;v<of.numchn;v++)
- of.tracks[numtrk++]=XM_Convert(&xmpat[v*64],64);
- free(xmpat);
- }
-
- if(!AllocInstruments()) return 0;
- d=of.instruments;
-
- for(t=0;t<of.numins;t++) {
- XMINSTHEADER ih;
- int headend;
-
- memset(d->samplenumber,255,INSTNOTES);
-
- /* read instrument header */
- headend = _mm_ftell(modfp);
- ih.size = _mm_read_I_ULONG(modfp);
- headend += ih.size;
- _mm_read_string(ih.name, 22, modfp);
- ih.type = _mm_read_UBYTE(modfp);
- ih.numsmp = _mm_read_I_UWORD(modfp);
- d->insname = DupStr(ih.name,22);
-
- if((SWORD)ih.size>29) {
- ih.ssize = _mm_read_I_ULONG(modfp);
- if(((SWORD)ih.numsmp>0)&&(ih.numsmp<=XMNOTECNT)) {
- XMPATCHHEADER pth;
-
- _mm_read_UBYTES (pth.what,XMNOTECNT,modfp);
- _mm_read_I_UWORDS (pth.volenv, XMENVCNT, modfp);
- _mm_read_I_UWORDS (pth.panenv, XMENVCNT, 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);
-
- /* read the remainder of the header */
- for(u=headend-_mm_ftell(modfp);u;u--) _mm_read_UBYTE(modfp);
-
- if(feof(modfp)) {
- free(nextwav);free(wh);
- _mm_errno = MMERR_LOADING_SAMPLEINFO;
- return 0;
- }
-
- for(u=0;u<XMNOTECNT;u++)
- d->samplenumber[u]=pth.what[u]+of.numsmp;
- d->volfade = pth.volfade;
- memcpy(d->volenv,pth.volenv,XMENVCNT);
- if(pth.volflg & 1) d->volflg |= EF_ON;
- if(pth.volflg & 2) d->volflg |= EF_SUSTAIN;
- if(pth.volflg & 4) d->volflg |= EF_LOOP;
- d->volsusbeg = d->volsusend = pth.volsus;
- d->volbeg = pth.volbeg;
- d->volend = pth.volend;
- d->volpts = pth.volpts;
-
- /* scale volume envelope: */
- for(p=0;p<XMENVCNT/2;p++)
- d->volenv[p].val<<=2;
-
- if((d->volflg & EF_ON)&&(d->volpts<2))
- d->volflg &= ~EF_ON;
-
- memcpy(d->panenv,pth.panenv,XMENVCNT);
- d->panflg = pth.panflg;
- d->pansusbeg = d->pansusend = pth.pansus;
- d->panbeg = pth.panbeg;
- d->panend = pth.panend;
- d->panpts = pth.panpts;
-
- /* scale panning envelope: */
- for(p=0;p<XMENVCNT/2;p++)
- d->panenv[p].val<<=2;
- if((d->panflg & EF_ON) && (d->panpts<2))
- d->panflg &= ~EF_ON;
- next = 0;
-
- /* Samples are stored outside the instrument struct now, so we
- have to load them all into a temp area, count the of.numsmp
- along the way and then do an AllocSamples() and move
- everything over */
- for(u=0; u<ih.numsmp;u++,s++) {
- /* Allocate more room for sample information if necessary */
- if(of.numsmp+u==wavcnt) {
- wavcnt+=XM_SMPINCR;
- if(!(nextwav=realloc(nextwav,wavcnt*sizeof(ULONG)))){
- free(wh);
- _mm_errno = MMERR_OUT_OF_MEMORY;
- return 0;
- }
- if(!(wh=realloc(wh,wavcnt*sizeof(XMWAVHEADER)))) {
- free(nextwav);
- _mm_errno = MMERR_OUT_OF_MEMORY;
- return 0;
- }
- s=wh+(wavcnt-XM_SMPINCR);
- }
-
- s->length =_mm_read_I_ULONG (modfp);
- s->loopstart =_mm_read_I_ULONG (modfp);
- s->looplength =_mm_read_I_ULONG (modfp);
- s->volume =_mm_read_UBYTE (modfp);
- s->finetune =_mm_read_SBYTE (modfp);
- s->type =_mm_read_UBYTE (modfp);
- s->panning =_mm_read_UBYTE (modfp);
- s->relnote =_mm_read_SBYTE (modfp);
- s->vibtype = pth.vibflg;
- s->vibsweep = pth.vibsweep;
- s->vibdepth = pth.vibdepth*4;
- s->vibrate = pth.vibrate;
- s->reserved =_mm_read_UBYTE (modfp);
- _mm_read_string(s->samplename, 22, modfp);
-
- nextwav[of.numsmp+u]=next;
- next+=s->length;
-
- if(feof(modfp)) {
- free(nextwav);free(wh);
- _mm_errno = MMERR_LOADING_SAMPLEINFO;
- return 0;
- }
- }
-
- for(u=0;u<ih.numsmp;u++)
- nextwav[of.numsmp++]+=_mm_ftell(modfp);
- _mm_fseek(modfp,next,SEEK_CUR);
- }
- }
- d++;
- }
-
- /* sanity check */
- if(!of.numsmp) {
- free(nextwav);free(wh);
- _mm_errno = MMERR_LOADING_SAMPLEINFO;
- return 0;
- }
-
- if(!AllocSamples()) {
- free(nextwav);free(wh);
- return 0;
- }
- q = of.samples;
- s = wh;
- for(u=0;u<of.numsmp;u++,q++,s++) {
- q->samplename = DupStr(s->samplename,22);
- q->length = s->length;
- q->loopstart = s->loopstart;
- q->loopend = s->loopstart+s->looplength;
- q->volume = s->volume;
- q->speed = s->finetune+128;
- q->panning = s->panning;
- q->seekpos = nextwav[u];
- q->vibtype = s->vibtype;
- q->vibsweep = s->vibsweep;
- q->vibdepth = s->vibdepth;
- q->vibrate = s->vibrate;
-
- if(s->type & 0x10) {
- q->length >>= 1;
- q->loopstart >>= 1;
- q->loopend >>= 1;
- }
-
- q->flags|=SF_OWNPAN;
- if(s->type&0x3) q->flags|=SF_LOOP;
- if(s->type&0x2) q->flags|=SF_BIDI;
- if(s->type&0x10) q->flags|=SF_16BITS;
- q->flags|=SF_DELTA|SF_SIGNED;
- }
-
- d = of.instruments;
- s = wh;
- for(u=0;u<of.numins;u++,d++) {
- for(t=0;t<XMNOTECNT;t++)
- d->samplenote[t]=(d->samplenumber[t]>=of.numsmp)?
- 255:(t+s[d->samplenumber[t]].relnote);
- }
-
- free(wh);free(nextwav);
- return 1;
- }
-
- CHAR *XM_LoadTitle(void)
- {
- CHAR s[21];
-
- _mm_fseek(modfp,17,SEEK_SET);
- if(!fread(s,21,1,modfp)) return NULL;
-
- return(DupStr(s,21));
- }
-
- /*========== Loader information */
-
- MLOADER load_xm={
- NULL,
- "XM",
- "XM loader v1.0",
- XM_Init,
- XM_Test,
- XM_Load,
- XM_Cleanup,
- XM_LoadTitle
- };
-