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_med.c,v 1.17 1998/12/07 06:00:16 miod Exp $
-
- Amiga MED module loader
-
- ==============================================================================*/
-
- #ifdef HAVE_CONFIG_H
- #include "config.h"
- #endif
-
- #include <string.h>
-
- #include <mikmod_internals.h>
-
- /*========== Module information */
-
- typedef struct MMD0 {
- ULONG id;
- ULONG modlen;
- ULONG MMD0songP; /* struct MMD0song *song; */
- UWORD psecnum; /* for the player routine, MMD2 only */
- UWORD pseq; /* " " " " */
- ULONG MMD0BlockPP; /* struct MMD0Block **blockarr; */
- ULONG reserved1;
- ULONG InstrHdrPP; /* struct InstrHdr **smplarr; */
- ULONG reserved2;
- ULONG MMD0expP; /* struct MMD0exp *expdata; */
- ULONG reserved3;
- UWORD pstate; /* some data for the player routine */
- UWORD pblock;
- UWORD pline;
- UWORD pseqnum;
- SWORD actplayline;
- UBYTE counter;
- UBYTE extra_songs; /* number of songs - 1 */
- } MMD0;
-
- typedef struct MMD0sample {
- UWORD rep,replen; /* offs: 0(s), 2(s) */
- UBYTE midich; /* offs: 4(s) */
- UBYTE midipreset; /* offs: 5(s) */
- UBYTE svol; /* offs: 6(s) */
- SBYTE strans; /* offs: 7(s) */
- } MMD0sample;
-
- typedef struct MMD0song {
- MMD0sample sample[63]; /* 63 * 8 bytes = 504 bytes */
- UWORD numblocks; /* offs: 504 */
- UWORD songlen; /* offs: 506 */
- UBYTE playseq[256]; /* offs: 508 */
- UWORD deftempo; /* offs: 764 */
- SBYTE playtransp; /* offs: 766 */
- UBYTE flags; /* offs: 767 */
- UBYTE flags2; /* offs: 768 */
- UBYTE tempo2; /* offs: 769 */
- UBYTE trkvol[16]; /* offs: 770 */
- UBYTE mastervol; /* offs: 786 */
- UBYTE numsamples; /* offs: 787 */
- } MMD0song;
-
- typedef struct MMD0NOTE {
- UBYTE a,b,c;
- } MMD0NOTE;
-
- typedef struct MMD1NOTE {
- UBYTE a,b,c,d;
- } MMD1NOTE;
-
- typedef struct InstrHdr {
- ULONG length;
- SWORD type;
- /* Followed by actual data */
- } InstrHdr;
-
- /*========== Loader variables */
-
- #define MMD0_string 0x4D4D4430
- #define MMD1_string 0x4D4D4431
-
- static MMD0 *mh = NULL;
- static MMD0song *ms = NULL;
- static ULONG *ba = NULL;
- static MMD0NOTE *mmd0pat = NULL;
- static MMD1NOTE *mmd1pat = NULL;
-
- static int currentspeed,currentbpm,speeddivisor;
- static BOOL decimalvolumes;
- static BOOL bpmtempos;
-
- #define d0note(row,col) mmd0pat[((row)*(UWORD)of.numchn)+(col)]
- #define d1note(row,col) mmd1pat[((row)*(UWORD)of.numchn)+(col)]
-
- static CHAR MED_Version[] = "OctaMED";
-
- /*========== Loader code */
-
- BOOL MED_Test(void)
- {
- UBYTE id[4];
-
- if(!_mm_read_UBYTES(id,4,modfp)) return 0;
- if((!memcmp(id,"MMD0",4))||(!memcmp(id,"MMD1",4))) return 1;
- return 0;
- }
-
- BOOL MED_Init(void)
- {
- if(!(mh=(MMD0*)_mm_malloc(sizeof(MMD0)))) return 0;
- if(!(ms=(MMD0song*)_mm_malloc(sizeof(MMD0song)))) return 0;
- return 1;
- }
-
- void MED_Cleanup(void)
- {
- if(mh) free(mh);
- if(ms) free(ms);
- if(ba) free(ba);
- if(mmd0pat) free(mmd0pat);
- if(mmd1pat) free(mmd1pat);
-
- mh = NULL;
- ms = NULL;
- ba = NULL;
- mmd0pat = NULL;
- mmd1pat = NULL;
- }
-
- void EffectCvt(UBYTE eff,UBYTE dat)
- {
- switch(eff) {
- /* 0x0 0x1 0x2 0x3 0x4 PT effects */
- case 0x5: /* PT vibrato with speed/depth nibbles swapped */
- UniPTEffect(0x4,(dat>>4)|((dat&0xf)<<4));
- break;
- /* 0x6 0x7 not used */
- case 0x6:
- case 0x7:
- break;
- case 0x8: /* midi hold/decay */
- break;
- case 0x9:
- if(dat<=0x20) {
- if(!dat) currentspeed=of.initspeed;
- else {
- if(bpmtempos) currentspeed=dat*4/speeddivisor;
- else currentspeed=dat/speeddivisor;
- }
- UniPTEffect(0xf,currentspeed);
- } else {
- if(bpmtempos)
- currentbpm=dat;
- else {
- int val=((UWORD)dat*125)/(33*speeddivisor);
- currentbpm=(val<32)?32:(val>255?255:val);
- }
- UniPTEffect(0xf,currentbpm);
- }
- break;
- /* 0xa 0xb PT effects */
- case 0xc:
- if(decimalvolumes) dat=(dat>>4)*10+(dat&0xf);
- UniPTEffect(0xc,dat);
- break;
- case 0xd: /* same as PT volslide */
- UniPTEffect(0xa,dat);
- break;
- case 0xe: /* synth jmp - midi */
- break;
- case 0xf:
- switch(dat) {
- case 0: /* patternbreak */
- UniPTEffect(0xd,0);
- break;
- case 0xf1: /* play note twice */
- UniPTEffect(0xe,0x90|(currentspeed/2));
- break;
- case 0xf2: /* delay note */
- UniPTEffect(0xe,0xd0|(currentspeed/2));
- break;
- case 0xf3: /* play note three times */
- UniPTEffect(0xe,0x90|(currentspeed/3));
- break;
- case 0xfe: /* stop playing */
- UniPTEffect(0xb,of.numpat);
- break;
- case 0xff: /* note cut */
- UniPTEffect(0xc,0);
- break;
- default:
- if(dat<=10) {
- currentspeed=dat*4/speeddivisor;
- UniPTEffect(0xf,currentspeed);
- } else if(dat<=240) {
- if(bpmtempos)
- currentbpm=(dat<32)?32:dat;
- else {
- int val=((UWORD)dat*125)/33;
- currentbpm=(val<32)?32:(val>255?255:val);
- }
- UniPTEffect(0xf,currentbpm);
- }
- }
- break;
- default: /* all normal PT effects are handled here */
- UniPTEffect(eff,dat);
- break;
- }
- }
-
- UBYTE *MED_Convert1(int count,int col)
- {
- int t;
- UBYTE inst,note,eff,dat;
- MMD1NOTE *n;
- int trackspeed=currentspeed,trackbpm=currentbpm;
-
- UniReset();
- for(t=0;t<count;t++) {
- n=&d1note(t,col);
-
- note=n->a&0x7f;
- inst=n->b&0x3f;
- eff =n->c&0xf;
- dat =n->d;
-
- if(inst) UniInstrument(inst-1);
- if(note) UniNote(note+3*OCTAVE-1);
-
- if((eff!=9)||(dat>0x20))
- if(trackspeed!=currentspeed) UniPTEffect(0xf,currentspeed=trackspeed);
- if((eff!=9)||(dat<=0x20))
- if(trackbpm!=currentbpm) UniPTEffect(0xf,currentbpm=trackbpm);
- EffectCvt(eff,dat);
- if((eff!=9)||(!t)) {
- trackspeed=currentspeed;
- trackbpm=currentbpm;
- }
- UniNewline();
- }
- return UniDup();
- }
-
- UBYTE *MED_Convert0(int count,int col)
- {
- int t;
- UBYTE a,b,inst,note,eff,dat;
- MMD0NOTE *n;
- int trackspeed=currentspeed,trackbpm=currentbpm;
-
- UniReset();
- for(t=0;t<count;t++) {
- n=&d0note(t,col);
- a=n->a;
- b=n->b;
-
- note=a&0x3f;
- a>>=6;
- a=((a&1)<<1)|(a>>1);
- inst=(b>>4)|(a<<4);
- eff=b&0xf;
- dat=n->c;
-
- if(inst) UniInstrument(inst-1);
- if(note) UniNote(note+3*OCTAVE-1);
-
- if((eff!=9)||(dat>0x20))
- if(trackspeed!=currentspeed) UniPTEffect(0xf,currentspeed=trackspeed);
- if((eff!=9)||(dat<=0x20))
- if(trackbpm!=currentbpm) UniPTEffect(0xf,currentbpm=trackbpm);
- EffectCvt(eff,dat);
- if((eff!=9)||(!t)) {
- trackspeed=currentspeed;
- trackbpm=currentbpm;
- }
- UniNewline();
- }
- return UniDup();
- }
-
- BOOL LoadMMD0Patterns(void)
- {
- int t,row,col;
- UWORD numtracks,numlines,maxlines=0,track=0;
- MMD0NOTE *mmdp;
-
- /* first, scan patterns to see how many channels are used */
- for(t=0;t<of.numpat;t++) {
- _mm_fseek(modfp,ba[t],SEEK_SET);
- numtracks=_mm_read_UBYTE(modfp);
- numlines=_mm_read_UBYTE(modfp);
-
- if(numtracks>of.numchn) of.numchn=numtracks;
- if(numlines>maxlines) maxlines=numlines;
- }
-
- of.numtrk=of.numpat*of.numchn;
- if(!AllocTracks()) return 0;
- if(!AllocPatterns()) return 0;
-
- if(!(mmd0pat=(MMD0NOTE*)_mm_calloc(of.numchn*(maxlines+1),sizeof(MMD0NOTE))))
- return 0;
-
- /* second read: read and convert patterns */
- for(t=0;t<of.numpat;t++) {
- _mm_fseek(modfp,ba[t],SEEK_SET);
- numtracks=_mm_read_UBYTE(modfp);
- numlines=_mm_read_UBYTE(modfp);
-
- of.pattrows[t]=++numlines;
- memset(mmdp=mmd0pat,0,of.numchn*maxlines*sizeof(MMD0NOTE));
- for(row=numlines;row;row--) {
- for(col=numtracks;col;col--,mmdp++) {
- mmdp->a=_mm_read_UBYTE(modfp);
- mmdp->b=_mm_read_UBYTE(modfp);
- mmdp->c=_mm_read_UBYTE(modfp);
- }
- }
-
- for(col=0;col<of.numchn;col++)
- of.tracks[track++]=MED_Convert0(numlines,col);
- }
- return 1;
- }
-
- BOOL LoadMMD1Patterns(void)
- {
- int t,row,col;
- UWORD numtracks,numlines,maxlines=0,track=0;
- MMD1NOTE *mmdp;
-
- /* first, scan patterns to see how many channels are used */
- for(t=0;t<of.numpat;t++) {
- _mm_fseek(modfp,ba[t],SEEK_SET);
- numtracks=_mm_read_M_UWORD(modfp);
- numlines=_mm_read_M_UWORD(modfp);
- if(numtracks>of.numchn) of.numchn=numtracks;
- if(numlines>maxlines) maxlines=numlines;
- }
-
- of.numtrk = of.numpat*of.numchn;
- if(!AllocTracks()) return 0;
- if(!AllocPatterns()) return 0;
-
- if(!(mmd1pat=(MMD1NOTE*)_mm_calloc(of.numchn*(maxlines+1),sizeof(MMD1NOTE))))
- return 0;
-
- /* second read: really read and convert patterns */
- for(t=0;t<of.numpat;t++) {
- _mm_fseek(modfp,ba[t],SEEK_SET);
- numtracks=_mm_read_M_UWORD(modfp);
- numlines=_mm_read_M_UWORD(modfp);
-
- _mm_fseek(modfp,sizeof(ULONG),SEEK_CUR);
- of.pattrows[t]=++numlines;
- memset(mmdp=mmd1pat,0,of.numchn*maxlines*sizeof(MMD1NOTE));
-
- for(row=numlines;row;row--) {
- for(col=numtracks;col;col--,mmdp++) {
- mmdp->a=_mm_read_UBYTE(modfp);
- mmdp->b=_mm_read_UBYTE(modfp);
- mmdp->c=_mm_read_UBYTE(modfp);
- mmdp->d=_mm_read_UBYTE(modfp);
- }
- }
-
- for(col=0;col<of.numchn;col++)
- of.tracks[track++]=MED_Convert1(numlines,col);
- }
- return 1;
- }
-
- BOOL MED_Load(BOOL curious)
- {
- int t;
- ULONG sa[64];
- InstrHdr s;
- SAMPLE *q;
- MMD0sample *mss;
-
- /* try to read module header */
- mh->id = _mm_read_M_ULONG(modfp);
- mh->modlen = _mm_read_M_ULONG(modfp);
- mh->MMD0songP = _mm_read_M_ULONG(modfp);
- mh->psecnum = _mm_read_M_UWORD(modfp);
- mh->pseq = _mm_read_M_UWORD(modfp);
- mh->MMD0BlockPP = _mm_read_M_ULONG(modfp);
- mh->reserved1 = _mm_read_M_ULONG(modfp);
- mh->InstrHdrPP = _mm_read_M_ULONG(modfp);
- mh->reserved2 = _mm_read_M_ULONG(modfp);
- mh->MMD0expP = _mm_read_M_ULONG(modfp);
- mh->reserved3 = _mm_read_M_ULONG(modfp);
- mh->pstate = _mm_read_M_UWORD(modfp);
- mh->pblock = _mm_read_M_UWORD(modfp);
- mh->pline = _mm_read_M_UWORD(modfp);
- mh->pseqnum = _mm_read_M_UWORD(modfp);
- mh->actplayline = _mm_read_M_SWORD(modfp);
- mh->counter = _mm_read_UBYTE(modfp);
- mh->extra_songs = _mm_read_UBYTE(modfp);
-
- /* Seek to MMD0song struct */
- _mm_fseek(modfp,mh->MMD0songP,SEEK_SET);
-
- /* Load the MMD0 Song Header */
- mss = ms->sample; /* load the sample data first */
- for(t=63;t;t--,mss++) {
- mss->rep = _mm_read_M_UWORD(modfp);
- mss->replen = _mm_read_M_UWORD(modfp);
- mss->midich = _mm_read_UBYTE(modfp);
- mss->midipreset = _mm_read_UBYTE(modfp);
- mss->svol = _mm_read_UBYTE(modfp);
- mss->strans = _mm_read_SBYTE(modfp);
- }
-
- ms->numblocks = _mm_read_M_UWORD(modfp);
- ms->songlen = _mm_read_M_UWORD(modfp);
- _mm_read_UBYTES(ms->playseq,256,modfp);
- ms->deftempo = _mm_read_M_UWORD(modfp);
- ms->playtransp = _mm_read_SBYTE(modfp);
- ms->flags = _mm_read_UBYTE(modfp);
- ms->flags2 = _mm_read_UBYTE(modfp);
- ms->tempo2 = _mm_read_UBYTE(modfp);
- _mm_read_UBYTES(ms->trkvol,16,modfp);
- ms->mastervol = _mm_read_UBYTE(modfp);
- ms->numsamples = _mm_read_UBYTE(modfp);
-
- /* check for a bad header */
- if(feof(modfp)) {
- _mm_errno = MMERR_LOADING_HEADER;
- return 0;
- }
-
- /* seek to and read the samplepointer array */
- _mm_fseek(modfp,mh->InstrHdrPP,SEEK_SET);
- if(!_mm_read_M_ULONGS(sa,ms->numsamples,modfp)) {
- _mm_errno = MMERR_LOADING_HEADER;
- return 0;
- }
-
- /* alloc and read the blockpointer array */
- if(!(ba=(ULONG *)_mm_calloc(ms->numblocks, sizeof(ULONG)))) return 0;
- _mm_fseek(modfp,mh->MMD0BlockPP,SEEK_SET);
- if(!_mm_read_M_ULONGS(ba,ms->numblocks,modfp)) {
- _mm_errno = MMERR_LOADING_HEADER;
- return 0;
- }
-
- /* copy song positions */
- if(!AllocPositions(ms->songlen)) return 0;
- for(t=0;t<ms->songlen;t++)
- of.positions[t]=ms->playseq[t];
-
- decimalvolumes=(ms->flags&0x10)?0:1;
- bpmtempos=(ms->flags2&0x20)?1:0;
-
- if(bpmtempos) {
- speeddivisor=(ms->flags2&0x1f)+1;
- of.initspeed=(ms->tempo2*4)/speeddivisor;
- of.inittempo=ms->deftempo;
- } else {
- of.initspeed=ms->tempo2;
- speeddivisor=4;
- of.inittempo=(ms->deftempo>=68)?255:(ms->deftempo?ms->deftempo*125/33:128);
- }
- currentspeed=of.initspeed;
- currentbpm=of.inittempo;
- of.modtype = strdup(MED_Version);
- of.numchn = 0; /* will be counted later */
- of.numpat = ms->numblocks;
- of.numpos = ms->songlen;
- of.numins = ms->numsamples;
- of.numsmp = of.numins;
- of.songname = DupStr(NULL,0);
- of.reppos = 0;
-
- if(!AllocSamples()) return 0;
- q = of.samples;
- for(t=0;t<of.numins;t++) {
- q->flags = SF_SIGNED;
- q->speed = 8363;
- q->volume = 64;
- q->samplename = NULL;
- if (sa[t]) {
- _mm_fseek(modfp,sa[t],SEEK_SET);
- s.length = _mm_read_M_ULONG(modfp);
- s.type = _mm_read_M_SWORD(modfp);
-
- if(s.type) {
- #ifdef MIKMOD_DEBUG
- fputs("\rNon-sample instruments not supported in MED loader yet\n",stderr);
- #endif
- if(!curious) {
- _mm_errno = MMERR_MED_SYNTHSAMPLES;
- return 0;
- }
- s.length=0;
- }
-
- if(feof(modfp)) {
- _mm_errno = MMERR_LOADING_SAMPLEINFO;
- return 0;
- }
-
- q->length = s.length;
- q->seekpos = _mm_ftell(modfp);
- q->loopstart = ms->sample[t].rep<<1;
- q->loopend = q->loopstart+(ms->sample[t].replen<<1);
-
- if(ms->sample[t].replen>1) q->flags|=SF_LOOP;
-
- /* don't load sample if length>='MMD0'...
- such kluges make MikMod code unique !!! */
- if(q->length >= MMD0_string) q->length=0;
- } else
- q->length=0;
- q++;
- }
-
- if(mh->id==MMD0_string) {
- if(!LoadMMD0Patterns()) {
- _mm_errno=MMERR_LOADING_PATTERN;
- return 0;
- }
- } else
- if(mh->id==MMD1_string) {
- if(!LoadMMD1Patterns()) {
- _mm_errno=MMERR_LOADING_PATTERN;
- return 0;
- }
- } else {
- _mm_errno = MMERR_NOT_A_MODULE;
- return 0;
- }
- return 1;
- }
-
- /*========== Loader information */
-
- MLOADER load_med={
- NULL,
- "MED",
- "MED loader v0.2",
- MED_Init,
- MED_Test,
- MED_Load,
- MED_Cleanup,
- NULL
- };
-