From 26f2bfde03420edad4de1f22cb3d515dc063b20d Mon Sep 17 00:00:00 2001 From: Frank Gevaerts Date: Sun, 12 Dec 2010 15:03:30 +0000 Subject: Add MikMod plugin, ported by Jason Yu, with some minor work by Craig Mann and William Peters (FS#8806) git-svn-id: svn://svn.rockbox.org/rockbox/trunk@28810 a1c6a512-1295-4272-9138-f99709370657 --- apps/plugins/mikmod/load_amf.c | 569 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 569 insertions(+) create mode 100644 apps/plugins/mikmod/load_amf.c (limited to 'apps/plugins/mikmod/load_amf.c') diff --git a/apps/plugins/mikmod/load_amf.c b/apps/plugins/mikmod/load_amf.c new file mode 100644 index 0000000000..12e0af0738 --- /dev/null +++ b/apps/plugins/mikmod/load_amf.c @@ -0,0 +1,569 @@ +/* MikMod sound library + (c) 1998, 1999, 2000, 2001, 2002 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., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id: load_amf.c,v 1.3 2005/04/07 19:57:38 realtech Exp $ + + DMP Advanced Module Format loader + +==============================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#ifdef HAVE_MEMORY_H +#include +#endif +#include + +#include "mikmod_internals.h" + +#ifdef SUNOS +extern int fprintf(FILE *, const char *, ...); +#endif + +/*========== Module structure */ + +typedef struct AMFHEADER { + UBYTE id[3]; /* AMF file marker */ + UBYTE version; /* upper major, lower nibble minor version number */ + CHAR songname[32]; /* ASCIIZ songname */ + UBYTE numsamples; /* number of samples saved */ + UBYTE numorders; + UWORD numtracks; /* number of tracks saved */ + UBYTE numchannels; /* number of channels used */ + SBYTE panpos[32]; /* voice pan positions */ + UBYTE songbpm; + UBYTE songspd; +} AMFHEADER; + +typedef struct AMFSAMPLE { + UBYTE type; + CHAR samplename[32]; + CHAR filename[13]; + ULONG offset; + ULONG length; + UWORD c2spd; + UBYTE volume; + ULONG reppos; + ULONG repend; +} AMFSAMPLE; + +typedef struct AMFNOTE { + UBYTE note,instr,volume,fxcnt; + UBYTE effect[3]; + SBYTE parameter[3]; +} AMFNOTE; + +/*========== Loader variables */ + +static AMFHEADER *mh = NULL; +#define AMFTEXTLEN 22 +static CHAR AMF_Version[AMFTEXTLEN+1] = "DSMI Module Format 0.0"; +static AMFNOTE *track = NULL; + +/*========== Loader code */ + +int AMF_Test(void) +{ + UBYTE id[3],ver; + + if(!_mm_read_UBYTES(id,3,modreader)) return 0; + if(memcmp(id,"AMF",3)) return 0; + + ver=_mm_read_UBYTE(modreader); + if((ver>=10)&&(ver<=14)) return 1; + return 0; +} + +int AMF_Init(void) +{ + if(!(mh=(AMFHEADER*)MikMod_malloc(sizeof(AMFHEADER)))) return 0; + if(!(track=(AMFNOTE*)MikMod_calloc(64,sizeof(AMFNOTE)))) return 0; + + return 1; +} + +void AMF_Cleanup(void) +{ + MikMod_free(mh); + MikMod_free(track); +} + +static int AMF_UnpackTrack(MREADER* modreader) +{ + ULONG tracksize; + UBYTE row,cmd; + SBYTE arg; + + /* empty track */ + memset(track,0,64*sizeof(AMFNOTE)); + + /* read packed track */ + if (modreader) { + tracksize=_mm_read_I_UWORD(modreader); + tracksize+=((ULONG)_mm_read_UBYTE(modreader))<<16; + if (tracksize) + while(tracksize--) { + row=_mm_read_UBYTE(modreader); + cmd=_mm_read_UBYTE(modreader); + arg=_mm_read_SBYTE(modreader); + /* unexpected end of track */ + if(!tracksize) { + if((row==0xff)&&(cmd==0xff)&&(arg==-1)) + break; + /* the last triplet should be FF FF FF, but this is not + always the case... maybe a bug in m2amf ? + else + return 0; + */ + + } + /* invalid row (probably unexpected end of row) */ + if (row>=64) + return 0; + if (cmd<0x7f) { + /* note, vol */ + track[row].note=cmd; + track[row].volume=(UBYTE)arg+1; + } else + if (cmd==0x7f) { + /* duplicate row */ + if ((arg<0)&&(row+arg>=0)) { + memcpy(track+row,track+(row+arg),sizeof(AMFNOTE)); + } + } else + if (cmd==0x80) { + /* instr */ + track[row].instr=arg+1; + } else + if (cmd==0x83) { + /* volume without note */ + track[row].volume=(UBYTE)arg+1; + } else + if (cmd==0xff) { + /* apparently, some M2AMF version fail to estimate the + size of the compressed patterns correctly, and end + up with blanks, i.e. dead triplets. Those are marked + with cmd == 0xff. Let's ignore them. */ + } else + if(track[row].fxcnt<3) { + /* effect, param */ + if(cmd>0x97) + return 0; + track[row].effect[track[row].fxcnt]=cmd&0x7f; + track[row].parameter[track[row].fxcnt]=arg; + track[row].fxcnt++; + } else + return 0; + } + } + return 1; +} + +static UBYTE* AMF_ConvertTrack(void) +{ + int row,fx4memory=0; + + /* convert track */ + UniReset(); + for (row=0;row<64;row++) { + if (track[row].instr) UniInstrument(track[row].instr-1); + if (track[row].note>OCTAVE) UniNote(track[row].note-OCTAVE); + + /* AMF effects */ + while(track[row].fxcnt--) { + SBYTE inf=track[row].parameter[track[row].fxcnt]; + + switch(track[row].effect[track[row].fxcnt]) { + case 1: /* Set speed */ + UniEffect(UNI_S3MEFFECTA,inf); + break; + case 2: /* Volume slide */ + if(inf) { + UniWriteByte(UNI_S3MEFFECTD); + if (inf>=0) + UniWriteByte((inf&0xf)<<4); + else + UniWriteByte((-inf)&0xf); + } + break; + /* effect 3, set channel volume, done in UnpackTrack */ + case 4: /* Porta up/down */ + if(inf) { + if(inf>0) { + UniEffect(UNI_S3MEFFECTE,inf); + fx4memory=UNI_S3MEFFECTE; + } else { + UniEffect(UNI_S3MEFFECTF,-inf); + fx4memory=UNI_S3MEFFECTF; + } + } else if(fx4memory) + UniEffect(fx4memory,0); + break; + /* effect 5, "Porta abs", not supported */ + case 6: /* Porta to note */ + UniEffect(UNI_ITEFFECTG,inf); + break; + case 7: /* Tremor */ + UniEffect(UNI_S3MEFFECTI,inf); + break; + case 8: /* Arpeggio */ + UniPTEffect(0x0,inf); + break; + case 9: /* Vibrato */ + UniPTEffect(0x4,inf); + break; + case 0xa: /* Porta + Volume slide */ + UniPTEffect(0x3,0); + if(inf) { + UniWriteByte(UNI_S3MEFFECTD); + if (inf>=0) + UniWriteByte((inf&0xf)<<4); + else + UniWriteByte((-inf)&0xf); + } + break; + case 0xb: /* Vibrato + Volume slide */ + UniPTEffect(0x4,0); + if(inf) { + UniWriteByte(UNI_S3MEFFECTD); + if (inf>=0) + UniWriteByte((inf&0xf)<<4); + else + UniWriteByte((-inf)&0xf); + } + break; + case 0xc: /* Pattern break (in hex) */ + UniPTEffect(0xd,inf); + break; + case 0xd: /* Pattern jump */ + UniPTEffect(0xb,inf); + break; + /* effect 0xe, "Sync", not supported */ + case 0xf: /* Retrig */ + UniEffect(UNI_S3MEFFECTQ,inf&0xf); + break; + case 0x10: /* Sample offset */ + UniPTEffect(0x9,inf); + break; + case 0x11: /* Fine volume slide */ + if(inf) { + UniWriteByte(UNI_S3MEFFECTD); + if (inf>=0) + UniWriteByte((inf&0xf)<<4|0xf); + else + UniWriteByte(0xf0|((-inf)&0xf)); + } + break; + case 0x12: /* Fine portamento */ + if(inf) { + if(inf>0) { + UniEffect(UNI_S3MEFFECTE,0xf0|(inf&0xf)); + fx4memory=UNI_S3MEFFECTE; + } else { + UniEffect(UNI_S3MEFFECTF,0xf0|((-inf)&0xf)); + fx4memory=UNI_S3MEFFECTF; + } + } else if(fx4memory) + UniEffect(fx4memory,0); + break; + case 0x13: /* Delay note */ + UniPTEffect(0xe,0xd0|(inf&0xf)); + break; + case 0x14: /* Note cut */ + UniPTEffect(0xc,0); + track[row].volume=0; + break; + case 0x15: /* Set tempo */ + UniEffect(UNI_S3MEFFECTT,inf); + break; + case 0x16: /* Extra fine portamento */ + if(inf) { + if(inf>0) { + UniEffect(UNI_S3MEFFECTE,0xe0|((inf>>2)&0xf)); + fx4memory=UNI_S3MEFFECTE; + } else { + UniEffect(UNI_S3MEFFECTF,0xe0|(((-inf)>>2)&0xf)); + fx4memory=UNI_S3MEFFECTF; + } + } else if(fx4memory) + UniEffect(fx4memory,0); + break; + case 0x17: /* Panning */ + if (inf>64) + UniEffect(UNI_ITEFFECTS0,0x91); /* surround */ + else + UniPTEffect(0x8,(inf==64)?255:(inf+64)<<1); + of.flags |= UF_PANNING; + break; + } + + } + if (track[row].volume) UniVolEffect(VOL_VOLUME,track[row].volume-1); + UniNewline(); + } + return UniDup(); +} + +int AMF_Load(int curious) +{ + int t,u,realtrackcnt,realsmpcnt,defaultpanning; + AMFSAMPLE s; + SAMPLE *q; + UWORD *track_remap; + ULONG samplepos; + int channel_remap[16]; + + /* try to read module header */ + _mm_read_UBYTES(mh->id,3,modreader); + mh->version =_mm_read_UBYTE(modreader); + _mm_read_string(mh->songname,32,modreader); + mh->numsamples =_mm_read_UBYTE(modreader); + mh->numorders =_mm_read_UBYTE(modreader); + mh->numtracks =_mm_read_I_UWORD(modreader); + mh->numchannels =_mm_read_UBYTE(modreader); + if((!mh->numchannels)||(mh->numchannels>(mh->version>=12?32:16))) { + _mm_errno=MMERR_NOT_A_MODULE; + return 0; + } + + if(mh->version>=11) { + memset(mh->panpos,0,32); + _mm_read_SBYTES(mh->panpos,(mh->version>=13)?32:16,modreader); + } else + _mm_read_UBYTES(channel_remap,16,modreader); + + if (mh->version>=13) { + mh->songbpm=_mm_read_UBYTE(modreader); + if(mh->songbpm<32) { + _mm_errno=MMERR_NOT_A_MODULE; + return 0; + } + mh->songspd=_mm_read_UBYTE(modreader); + if(mh->songspd>32) { + _mm_errno=MMERR_NOT_A_MODULE; + return 0; + } + } else { + mh->songbpm=125; + mh->songspd=6; + } + + if(_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + /* set module variables */ + of.initspeed = mh->songspd; + of.inittempo = mh->songbpm; + AMF_Version[AMFTEXTLEN-3]='0'+(mh->version/10); + AMF_Version[AMFTEXTLEN-1]='0'+(mh->version%10); + of.modtype = StrDup(AMF_Version); + of.numchn = mh->numchannels; + of.numtrk = mh->numorders*mh->numchannels; + if (mh->numtracks>of.numtrk) + of.numtrk=mh->numtracks; + of.numtrk++; /* add room for extra, empty track */ + of.songname = DupStr(mh->songname,32,1); + of.numpos = mh->numorders; + of.numpat = mh->numorders; + of.reppos = 0; + of.flags |= UF_S3MSLIDES; + /* XXX whenever possible, we should try to determine the original format. + Here we assume it was S3M-style wrt bpmlimit... */ + of.bpmlimit = 32; + + /* + * Play with the panning table. Although the AMF format embeds a + * panning table, if the module was a MOD or an S3M with default + * panning and didn't use any panning commands, don't flag + * UF_PANNING, to use our preferred panning table for this case. + */ + defaultpanning = 1; + for (t = 0; t < 32; t++) { + if (mh->panpos[t] > 64) { + of.panning[t] = PAN_SURROUND; + defaultpanning = 0; + } else + if (mh->panpos[t] == 64) + of.panning[t] = PAN_RIGHT; + else + of.panning[t] = (mh->panpos[t] + 64) << 1; + } + if (defaultpanning) { + for (t = 0; t < of.numchn; t++) + if (of.panning[t] == (((t + 1) & 2) ? PAN_RIGHT : PAN_LEFT)) { + defaultpanning = 0; /* not MOD canonical panning */ + break; + } + } + if (defaultpanning) + of.flags |= UF_PANNING; + + of.numins=of.numsmp=mh->numsamples; + + if(!AllocPositions(of.numpos)) return 0; + for(t=0;tversion>=14) + /* track size */ + of.pattrows[t]=_mm_read_I_UWORD(modreader); + if (mh->version>=10) + _mm_read_I_UWORDS(of.patterns+(t*of.numchn),of.numchn,modreader); + else + for(u=0;uversion>=11) { + s.reppos =_mm_read_I_ULONG(modreader); + s.repend =_mm_read_I_ULONG(modreader); + } else { + s.reppos =_mm_read_I_UWORD(modreader); + s.repend =s.length; + } + + if(_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_SAMPLEINFO; + return 0; + } + + q->samplename = DupStr(s.samplename,32,1); + q->speed = s.c2spd; + q->volume = s.volume; + if (s.type) { + q->seekpos = s.offset; + q->length = s.length; + q->loopstart = s.reppos; + q->loopend = s.repend; + if((s.repend-s.reppos)>2) q->flags |= SF_LOOP; + } + q++; + } + + /* read track table */ + if(!(track_remap=MikMod_calloc(mh->numtracks+1,sizeof(UWORD)))) + return 0; + _mm_read_I_UWORDS(track_remap+1,mh->numtracks,modreader); + if(_mm_eof(modreader)) { + MikMod_free(track_remap); + _mm_errno=MMERR_LOADING_TRACK; + return 0; + } + + for(realtrackcnt=t=0;t<=mh->numtracks;t++) + if (realtrackcntnumtracks)? + track_remap[of.patterns[t]]-1:realtrackcnt; + + MikMod_free(track_remap); + + /* unpack tracks */ + for(t=0;tseekpos!=t) q++; + q->seekpos=samplepos; + samplepos+=q->length; + } + + return 1; +} + +CHAR *AMF_LoadTitle(void) +{ + CHAR s[32]; + + _mm_fseek(modreader,4,SEEK_SET); + if(!_mm_read_UBYTES(s,32,modreader)) return NULL; + + return(DupStr(s,32,1)); +} + +/*========== Loader information */ + +MIKMODAPI MLOADER load_amf={ + NULL, + "AMF", + "AMF (DSMI Advanced Module Format)", + AMF_Init, + AMF_Test, + AMF_Load, + AMF_Cleanup, + AMF_LoadTitle +}; + +/* ex:set ts=4: */ -- cgit v1.2.3