Mercurial > ~darius > hgwebdir.cgi > mikmod
changeset 4:5d614bcc4287
Initial entry of mikmod into the CVS tree.
author | darius |
---|---|
date | Fri, 23 Jan 1998 16:05:08 +0000 |
parents | 71e20a32bd84 |
children | 42e11dc15457 |
files | docs/bugs.txt docs/future.txt docs/mikcvt.txt docs/mikmod.doc docs/tips.txt playercode/drv_nos.c playercode/drv_wav.c playercode/load_669.c playercode/load_dsm.c playercode/load_far.c playercode/load_it.c playercode/load_m15.c playercode/load_med.c playercode/load_mod.c playercode/load_mtm.c playercode/load_s3m.c playercode/load_stm.c playercode/load_ult.c playercode/load_uni.c playercode/load_wav.c playercode/load_xm.c playercode/mloader.c playercode/stream.c playercode/virtch2.c |
diffstat | 24 files changed, 7301 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/bugs.txt Fri Jan 23 16:05:08 1998 +0000 @@ -0,0 +1,25 @@ + + --> MikMod Sound Libraries Version 3.0 + -> Known Bugs - August 20st, 1997 + + + If anyone knows of a fix for any of the bugs listed below, please contact + me at dracoirs@epix.net and tell me about it. + + - The DSM loader has been changed from MikMod 2.10 to 3.0 and is un- + tested. It may not work. + + - 669 loader is far from perfect. Thanks go out to the complete lack of + useful docs for this format. It seems to play most 669's adequately, + however. + + - Since there is no 100% sure detection of UST / Soundtracker the 15- + instrument loader may, under very rare circumstances, detect a sound- + tracker module as UST and play it incorrectly. + + - MikMod locks Aztec soundcards (SB clones distributed with Packard Bell + computers). MikMod will work, but it hard locks the card and the + user must turn off the computer to reset it (Windows can't even do it!) + + This bug may not exist anymore; someone tell me if it does. +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/future.txt Fri Jan 23 16:05:08 1998 +0000 @@ -0,0 +1,26 @@ + + --> MikMod Sound Libraries Version 3.0 + -> Changes Planned for the Future - August 20th, 1997 + + + - Optional Interpolated mixing in VIRTCH.C [real-time]. + + - Floating point alternative to 64-bit ints in VIRTCH2.C. + + - Support for stereo samples and sound effects. + + - Audio streaming from disk for large raw audio files (WAV, etc). + + - Native Gravis Ultrasound Plug and Play soundcard support. + + - Better AWE32 support (AWE32 does not support software sound-fx + mixing). + + - Native AWE64 support [does anyone have GOOD specs on this or the + AWE32?] + + - Windows 95 and DirectSound [ugh] Drivers. + + - Things people suggest - send suggestions to dracoirs@epix.net and + don't forget to visit the webpage at http://www.epix.net/~dracoirs/ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/mikcvt.txt Fri Jan 23 16:05:08 1998 +0000 @@ -0,0 +1,76 @@ + + --> MikMod Sound System v3.0 + -> UniFormat Conversion Program (MIKCVT.EXE) + + + +-/- Introduction +================ + + MikMod utilizes its own internal format called UniMod (or UniFormat). + This internal format can 100% accurately represent any of the modules + that MikMod supports. The advantage of UniMod is that the programmer + can convert all modules of any type to this native MikMod format, and + simply include the UniMod loader alone into the program. + + For more information about UniMod, see UNIMOD30.TXT. + + MikMod comes with a program called MIKCVT.EXE, which converts modules + to the UniMod format. MIKCVT will load any module supported by + MikMod, optimize and compress the pattern data, and save out a file + with the .UNI extension. MIKCVT also supports wildcards, so that you + can easily convert several modules at a time. + + +-/- Distribution +================ + + MIKCVT is a totally free utility, and can be downloaded separately + from MikMod. MIKCVT comes with full source code, and the programmer + can feel free to modify this source code in any way for PERSONAL use + only (to rearrange the format to prevent module ripping for instance). + PLEASE DO NOT REDISTRIBUTE A MODIFIED VERSION OF MIKCVT. The last + thing anyone wants is to have odd non-std versions of UniMod modules + floating around. + + + +-/- Usage +========= + + MIKCVT has a very simple command line interface, as follows: + + MIKCVT [-strip] infile [infile] [infile] ... + + MIKCVT song1.s3m song2.xm + MIKCVT -strip *.s3m + MIKCVT *.* + + The first example shows the general syntax of MIKCVT. Note that + -strip is optional, but if present MUST be the first option on + the command line. + + In any instance, the -strip is optional, and if present will tell + MIKCVT to remove all text strings from the module. This includes + the title, sample names, instrument names, comments and other forms + of text. This feature is generally intended for use by game and demo + developers, who may not require such information in the release pro- + duct. + + +-/- Miscellaneous +================= + + There will probably be a future version of MIKCVT that will further + optimize modules in -strip mode. + + For information, questions, comments, or various chit-chat, contact + Jake Stine / Divine Entertainment at jstine@hotmail.com. + + For updates and other updated information, check out the Divine Ent- + ertainment website at http://www.epix.net/~dracoirs/ + + + That's all for today. Have fun! + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/tips.txt Fri Jan 23 16:05:08 1998 +0000 @@ -0,0 +1,64 @@ + + --> MikMod 3.0 + -> Various Programming Tips and Tricks! + + + Here are some tips and tricks that I have jotted down as I think of them. + The list is short for now, but it will no doubt grow as more are thought + up. Also, check for additional information on the Divine Entertainment + website at http://www.epix.net/~dracoirs/ + + Ok, so this little text file is pretty short right now. These things + take time to think of.. :-) + + +-/- Compiling / Makefiles + + Watcom: + + The example files that come with MikMod default to 30k stack buffers + (option stack=30000 on the wlink command line). This is a safety pre- + caution and by no means a requirement. + + You can usually safely use a 10k stack if you are using an interrupt + driven MikMod_Update() and you can safely use a 5k stack if you are + using polling (calling MikMod_Update() from a main program loop). + + ---------- + + If you are writing a module player and are NOT calling MikMod_Update() + from an interrupt, then you can safely remove /ZU from the following + modules in the /MikMod directory: + + MDRIVER.C + VIRTCH.C + + Removing /ZU will slightly reduce code size and increase replay speed. + + ---------- + + If you are using the IDE and want to add PMODE/W to the list of valid + targets, follow these steps: + + a) copy PMWBIND.EXE and PMODEW.LNK to your watcom/binw diectory. + + b) Load WSYSTEM.LNK, and inster the contents of PMODEW.LINK into it + at the very top. + + c) Load IDEDOS32.CFG. Insert a line that reads: + Target *.exe, dw2e_, "PMODE/W Executable" + + d) Load IDE.CFG. Around line 840, you will find the following line: + VSwitch 0, dr2??, "System:", SYS, " ", ONE, REQ, dos4g + Inser this line below it: + VSwitch 0, dw2??, "System:", SYS, " ", ONE, REQ, pmodew + + e) This is the tricky part. There are several lines in IDE.CFG that + contain 'dr2'. Each of these [excluding the special case in step + d] must be duplicated, and 'dr2' replaced with 'dw2' (no other + changes required). Do this right and you will have full IDE cap- + ability with the pmode/w extender! + + There you go, all set to compile with pmode/w within the Watcom IDE! + + ----------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/playercode/drv_nos.c Fri Jan 23 16:05:08 1998 +0000 @@ -0,0 +1,160 @@ +/* + +Name: +DRV_NOS.C + +Description: +Mikmod driver for no output on any soundcard, monitor, keyboard, or whatever :) + +Portability: +All systems - All compilers + +*/ + +#include "mikmod.h" + + +static BOOL NS_IsThere(void) +{ + return 1; +} + + +static SWORD NS_SampleLoad(SAMPLOAD *s, int type, FILE *fp) +{ + return 0; +} + + +static void NS_SampleUnload(SWORD h) +{ +} + + +static ULONG NS_SampleSpace(int type) +{ + return 0; +} + + +static ULONG NS_SampleLength(int type, SAMPLE *s) +{ + return s->length; +} + + +static BOOL NS_Init(void) +{ + return 0; +} + + +static void NS_Exit(void) +{ +} + + +static BOOL NS_Reset(void) +{ + return 0; +} + + +static BOOL NS_PlayStart(void) +{ +} + + +static void NS_PlayStop(void) +{ +} + + +static void NS_Update(void) +{ +} + + +static BOOL NS_SetNumVoices(void) +{ + return 0; +} + + +static void NS_VoiceSetVolume(UBYTE voice,UWORD vol) +{ +} + + +static void NS_VoiceSetFrequency(UBYTE voice,ULONG frq) +{ +} + + +static void NS_VoiceSetPanning(UBYTE voice,ULONG pan) +{ +} + + +static void NS_VoicePlay(UBYTE voice,SWORD handle,ULONG start,ULONG size,ULONG reppos,ULONG repend,UWORD flags) +{ +} + + +static void NS_VoiceStop(UBYTE voice) +{ +} + + +static BOOL NS_VoiceStopped(UBYTE voice) +{ + return 0; +} + + +static void NS_VoiceReleaseSustain(UBYTE voice) +{ +} + + +static SLONG NS_VoiceGetPosition(UBYTE voice) +{ + return 0; +} + + +static ULONG NS_VoiceRealVolume(UBYTE voice) +{ + return 0; +} + + + +MDRIVER drv_nos = +{ NULL, + "No Sound", + "Nosound Driver v2.0 - (c) Creative Silence", + 255,255, + NS_IsThere, + NS_SampleLoad, + NS_SampleUnload, + NS_SampleSpace, + NS_SampleLength, + NS_Init, + NS_Exit, + NS_Reset, + NS_SetNumVoices, + NS_PlayStart, + NS_PlayStop, + NS_Update, + NS_VoiceSetVolume, + NS_VoiceSetFrequency, + NS_VoiceSetPanning, + NS_VoicePlay, + NS_VoiceStop, + NS_VoiceStopped, + NS_VoiceReleaseSustain, + NS_VoiceGetPosition, + NS_VoiceRealVolume +}; +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/playercode/drv_wav.c Fri Jan 23 16:05:08 1998 +0000 @@ -0,0 +1,116 @@ + +#include "mikmod.h" + +#ifdef __GNUC__ +#include <sys/types.h> +#else +#include <io.h> +#endif +#include <sys/stat.h> +#include <fcntl.h> + +#define WAVBUFFERSIZE 65536 + +static FILE *wavout; + +static SBYTE *WAV_DMABUF; +static ULONG dumpsize; + +static BOOL WAV_IsThere(void) +{ + return 1; +} + + +static BOOL WAV_Init(void) +{ + if(NULL == (wavout = fopen("music.wav", "wb"))) return 1; + if(NULL == (WAV_DMABUF = _mm_malloc(WAVBUFFERSIZE))) return 1; + + md_mode |= DMODE_SOFT_MUSIC | DMODE_SOFT_SNDFX; + + if(VC_Init()) return 1; + + _mm_write_string("RIFF WAVEfmt ",wavout); + _mm_write_I_ULONG(16,wavout); // length of this RIFF block crap + + _mm_write_I_UWORD(1, wavout); // microsoft format type + _mm_write_I_UWORD((md_mode & DMODE_STEREO) ? 2 : 1, wavout); + _mm_write_I_ULONG(md_mixfreq, wavout); + _mm_write_I_ULONG(md_mixfreq * ((md_mode & DMODE_STEREO) ? 2 : 1) * + ((md_mode & DMODE_16BITS) ? 2 : 1), wavout); + + _mm_write_I_UWORD(((md_mode & DMODE_16BITS) ? 2 : 1) * + ((md_mode & DMODE_STEREO) ? 2 : 1), wavout); // block alignment (8/16 bit) + + _mm_write_I_UWORD((md_mode & DMODE_16BITS) ? 16 : 8,wavout); + + _mm_write_string("data",wavout); + + dumpsize = 0; + + return 0; +} + + +static void WAV_Exit(void) +{ + VC_Exit(); + + // write in the actual sizes now + + if(wavout!=NULL) + { _mm_fseek(wavout,4,SEEK_SET); + _mm_write_I_ULONG(dumpsize + 32, wavout); + _mm_fseek(wavout,40,SEEK_SET); + _mm_write_I_ULONG(dumpsize, wavout); + + fclose(wavout); + + if(WAV_DMABUF != NULL) free(WAV_DMABUF); + } +} + + +static void WAV_Update(void) +{ + VC_WriteBytes(WAV_DMABUF, WAVBUFFERSIZE); + fwrite(WAV_DMABUF, 1, WAVBUFFERSIZE, wavout); + dumpsize += WAVBUFFERSIZE; +} + + +static BOOL WAV_Reset(void) +{ + return 0; +} + + +MDRIVER drv_wav = +{ NULL, + "music.wav file", + "WAV [music.wav] file output driver v1.0", + 0,255, + WAV_IsThere, + VC_SampleLoad, + VC_SampleUnload, + VC_SampleSpace, + VC_SampleLength, + WAV_Init, + WAV_Exit, + WAV_Reset, + VC_SetNumVoices, + VC_PlayStart, + VC_PlayStop, + WAV_Update, + VC_VoiceSetVolume, + VC_VoiceSetFrequency, + VC_VoiceSetPanning, + VC_VoicePlay, + VC_VoiceStop, + VC_VoiceStopped, + VC_VoiceReleaseSustain, + VC_VoiceGetPosition, + NULL +}; +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/playercode/load_669.c Fri Jan 23 16:05:08 1998 +0000 @@ -0,0 +1,255 @@ +/* + + Name: LOAD_669.C + + Description: + Tran's 669 module loader + + Portability: + All systems - all compilers (hopefully) + + If this module is found to not be portable to any particular platform, + please contact Jake Stine at dracoirs@epix.net (see MIKMOD.TXT for + more information on contacting the author). + +*/ + +#include <string.h> +#include "mikmod.h" + + +// Raw 669 header struct: + +typedef struct S69HEADER +{ UBYTE marker[2]; + CHAR message[108]; + UBYTE nos; + UBYTE nop; + UBYTE looporder; + UBYTE orders[0x80]; + UBYTE tempos[0x80]; + UBYTE breaks[0x80]; +} S69HEADER; + + +// Raw 669 sampleinfo struct: + +typedef struct S69SAMPLE +{ CHAR filename[13]; + SLONG length; + SLONG loopbeg; + SLONG loopend; +} S69SAMPLE; + + +// Raw 669 Note struct + +typedef struct S69NOTE +{ UBYTE a,b,c; +} S69NOTE; + + +static S69NOTE *s69pat = NULL; +static S69HEADER *mh = NULL; + +static CHAR *S69_Version[] = +{ "669", + "Extended 669" +}; + + +BOOL S69_Test(void) +{ + UBYTE id[2]; + + if(!_mm_read_UBYTES(id,2,modfp)) return 0; + if(!memcmp(id,"if",2) || !memcmp(id,"JN",2)) + { _mm_fseek(modfp,108,SEEK_CUR); + if(_mm_read_UBYTE(modfp) > 64) return 0; + if(_mm_read_UBYTE(modfp) > 128) return 0; + if(_mm_read_UBYTE(modfp) > 120) return 0; + return 1; + } + return 0; +} + + +BOOL S69_Init(void) +{ + if(!(s69pat=(S69NOTE *)_mm_malloc(64*8*sizeof(S69NOTE)))) return 0; + if(!(mh=(S69HEADER *)_mm_calloc(1,sizeof(S69HEADER)))) return 0; + return 1; +} + + +void S69_Cleanup(void) +{ + if(s69pat!=NULL) free(s69pat); + if(mh!=NULL) free(mh); + + mh = NULL; + s69pat = NULL; +} + + +BOOL S69_LoadPatterns(void) +{ + int t,s,q,tracks=0,t2,t3; + UBYTE note,inst,vol,a,b,c, lo; + S69NOTE *cur; + + if(!AllocPatterns()) return 0; + if(!AllocTracks()) return 0; + + for(t=0; t<of.numpat; t++) + { of.pattrows[t] = mh->breaks[t]+1; + + // Load the pattern into the temp buffer + // and convert it into the 3-byte format + + cur = s69pat; + for(t2=64; t2; t2--) + { for(t3=8; t3; t3--, cur++) + { cur->a = _mm_read_UBYTE(modfp); + cur->b = _mm_read_UBYTE(modfp); + cur->c = _mm_read_UBYTE(modfp); + } + } + + if(feof(modfp)) + { _mm_errno = MMERR_LOADING_PATTERN; + return 0; + } + + for(s=0; s<8; s++) + { UniReset(); + UniPTEffect(0xf,75); // was 78 + UniPTEffect(0xf,3); + + for(q=0; q<64; q++) + { a = s69pat[(q*8)+s].a; + b = s69pat[(q*8)+s].b; + c = s69pat[(q*8)+s].c; + + note = a >> 2; + inst = ((a & 0x3) << 4) | ((b & 0xf0) >> 4); + vol = b & 0xf; + + if(note < 0x3e) + { UniInstrument(inst); + UniNote(note+24); + } + + if(note < 0x3f) UniPTEffect(0xc,vol<<2); + + lo = c & 0xf; + switch(c >> 4) + { case 0: + UniPTEffect(0x1,lo); + break; + + case 1: + UniPTEffect(0x2,lo); + break; + + case 2: + UniPTEffect(0x3,lo); + break; + + case 4: + UniPTEffect(0x4,lo); + break; + } + UniNewline(); + } + if(!(of.tracks[tracks++]=UniDup())) return 0; + } + } + return 1; +} + + +BOOL S69_Load(void) +{ + int t; + S69SAMPLE s; + SAMPLE *q; + + // try to read module header + + _mm_read_UBYTES(mh->marker,2,modfp); + _mm_read_UBYTES((UBYTE *)mh->message,108,modfp); + mh->nos = _mm_read_UBYTE(modfp); + mh->nop = _mm_read_UBYTE(modfp); + mh->looporder = _mm_read_UBYTE(modfp); + _mm_read_UBYTES(mh->orders,0x80,modfp); + _mm_read_UBYTES(mh->tempos,0x80,modfp); + _mm_read_UBYTES(mh->breaks,0x80,modfp); + + // set module variables + + of.initspeed = 6; + of.inittempo = 125; + of.songname = DupStr(mh->message,108); + of.modtype = strdup(S69_Version[memcmp(mh->marker,"JN",2)==0]); + of.numchn = 8; + of.numpat = mh->nop; + of.numins = of.numsmp = mh->nos; + of.numtrk = of.numchn*of.numpat; + of.flags = UF_XMPERIODS; // | UF_LINEAR; + + if(!AllocPositions(0x80)) return 0; + for(t=0; t<0x80; t++) + { if(mh->orders[t]==0xff) break; + of.positions[t] = mh->orders[t]; + } + + of.numpos = t; + + if(!AllocSamples()) return 0; + q = of.samples; + + for(t=0; t<of.numins; t++) + { // try to read sample info + + _mm_read_UBYTES((UBYTE *)s.filename,13,modfp); + s.length = _mm_read_I_SLONG(modfp); + s.loopbeg = _mm_read_I_SLONG(modfp); + s.loopend = _mm_read_I_SLONG(modfp); + + if((s.length < 0) || (s.loopbeg < -1) || (s.loopend < -1)) + { _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + q->samplename = DupStr(s.filename,13); + + q->seekpos = 0; + q->speed = 0; + q->length = s.length; + q->loopstart = s.loopbeg; + q->loopend = (s.loopend<s.length) ? s.loopend : s.length; + q->flags = (s.loopbeg<s.loopend) ? SF_LOOP : 0; + q->volume = 64; + + q++; + } + + if(!S69_LoadPatterns()) return 0; + + return 1; +} + + +MLOADER load_669 = +{ NULL, + "669", + "Portable 669 loader v0.1", + S69_Init, + S69_Test, + S69_Load, + S69_Cleanup, + NULL +}; + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/playercode/load_dsm.c Fri Jan 23 16:05:08 1998 +0000 @@ -0,0 +1,314 @@ +/* + + Name: LOAD_DSM.C + + Description: + DSIK Internal Format (DSM) module loader + + Portability: + All systems - all compilers (hopefully) + + If this module is found to not be portable to any particular platform, + please contact Jake Stine at dracoirs@epix.net (see MIKMOD.TXT for + more information on contacting the author). +*/ + +#include <string.h> +#include "mikmod.h" + + +typedef struct DSMNOTE +{ UBYTE note,ins,vol,cmd,inf; +} DSMNOTE; + + +typedef struct DSMINST +{ CHAR filename[13]; + UWORD flags; + UBYTE volume; + ULONG length; + ULONG loopstart; + ULONG loopend; + ULONG reserved1; + UWORD c2spd; + UWORD reserved2; + CHAR samplename[28]; +} DSMINST; + + +typedef struct DSMSONG +{ CHAR songname[28]; + UWORD reserved1; + UWORD flags; + ULONG reserved2; + UWORD numord; + UWORD numsmp; + UWORD numpat; + UWORD numtrk; + UBYTE globalvol; + UBYTE mastervol; + UBYTE speed; + UBYTE bpm; + UBYTE panpos[16]; + UBYTE orders[128]; +} DSMSONG; + + + +static CHAR *SONGID = "SONG"; +static CHAR *INSTID = "INST"; +static CHAR *PATTID = "PATT"; + + +static UBYTE blockid[4]; +static ULONG blockln; +static ULONG blocklp; +static DSMSONG *mh = NULL; +static DSMNOTE *dsmbuf = NULL; + +static CHAR DSM_Version[] = "DSIK DSM-format"; + + +BOOL DSM_Test(void) +{ + UBYTE id[12]; + + if(_mm_read_UBYTES((UBYTE *)id,12,modfp)) return 0; + if(!memcmp(id,"RIFF",4) && !memcmp(&id[8],"DSMF",4)) return 1; + + return 0; +} + + +BOOL DSM_Init(void) +{ + if(!(dsmbuf=(DSMNOTE *)_mm_malloc(16*64*sizeof(DSMNOTE)))) return 0; + if(!(mh=(DSMSONG *)_mm_calloc(1,sizeof(DSMSONG)))) return 0; + return 1; +} + + +void DSM_Cleanup(void) +{ + if(dsmbuf!=NULL) free(dsmbuf); + if(mh!=NULL) free(mh); + + dsmbuf = NULL; + mh = NULL; +} + + +BOOL GetBlockHeader(void) +{ + // make sure we're at the right position for reading the + // next riff block, no matter how many bytes read + + _mm_fseek(modfp, blocklp+blockln, SEEK_SET); + + while(1) + { _mm_read_UBYTES(blockid,4,modfp); + blockln = _mm_read_I_ULONG(modfp); + if(feof(modfp)) + { _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + if(memcmp(blockid,SONGID,4) && memcmp(blockid,INSTID,4) && memcmp(blockid,PATTID,4)) + { //printf("Skipping unknown block type %4.4s\n",&blockid); + _mm_fseek(modfp, blockln, SEEK_CUR); + } else break; + } + + blocklp = _mm_ftell(modfp); + return 1; +} + + +BOOL DSM_ReadPattern(void) +{ + int row=0,flag; + DSMNOTE *n; + + // clear pattern data + memset(dsmbuf,255,16*64*sizeof(DSMNOTE)); + _mm_read_UBYTE(modfp); + _mm_read_UBYTE(modfp); + + while(row<64) + { flag = _mm_read_UBYTE(modfp); + if(feof(modfp)) + { _mm_errno = MMERR_LOADING_PATTERN; + return 0; + } + + if(flag) + { n = &dsmbuf[((flag&0xf)*64)+row]; + if(flag&0x80) n->note = _mm_read_UBYTE(modfp); + if(flag&0x40) n->ins = _mm_read_UBYTE(modfp); + if(flag&0x20) n->vol = _mm_read_UBYTE(modfp); + if(flag&0x10) + { n->cmd = _mm_read_UBYTE(modfp); + n->inf = _mm_read_UBYTE(modfp); + } + } else row++; + } + return 1; +} + + +UBYTE *DSM_ConvertTrack(DSMNOTE *tr) +{ + int t; + UBYTE note,ins,vol,cmd,inf; + + UniReset(); + for(t=0; t<64; t++) + { note = tr[t].note; + ins = tr[t].ins; + vol = tr[t].vol; + cmd = tr[t].cmd; + inf = tr[t].inf; + + if(ins!=0 && ins!=255) UniInstrument(ins-1); + if(note!=255) UniNote(note-1); // <- normal note + if(vol<65) UniPTEffect(0xc,vol); + + if(cmd!=255) + { if(cmd==0x8) + { if(inf<=0x80) + { inf = (inf<0x80) ? inf<<1 : 255; + UniPTEffect(cmd,inf); + } + } else if(cmd==0xb) + { if(inf<=0x7f) UniPTEffect(cmd,inf); + } else + { // Convert pattern jump from Dec to Hex + if(cmd == 0xd) + inf = (((inf&0xf0)>>4)*10)+(inf&0xf); + UniPTEffect(cmd,inf); + } + } + UniNewline(); + } + return UniDup(); +} + + +BOOL DSM_Load(void) +{ + int t; + DSMINST s; + SAMPLE *q; + int cursmp = 0, curpat = 0, track = 0; + + blocklp = 0; + blockln = 12; + + if(!GetBlockHeader()) return 0; + if(memcmp(blockid,SONGID,4)) + { _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + _mm_read_UBYTES(mh->songname,28,modfp); + mh->reserved1 = _mm_read_I_UWORD(modfp); + mh->flags = _mm_read_I_UWORD(modfp); + mh->reserved2 = _mm_read_I_ULONG(modfp); + mh->numord = _mm_read_I_UWORD(modfp); + mh->numsmp = _mm_read_I_UWORD(modfp); + mh->numpat = _mm_read_I_UWORD(modfp); + mh->numtrk = _mm_read_I_UWORD(modfp); + mh->globalvol = _mm_read_UBYTE(modfp); + mh->mastervol = _mm_read_UBYTE(modfp); + mh->speed = _mm_read_UBYTE(modfp); + mh->bpm = _mm_read_UBYTE(modfp); + _mm_read_UBYTES(mh->panpos,16,modfp); + _mm_read_UBYTES(mh->orders,128,modfp); + + // set module variables + of.initspeed = mh->speed; + of.inittempo = mh->bpm; + of.modtype = strdup(DSM_Version); + of.numchn = mh->numtrk; + of.numpat = mh->numpat; + of.numtrk = of.numchn*of.numpat; + of.songname = DupStr(mh->songname,28); // make a cstr of songname + + for(t=0; t<16; t++) + of.panning[t] = mh->panpos[t]<0x80 ? (mh->panpos[t]<<1) : 255; + + if(!AllocPositions(mh->numord)) return 0; + of.numpos = 0; + for(t=0; t<mh->numord; t++) + { of.positions[of.numpos] = mh->orders[t]; + if(mh->orders[t]<254) of.numpos++; + } + + of.numins = of.numsmp = mh->numsmp; + + if(!AllocSamples()) return 0; + if(!AllocTracks()) return 0; + if(!AllocPatterns()) return 0; + + while(cursmp<of.numins || curpat<of.numpat) + { if(!GetBlockHeader()) return 0; + if(!memcmp(blockid,INSTID,4) && cursmp<of.numins) + { q = &of.samples[cursmp]; + + // try to read sample info + _mm_read_UBYTES(s.filename,13,modfp); + s.flags = _mm_read_I_UWORD(modfp); + s.volume = _mm_read_UBYTE(modfp); + s.length = _mm_read_I_ULONG(modfp); + s.loopstart = _mm_read_I_ULONG(modfp); + s.loopend = _mm_read_I_ULONG(modfp); + s.reserved1 = _mm_read_I_ULONG(modfp); + s.c2spd = _mm_read_I_UWORD(modfp); + s.reserved2 = _mm_read_I_UWORD(modfp); + _mm_read_UBYTES(s.samplename,28,modfp); + + q->samplename= DupStr(s.samplename,28); + q->seekpos = _mm_ftell(modfp); + q->speed = s.c2spd; + q->length = s.length; + q->loopstart = s.loopstart; + q->loopend = s.loopend; + q->volume = s.volume; + + if(s.flags&1) q->flags|=SF_LOOP; + if(s.flags&2) q->flags|=SF_SIGNED; + cursmp++; + } else if(!memcmp(blockid,PATTID,4) && curpat<of.numpat) + { DSM_ReadPattern(); + for(t=0; t<of.numchn; t++) + if(!(of.tracks[track++] = DSM_ConvertTrack(&dsmbuf[t*64]))) return 0; + curpat++; + } + } + + return 1; +} + + +CHAR *DSM_LoadTitle(void) +{ + CHAR s[28]; + + _mm_fseek(modfp,12,SEEK_SET); + if(!fread(s,28,1,modfp)) return NULL; + + return(DupStr(s,28)); +} + + +MLOADER load_dsm = +{ NULL, + "DSM", + "Portable DSM loader v0.1", + DSM_Init, + DSM_Test, + DSM_Load, + DSM_Cleanup, + DSM_LoadTitle +}; +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/playercode/load_far.c Fri Jan 23 16:05:08 1998 +0000 @@ -0,0 +1,288 @@ +/* + + Name: LOAD_FAR.C + + Description: + Farandole (FAR) module loader + + Portability: + All systems - all compilers (hopefully) + + If this module is found to not be portable to any particular platform, + please contact Jake Stine at dracoirs@epix.net (see MIKMOD.TXT for + more information on contacting the author). +*/ + +#include <string.h> +#include "mikmod.h" + + +typedef struct FARSAMPLE +{ CHAR samplename[32]; + ULONG length; + UBYTE finetune; + UBYTE volume; + ULONG reppos; + ULONG repend; + UBYTE type; + UBYTE loop; +} FARSAMPLE; + + + +typedef struct FARHEADER1 +{ UBYTE id[4]; // file magic + CHAR songname[40]; // songname + CHAR blah[3]; // 13,10,26 + UWORD headerlen; // remaining length of header in bytes + UBYTE version; + UBYTE onoff[16]; + UBYTE edit1[9]; + UBYTE speed; + UBYTE panning[16]; + UBYTE edit2[4]; + UWORD stlen; +} FARHEADER1; + + +typedef struct FARHEADER2 +{ UBYTE orders[256]; + UBYTE numpat; + UBYTE snglen; + UBYTE loopto; + UWORD patsiz[256]; +} FARHEADER2; + + +typedef struct FARNOTE +{ UBYTE note,ins,vol,eff; +} FARNOTE; + + + +static CHAR FAR_Version[] = "Farandole"; +static FARHEADER1 *mh1 = NULL; +static FARHEADER2 *mh2 = NULL; +static FARNOTE *pat = NULL; + + +BOOL FAR_Test(void) +{ + UBYTE id[4]; + + if(!_mm_read_UBYTES(id,4,modfp)) return 0; + return(!memcmp(id,"FAR=",4)); +} + + +BOOL FAR_Init(void) +{ + if(!(mh1 = (FARHEADER1 *)_mm_malloc(sizeof(FARHEADER1)))) return 0; + if(!(mh2 = (FARHEADER2 *)_mm_malloc(sizeof(FARHEADER2)))) return 0; + if(!(pat = (FARNOTE *)_mm_malloc(16*256*sizeof(FARNOTE)))) return 0; + + return 1; +} + + +void FAR_Cleanup(void) +{ + if(mh1!=NULL) free(mh1); + if(mh2!=NULL) free(mh2); + if(pat!=NULL) free(pat); + + mh1 = NULL; + mh2 = NULL; + pat = NULL; +} + + +UBYTE *FAR_ConvertTrack(FARNOTE *n,int rows) +{ + int t; + + UniReset(); + + for(t=0; t<rows; t++) + { if(n->note) + { UniInstrument(n->ins); + UniNote(n->note+23+12); + } + + if(n->vol&0xf) UniPTEffect(0xc,(n->vol&0xf)<<2); + switch(n->eff>>4) + { case 0xf: + UniPTEffect(0xf,n->eff&0xf); + break; + + // others not yet implemented + } + + UniNewline(); + n+=16; + } + return UniDup(); +} + + +BOOL FAR_Load(void) +{ + int t,u,tracks=0; + SAMPLE *q; + FARSAMPLE s; + FARNOTE *crow; + UBYTE smap[8]; + + // try to read module header (first part) + _mm_read_UBYTES(mh1->id,4,modfp); + _mm_read_SBYTES(mh1->songname,40,modfp); + _mm_read_SBYTES(mh1->blah,3,modfp); + mh1->headerlen = _mm_read_I_UWORD (modfp); + mh1->version = _mm_read_UBYTE (modfp); + _mm_read_UBYTES(mh1->onoff,16,modfp); + _mm_read_UBYTES(mh1->edit1,9,modfp); + mh1->speed = _mm_read_UBYTE(modfp); + _mm_read_UBYTES(mh1->panning,16,modfp); + _mm_read_UBYTES(mh1->edit2,4,modfp); + mh1->stlen = _mm_read_I_UWORD (modfp); + + + // init modfile data + + of.modtype = strdup(FAR_Version); + of.songname = DupStr(mh1->songname,40); + of.numchn = 16; + of.initspeed = mh1->speed; + of.inittempo = 99; + + for(t=0; t<16; t++) of.panning[t] = mh1->panning[t]<<4; + + // read songtext into comment field + if(!ReadComment(mh1->stlen)) return 0; + + // try to read module header (second part) + _mm_read_UBYTES(mh2->orders,256,modfp); + mh2->numpat = _mm_read_UBYTE(modfp); + mh2->snglen = _mm_read_UBYTE(modfp); + mh2->loopto = _mm_read_UBYTE(modfp); + _mm_read_I_UWORDS(mh2->patsiz,256,modfp); + +// of.numpat=mh2->numpat; + of.numpos = mh2->snglen; + if(!AllocPositions(of.numpos)) return 0; + for(t=0; t<of.numpos; t++) + { if(mh2->orders[t]==0xff) break; + of.positions[t] = mh2->orders[t]; + } + + // count number of patterns stored in file + of.numpat = 0; + for(t=0; t<256; t++) + if(mh2->patsiz[t]) if((t+1)>of.numpat) of.numpat=t+1; + + of.numtrk = of.numpat*of.numchn; + + // seek across eventual new data + _mm_fseek(modfp,mh1->headerlen-(869+mh1->stlen),SEEK_CUR); + + // alloc track and pattern structures + if(!AllocTracks()) return 0; + if(!AllocPatterns()) return 0; + + for(t=0; t<of.numpat; t++) + { UBYTE rows=0,tempo; + + memset(pat,0,16*256*sizeof(FARNOTE)); + if(mh2->patsiz[t]) + { rows = _mm_read_UBYTE(modfp); + tempo = _mm_read_UBYTE(modfp); + + crow = pat; + for(u=mh2->patsiz[t]-2; u; u--, crow++) + { crow->note = _mm_read_UBYTE(modfp); + crow->ins = _mm_read_UBYTE(modfp); + crow->vol = _mm_read_UBYTE(modfp); + crow->eff = _mm_read_UBYTE(modfp); + } + + if(feof(modfp)) + { _mm_errno = MMERR_LOADING_PATTERN; + return 0; + } + + of.pattrows[t] = rows+2; + crow = pat; + for(u=16; u; u--) + if(!(of.tracks[tracks++] = FAR_ConvertTrack(crow,rows+2))) return 0; + } + } + + // read sample map + if(!_mm_read_UBYTES(smap,8,modfp)) + { _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + // count number of samples used + of.numins = 0; + for(t=0; t<64; t++) + if(smap[t>>3] & (1 << (t&7))) of.numins++; + of.numsmp = of.numins; + + // alloc sample structs + if(!AllocSamples()) return 0; + q = of.samples; + + for(t=0; t<64; t++) + { if(smap[t>>3] & (1 << (t&7))) + { _mm_read_SBYTES(s.samplename,32,modfp); + s.length = _mm_read_I_ULONG(modfp); + s.finetune = _mm_read_UBYTE(modfp); + s.volume = _mm_read_UBYTE(modfp); + s.reppos = _mm_read_I_ULONG(modfp); + s.repend = _mm_read_I_ULONG(modfp); + s.type = _mm_read_UBYTE(modfp); + s.loop = _mm_read_UBYTE(modfp); + + q->samplename = DupStr(s.samplename,32); + q->length = s.length; + q->loopstart = s.reppos; + q->loopend = s.repend; + q->volume = 64; + q->speed = 8363; + + q->flags=SF_SIGNED; + if(s.type&1) q->flags|=SF_16BITS; + if(s.loop) q->flags|=SF_LOOP; + + q->seekpos = _mm_ftell(modfp); + _mm_fseek(modfp,q->length,SEEK_CUR); + } + q++; + } + return 1; +} + + +CHAR *FAR_LoadTitle(void) +{ + CHAR s[40]; + + _mm_fseek(modfp,4,SEEK_SET); + if(!fread(s,40,1,modfp)) return NULL; + + return(DupStr(s,40)); +} + + +MLOADER load_far = +{ NULL, + "FAR", + "Portable FAR loader v0.1", + FAR_Init, + FAR_Test, + FAR_Load, + FAR_Cleanup, + FAR_LoadTitle +}; +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/playercode/load_it.c Fri Jan 23 16:05:08 1998 +0000 @@ -0,0 +1,857 @@ +/* + + Name: LOAD_IT.C + + Description: + ImpulseTracker (IT) module loader + + Portability: + All systems - all compilers (hopefully) + + Copyright 1997 by Jake Stine and Divine Entertainment + + If this module is found to not be portable to any particular platform, + please contact Jake Stine at dracoirs@epix.net (see MIKMOD.TXT for + more information on contacting the author). +*/ + +#include <string.h> +#include "mikmod.h" + +/************************************************************************** +**************************************************************************/ + +typedef struct ITNOTE +{ UBYTE note,ins,volpan,cmd,inf; +} ITNOTE; + +UBYTE *IT_ConvertTrack(ITNOTE *tr,UWORD numrows); + + +// Raw IT header struct: + +typedef struct ITHEADER +{ CHAR songname[26]; + UBYTE blank01[2]; + UWORD ordnum; + UWORD insnum; + UWORD smpnum; + UWORD patnum; + UWORD cwt; // Created with tracker (y.xx = 0x0yxx) + UWORD cmwt; // Compatable with tracker ver > than val. + UWORD flags; + UWORD special; // bit 0 set = song message attached + UBYTE globvol; + UBYTE mixvol; // mixing volume [ignored] + UBYTE initspeed; + UBYTE inittempo; + UBYTE pansep; // panning separation between channels + UBYTE zerobyte; + UWORD msglength; + ULONG msgoffset; + UBYTE blank02[4]; + + UBYTE pantable[64]; + UBYTE voltable[64]; +} ITHEADER; + + +// Raw IT sampleinfo struct: + +typedef struct ITSAMPLE +{ CHAR filename[12]; + UBYTE zerobyte; + UBYTE globvol; + UBYTE flag; + UBYTE volume; + UBYTE panning; + CHAR sampname[28]; + UWORD convert; // sample conversion flag + ULONG length; + ULONG loopbeg; + ULONG loopend; + ULONG c5spd; + ULONG susbegin; + ULONG susend; + ULONG sampoffset; + UBYTE vibspeed; + UBYTE vibdepth; + UBYTE vibrate; + UBYTE vibwave; // 0 = sine; 1 = rampdown; 2 = square; 3 = random (speed ignored) + + UBYTE noteindex; // for converting c5spd to finetune +} ITSAMPLE; + + +typedef struct ITINSTHEADER +{ ULONG size; // (dword) Instrument size + CHAR filename[12]; // (char) Instrument filename + UBYTE zerobyte; // (byte) Instrument type (always 0) + UBYTE volflg; + UBYTE volpts; + UBYTE volbeg; // (byte) Volume loop start (node) + UBYTE volend; // (byte) Volume loop end (node) + UBYTE volsusbeg; // (byte) Volume sustain begin (node) + UBYTE volsusend; // (byte) Volume Sustain end (node) + UBYTE panflg; + UBYTE panpts; + UBYTE panbeg; // (byte) channel loop start (node) + UBYTE panend; // (byte) channel loop end (node) + UBYTE pansusbeg; // (byte) cahnnel sustain begin (node) + UBYTE pansusend; // (byte) channel Sustain end (node) + UBYTE pitflg; + UBYTE pitpts; + UBYTE pitbeg; // (byte) pitch loop start (node) + UBYTE pitend; // (byte) pitch loop end (node) + UBYTE pitsusbeg; // (byte) pitch sustain begin (node) + UBYTE pitsusend; // (byte) pitch Sustain end (node) + UWORD blank; + UBYTE globvol; + UBYTE chanpan; + UWORD fadeout; // Envelope end / NNA volume fadeout + UBYTE dnc; // Duplicate note check + UBYTE dca; // Duplicate check action + UBYTE dct; // Duplicate check type + UBYTE nna; // New Note Action [0,1,2,3] + UWORD trkvers; // tracker version used to save [in files only] + UBYTE ppsep; // Pitch-pan Separation + UBYTE ppcenter; // Pitch-pan Center + UBYTE rvolvar; // random volume varations + UBYTE rpanvar; // random panning varations + UWORD numsmp; // Number of samples in instrument [in files only] + CHAR name[26]; // Instrument name + UBYTE blank01[6]; + UWORD samptable[120]; // sample for each note [note / samp pairs] + + UBYTE volenv[200]; // volume envelope (IT 1.x stuff) + UBYTE oldvoltick[25]; // volume tick position (IT 1.x stuff) + UBYTE volnode[25]; // aplitude of volume nodes + UWORD voltick[25]; // tick value of volume nodes + SBYTE pannode[25]; // panenv - node points + UWORD pantick[25]; // tick value of panning nodes + SBYTE pitnode[25]; // pitchenv - node points + UWORD pittick[25]; // tick value of pitch nodes +} ITINSTHEADER; + + +/************************************************************************** +**************************************************************************/ + +extern SBYTE remap[64]; // for removing empty channels +extern UBYTE *poslookup; // S3M/IT fix - removing blank patterns needs a + // lookup table to fix position-jump commands +static ULONG *paraptr = NULL; // parapointer array (see IT docs) +static ITHEADER *mh = NULL; +static ITNOTE *itpat = NULL; // allocate to space for one full pattern +static UBYTE *mask = NULL; // arrays allocated to 64 elements and used for +static ITNOTE *last = NULL; // uncompressing IT's pattern information +static int numtrk = 0; +static int old_effect; // if set, use S3M old-effects stuffs +static int *noteindex; + +CHAR IT_Version[] = "ImpulseTracker x.xx"; + + +BOOL IT_Test(void) +{ + UBYTE id[4]; + + if(!_mm_read_UBYTES(id,4,modfp)) return 0; + if(!memcmp(id,"IMPM",4)) return 1; + return 0; +} + +BOOL IT_Init(void) +{ + if((mh=(ITHEADER *)_mm_calloc(1,sizeof(ITHEADER)))==NULL) return 0; + if((poslookup=(UBYTE *)_mm_malloc(256*sizeof(UBYTE)))==NULL) return 0; + if((itpat=(ITNOTE *)_mm_malloc(200*64*sizeof(ITNOTE)))==NULL) return 0; + if((mask=(UBYTE *)_mm_malloc(64*sizeof(UBYTE)))==NULL) return 0; + if((last=(ITNOTE *)_mm_malloc(64*sizeof(ITNOTE)))==NULL) return 0; + + return 1; +} + +void IT_Cleanup(void) +{ + if(mh!=NULL) free(mh); + if(poslookup!=NULL) free(poslookup); + if(itpat!=NULL) free(itpat); + if(mask!=NULL) free(mask); + if(last!=NULL) free(last); + if(paraptr!=NULL) free(paraptr); + if(noteindex!=NULL) free(noteindex); + + mh = NULL; + poslookup = NULL; + itpat = NULL; + mask = NULL; + last = NULL; + paraptr = NULL; + noteindex = NULL; +} + + +BOOL IT_GetNumChannels(UWORD patrows) + +// Because so many IT files have 64 channels as the set number used, but really +// only use far less (usually 8 to 12 still), I had to make this function, +// which determines the number of channels that are actually USED by a pattern. +// +// For every channel that's used, it sets the appropriate array entry of the +// global varialbe 'isused' +// +// NOTE: You must first seek to the file location of the pattern before calling +// this procedure. +// Returns 1 on error +{ + int row=0,flag,ch; + + do + { flag = _mm_read_UBYTE(modfp); + if(flag == EOF) + { _mm_errno = MMERR_LOADING_PATTERN; + return 1; + } + + if(flag == 0) + { row++; + } else + { ch = (flag-1) & 63; + remap[ch] = 0; + if(flag & 128) mask[ch] = _mm_read_UBYTE(modfp); + if(mask[ch] & 1) _mm_read_UBYTE(modfp); + if(mask[ch] & 2) _mm_read_UBYTE(modfp); + if(mask[ch] & 4) _mm_read_UBYTE(modfp); + if(mask[ch] & 8) { _mm_read_UBYTE(modfp); _mm_read_UBYTE(modfp); } + } + } while(row < patrows); + + return 0; +} + + +BOOL IT_ReadPattern(UWORD patrows) +{ + int blah; + int row=0,flag,ch; + ITNOTE *itt = itpat, dummy,*n,*l; + + memset(itt,255,patrows*of.numchn*sizeof(ITNOTE)); + + do + { flag = _mm_read_UBYTE(modfp); + if(feof(modfp)) + { _mm_errno = MMERR_LOADING_PATTERN; + return 0; + } + if(flag == 0) + { itt = &itt[of.numchn]; + row++; + } else + { ch = remap[(flag-1) & 63]; + if(ch != -1) + { n = &itt[ch]; + l = &last[ch]; + } else + { n = l = &dummy; } + + if(flag & 128) mask[ch] = _mm_read_UBYTE(modfp); + if(mask[ch] & 1) if((l->note = n->note = _mm_read_UBYTE(modfp)) == 255) + { l->note = n->note = 253; } + if(mask[ch] & 2) l->ins = n->ins = _mm_read_UBYTE(modfp); + if(mask[ch] & 4) l->volpan = n->volpan = _mm_read_UBYTE(modfp); + if(mask[ch] & 8) { l->cmd = n->cmd = _mm_read_UBYTE(modfp); + l->inf = n->inf = _mm_read_UBYTE(modfp); } + if(mask[ch] & 16) n->note = l->note; + if(mask[ch] & 32) n->ins = l->ins; + if(mask[ch] & 64) n->volpan = l->volpan; + if(mask[ch] & 128) { n->cmd = l->cmd; + n->inf = l->inf; } + } + } while(row < patrows); + + for(blah=0; blah<of.numchn; blah++) + of.tracks[numtrk++] = IT_ConvertTrack(&itpat[blah],patrows); + + return 1; +} + + +void S3MIT_ProcessCmd(UBYTE cmd, UBYTE inf, BOOL oldeffect); + +// table for porta-to-note command within volume/panning column +static UBYTE portatable[] = "1, 4, 8, 16, 32, 64, 96, 128, 255"; + +UBYTE *IT_ConvertTrack(ITNOTE *tr, UWORD numrows) +{ + int t; + + UBYTE note,ins,volpan; + + UniReset(); + + for(t=0; t<numrows; t++) + { note = tr[t*of.numchn].note; + ins = tr[t*of.numchn].ins; + volpan = tr[t*of.numchn].volpan; + + if(note!=255) + { if(note==253) + UniWrite(UNI_KEYOFF); + else if(note==254) + UniPTEffect(0xc,0); // <- note cut command + else + UniNote(note); + } + + if((ins != 0) && (ins < 100)) UniInstrument(ins-1); + + // process volume / panning column + // volume / panning effects do NOT all share the same memory address + // yet. That requires more work than I care to apply at the moment ;) + + if(volpan<=64) + { UniVolEffect(VOL_VOLUME,volpan); + } else if((volpan>=65) && (volpan<=74)) // fine volume slide up (65-74) + { UniVolEffect(VOL_VOLSLIDE,0x0f + ((volpan-65)<<4)); + } else if((volpan>=75) && (volpan<=84)) // fine volume slide down (75-84) + { UniVolEffect(VOL_VOLSLIDE,0xf0 + (volpan-75)); + } else if((volpan>=85) && (volpan<=94)) // volume slide up (85-94) + { UniVolEffect(VOL_VOLSLIDE,((volpan-85)<<4)); + } else if((volpan>=95) && (volpan<=104)) // volume slide down (95-104) + { UniVolEffect(VOL_VOLSLIDE,(volpan-95)); + } else if((volpan>=105) && (volpan<=114)) // pitch slide up (105-114) + { UniVolEffect(VOL_PITCHSLIDEDN,((volpan-105)<<4)); + } else if((volpan>=115) && (volpan<=124)) // pitch slide down (115-124) + { UniVolEffect(VOL_PITCHSLIDEUP,(volpan-115)); + } else if((volpan>=128) && (volpan<=192)) + { UniVolEffect(VOL_PANNING,((volpan-128) == 64) ? 255 : ((volpan-128) << 2)); + } else if((volpan>=193) && (volpan<=202)) // portamento to note + { UniVolEffect(VOL_PORTAMENTO,portatable[volpan-193]); + } else if((volpan>=203) && (volpan<=212)) // vibrato + { UniVolEffect(VOL_VIBRATO,(volpan-203)); + } + + S3MIT_ProcessCmd(tr[t*of.numchn].cmd,tr[t*of.numchn].inf,old_effect); + + UniNewline(); + } + return UniDup(); +} + + +int cvt_c5spd_to_finetune(ULONG c5spd, int sampnum) +{ + int ctmp=0,tmp,note=1,finetune=0; + + c5spd/=2; + + do + { tmp = getfrequency(of.flags,getlinearperiod(note,0)); + if(tmp >= c5spd) break; + ctmp = tmp; + note++; + } while(1); + + if(tmp != c5spd) + { if((tmp-c5spd) < (c5spd-ctmp)) + while(tmp>c5spd) tmp = getfrequency(of.flags,getlinearperiod(note,--finetune)); + else + { note--; + while(ctmp<c5spd) ctmp = getfrequency(of.flags,getlinearperiod(note,++finetune)); + } + } + + noteindex[sampnum] = note-48; + return finetune; +} + + +BOOL IT_Load(void) +{ + int t,u,lp; + INSTRUMENT *d; + SAMPLE *q; + + numtrk = 0; + + // try to read module header + + _mm_read_I_ULONG(modfp); // kill the 4 byte header + _mm_read_string(mh->songname,26,modfp); + _mm_read_UBYTES(mh->blank01,2,modfp); + mh->ordnum =_mm_read_I_UWORD(modfp); + mh->insnum =_mm_read_I_UWORD(modfp); + mh->smpnum =_mm_read_I_UWORD(modfp); + mh->patnum =_mm_read_I_UWORD(modfp); + mh->cwt =_mm_read_I_UWORD(modfp); + mh->cmwt =_mm_read_I_UWORD(modfp); + mh->flags =_mm_read_I_UWORD(modfp); + mh->special =_mm_read_I_UWORD(modfp); + + mh->globvol =_mm_read_UBYTE(modfp); + mh->mixvol =_mm_read_UBYTE(modfp); + mh->initspeed =_mm_read_UBYTE(modfp); + mh->inittempo =_mm_read_UBYTE(modfp); + mh->pansep =_mm_read_UBYTE(modfp); + mh->zerobyte =_mm_read_UBYTE(modfp); + mh->msglength =_mm_read_I_UWORD(modfp); + mh->msgoffset =_mm_read_I_ULONG(modfp); + _mm_read_UBYTES(mh->blank02,4,modfp); + _mm_read_UBYTES(mh->pantable,64,modfp); + _mm_read_UBYTES(mh->voltable,64,modfp); + + if(feof(modfp)) + { _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + // set module variables + + of.modtype = strdup(IT_Version); + of.modtype[15] = (mh->cwt >> 8) + 0x30; + of.modtype[17] = ((mh->cwt >> 4) & 0xf) + 0x30; + of.modtype[18] = ((mh->cwt) & 0xf) + 0x30; + of.songname = DupStr(mh->songname,26); // make a cstr of songname + of.reppos = 0; + of.numpat = mh->patnum; + of.numins = mh->insnum; + of.numsmp = mh->smpnum; + of.initspeed = mh->initspeed; + of.inittempo = mh->inittempo; + of.initvolume = mh->globvol; + + old_effect = 0; + if(mh->flags & 8) { of.flags |= (UF_XMPERIODS | UF_LINEAR); old_effect |= 2; } + if((mh->cwt >= 0x106) && (mh->flags & 16)) old_effect |= 1; + + // set panning positions + for(t=0; t<64; t++) + { if(mh->pantable[t] < 64) of.panning[t] = mh->pantable[t] << 2; + else if(mh->pantable[t]==64) of.panning[t] = 255; + else if(mh->pantable[t]==100) of.panning[t] = PAN_SURROUND; + } + + // set channel volumes + memcpy(of.chanvol,mh->voltable,64); + + // read the order data + if(!AllocPositions(mh->ordnum)) return 0; + + for(t=0; t<mh->ordnum; t++) + of.positions[t] = _mm_read_UBYTE(modfp); + + if(feof(modfp)) + { _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + of.numpos = 0; + for(t=0; t<mh->ordnum; t++) + { of.positions[of.numpos] = of.positions[t]; + poslookup[t] = of.numpos; // bug fix for FREAKY S3Ms / ITs + if(of.positions[t]<254) of.numpos++; + } + + if((paraptr=(ULONG *)_mm_malloc((mh->insnum+mh->smpnum+of.numpat)*sizeof(ULONG))) == NULL) return 0; + + // read the instrument, sample, and pattern parapointers + _mm_read_I_ULONGS(paraptr,mh->insnum+mh->smpnum+of.numpat,modfp); + + // now is a good time to check if the header was too short :) + if(feof(modfp)) + { _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + // Check for and load song comment + if(mh->special & 1) + { _mm_fseek(modfp,(long)(mh->msgoffset),SEEK_SET); + if(!ReadComment(mh->msglength)) return 0; + } + + if(!(mh->flags & 4)) of.numins = of.numsmp; + if(!AllocSamples()) return 0; + + if((noteindex=(int *)_mm_malloc(mh->smpnum*sizeof(int)))==NULL) return 0; + + q = of.samples; + + // Load all samples (they're used either way) + for(t=0; t<mh->smpnum; t++) + { ITSAMPLE s; + + // seek to sample position + _mm_fseek(modfp,(long)(paraptr[mh->insnum+t] + 4),SEEK_SET); + + // and load sample info + _mm_read_string(s.filename,12,modfp); + s.zerobyte = _mm_read_UBYTE(modfp); + s.globvol = _mm_read_UBYTE(modfp); + s.flag = _mm_read_UBYTE(modfp); + s.volume = _mm_read_UBYTE(modfp); + _mm_read_string(s.sampname,26,modfp); + s.convert = _mm_read_UBYTE(modfp); + s.panning = _mm_read_UBYTE(modfp); + s.length = _mm_read_I_ULONG(modfp); + s.loopbeg = _mm_read_I_ULONG(modfp); + s.loopend = _mm_read_I_ULONG(modfp); + s.c5spd = _mm_read_I_ULONG(modfp); + s.susbegin = _mm_read_I_ULONG(modfp); + s.susend = _mm_read_I_ULONG(modfp); + s.sampoffset = _mm_read_I_ULONG(modfp); + s.vibspeed = _mm_read_UBYTE(modfp); + s.vibdepth = _mm_read_UBYTE(modfp); + s.vibrate = _mm_read_UBYTE(modfp); + s.vibwave = _mm_read_UBYTE(modfp); + + + // Generate an error if c5spd is > 512k, or samplelength > 256 megs + // (nothing would EVER be that high) + + if(feof(modfp) || (s.c5spd > 0x7ffffL) || (s.length > 0xfffffffUL) || + (s.loopbeg > 0xfffffffUL) || (s.loopend > 0xfffffffUL)) + { _mm_errno = MMERR_LOADING_SAMPLEINFO; + return 0; + } + + q->samplename = DupStr(s.sampname,26); + + q->speed = s.c5spd / 2; + q->panning = ((s.panning & 127)==64) ? 255 : (s.panning & 127) << 2; + q->length = s.length; + q->loopstart = s.loopbeg; + q->loopend = s.loopend; + q->volume = s.volume; + q->globvol = s.globvol; + q->seekpos = s.sampoffset; + + // =================================== + // Convert speed to XM linear finetune + + if(of.flags & UF_LINEAR) + q->speed = cvt_c5spd_to_finetune(s.c5spd, t); + + if(s.panning & 128) q->flags |= SF_OWNPAN; + + if(s.vibrate) + { q->vibflags |= AV_IT; + q->vibtype = s.vibwave; + q->vibsweep = s.vibrate * 2; + q->vibdepth = s.vibdepth; + q->vibrate = s.vibspeed; + } + + if(s.flag & 2) q->flags |= SF_16BITS; + if(s.flag & 16) q->flags |= SF_LOOP; + if(s.flag & 64) q->flags |= SF_BIDI; + + if(mh->cwt >= 0x200) + { if(s.convert & 1) q->flags |= SF_SIGNED; + if(s.convert & 4) q->flags |= SF_DELTA; + } + + q++; + } + + // Load instruments if instrument mode flag enabled + + if(mh->flags & 4) + { if(!AllocInstruments()) return 0; + d = of.instruments; + of.flags |= UF_NNA | UF_INST; + + for(t=0; t<mh->insnum; t++) + { ITINSTHEADER ih; + + // seek to instrument position + _mm_fseek(modfp,paraptr[t]+4,SEEK_SET); + + // and load instrument info + _mm_read_string(ih.filename,12,modfp); + ih.zerobyte = _mm_read_UBYTE(modfp); + if(mh->cwt < 0x200) // load IT 1.xx inst header + { ih.volflg = _mm_read_UBYTE(modfp); + ih.volbeg = _mm_read_UBYTE(modfp); + ih.volend = _mm_read_UBYTE(modfp); + ih.volsusbeg = _mm_read_UBYTE(modfp); + ih.volsusend = _mm_read_UBYTE(modfp); + _mm_read_I_UWORD(modfp); + ih.fadeout = _mm_read_I_UWORD(modfp); + ih.nna = _mm_read_UBYTE(modfp); + ih.dnc = _mm_read_UBYTE(modfp); + } else // Read IT200+ header + { ih.nna = _mm_read_UBYTE(modfp); + ih.dct = _mm_read_UBYTE(modfp); + ih.dca = _mm_read_UBYTE(modfp); + ih.fadeout = _mm_read_I_UWORD(modfp); + ih.ppsep = _mm_read_UBYTE(modfp); + ih.ppcenter = _mm_read_UBYTE(modfp); + ih.globvol = _mm_read_UBYTE(modfp); + ih.chanpan = _mm_read_UBYTE(modfp); + ih.rvolvar = _mm_read_UBYTE(modfp); + ih.rpanvar = _mm_read_UBYTE(modfp); + } + + ih.trkvers = _mm_read_I_UWORD(modfp); + ih.numsmp = _mm_read_UBYTE(modfp); + _mm_read_UBYTE(modfp); + _mm_read_string(ih.name,26,modfp); + _mm_read_UBYTES(ih.blank01,6,modfp); + _mm_read_I_UWORDS(ih.samptable,120,modfp); + if(mh->cwt < 0x200) // load IT 1xx volume envelope + { _mm_read_UBYTES(ih.volenv,200,modfp); + for(lp=0; lp<25; lp++) + { ih.oldvoltick[lp] = _mm_read_UBYTE(modfp); + ih.volnode[lp] = _mm_read_UBYTE(modfp); + } + } else // load IT 2xx vol & chanpan & pitch envs + { ih.volflg = _mm_read_UBYTE(modfp); + ih.volpts = _mm_read_UBYTE(modfp); + ih.volbeg = _mm_read_UBYTE(modfp); + ih.volend = _mm_read_UBYTE(modfp); + ih.volsusbeg = _mm_read_UBYTE(modfp); + ih.volsusend = _mm_read_UBYTE(modfp); + for(lp=0; lp<25; lp++) + { ih.volnode[lp] = _mm_read_UBYTE(modfp); + ih.voltick[lp] = _mm_read_I_UWORD(modfp); + } + _mm_read_UBYTE(modfp); + + ih.panflg = _mm_read_UBYTE(modfp); + ih.panpts = _mm_read_UBYTE(modfp); + ih.panbeg = _mm_read_UBYTE(modfp); + ih.panend = _mm_read_UBYTE(modfp); + ih.pansusbeg = _mm_read_UBYTE(modfp); + ih.pansusend = _mm_read_UBYTE(modfp); + for(lp=0; lp<25; lp++) + { ih.pannode[lp] = _mm_read_SBYTE(modfp); + ih.pantick[lp] = _mm_read_I_UWORD(modfp); + } + _mm_read_UBYTE(modfp); + + ih.pitflg = _mm_read_UBYTE(modfp); + ih.pitpts = _mm_read_UBYTE(modfp); + ih.pitbeg = _mm_read_UBYTE(modfp); + ih.pitend = _mm_read_UBYTE(modfp); + ih.pitsusbeg = _mm_read_UBYTE(modfp); + ih.pitsusend = _mm_read_UBYTE(modfp); + for(lp=0; lp<25; lp++) + { ih.pitnode[lp] = _mm_read_SBYTE(modfp); + ih.pittick[lp] = _mm_read_I_UWORD(modfp); + } + _mm_read_UBYTE(modfp); + } + + if(feof(modfp)) + { _mm_errno = MMERR_LOADING_SAMPLEINFO; + return 0; + } + + d->volflg |= EF_VOLENV; + d->insname = DupStr(ih.name,26); + d->nnatype = ih.nna; + + if(mh->cwt < 0x200) + { d->volfade = ih.fadeout << 6; + if(ih.dnc) + { d->dct = DCT_NOTE; + d->dca = DCA_CUT; + } + + if(ih.volflg & 1) d->volflg |= EF_ON; + if(ih.volflg & 2) d->volflg |= EF_LOOP; + if(ih.volflg & 4) d->volflg |= EF_SUSTAIN; + + // XM conversion of IT envelope Array + + d->volbeg = ih.volbeg; + d->volend = ih.volend; + d->volsusbeg = ih.volsusbeg; + d->volsusend = ih.volsusend; + + if(ih.volflg & 1) + { for(u=0; u<25; u++) + if(ih.oldvoltick[d->volpts] != 0xff) + { d->volenv[d->volpts].val = (ih.volnode[d->volpts] << 2); + d->volenv[d->volpts].pos = ih.oldvoltick[d->volpts]; + d->volpts++; + } else break; + } + } else + { d->panning = ((ih.chanpan&127) == 64) ? 255 : (ih.chanpan&127)<<2; + if(!(ih.chanpan & 128)) d->flags |= IF_OWNPAN; + + if(!(ih.ppsep & 128)) + { d->pitpansep = ih.ppsep << 2; + d->pitpancenter= ih.ppcenter; + d->flags |= IF_PITCHPAN; + } + d->globvol = ih.globvol >> 1; + d->volfade = ih.fadeout << 5; + d->dct = ih.dct; + d->dca = ih.dca; + + if(mh->cwt >= 0x204) + { d->rvolvar = ih.rvolvar; + d->rpanvar = ih.rpanvar; + } + + if(ih.volflg & 1) d->volflg |= EF_ON; + if(ih.volflg & 2) d->volflg |= EF_LOOP; + if(ih.volflg & 4) d->volflg |= EF_SUSTAIN; + + if(ih.panflg & 1) d->panflg |= EF_ON; + if(ih.panflg & 2) d->panflg |= EF_LOOP; + if(ih.panflg & 4) d->panflg |= EF_SUSTAIN; + + if(ih.pitflg & 1) d->pitflg |= EF_ON; + if(ih.pitflg & 2) d->pitflg |= EF_LOOP; + if(ih.pitflg & 4) d->pitflg |= EF_SUSTAIN; + + d->volpts = ih.volpts; + d->volbeg = ih.volbeg; + d->volend = ih.volend; + d->volsusbeg = ih.volsusbeg; + d->volsusend = ih.volsusend; + + for(u=0; u<ih.volpts; u++) + { d->volenv[u].val = (ih.volnode[u] << 2); + d->volenv[u].pos = ih.voltick[u]; + } + + d->panpts = ih.panpts; + d->panbeg = ih.panbeg; + d->panend = ih.panend; + d->pansusbeg = ih.pansusbeg; + d->pansusend = ih.pansusend; + + for(u=0; u<ih.panpts; u++) + { d->panenv[u].val = (ih.pannode[u]+32) << 2; + d->panenv[u].pos = ih.pantick[u]; + } + + d->pitpts = ih.pitpts; + d->pitbeg = ih.pitbeg; + d->pitend = ih.pitend; + d->pitsusbeg = ih.pitsusbeg; + d->pitsusend = ih.pitsusend; + + for(u=0; u<ih.pitpts; u++) + { d->pitenv[u].val = (ih.pitnode[u]+32); + d->pitenv[u].pos = ih.pittick[u]; + } + } + + if(of.flags & UF_LINEAR) + { for(u=0; u<120; u++) + { d->samplenote[u] = (ih.samptable[u] & 255); + d->samplenumber[u] = (ih.samptable[u] >> 8) ? ((ih.samptable[u] >> 8) - 1) : 255; + if(d->samplenumber[u]!=255) + d->samplenote[u] += noteindex[d->samplenumber[u]]; + } + } else + { for(u=0; u<120; u++) + { d->samplenote[u] = (ih.samptable[u] & 255); + d->samplenumber[u] = (ih.samptable[u] >> 8) ? ((ih.samptable[u] >> 8) - 1) : 255; + } + } + + d++; + } + } else if(of.flags & UF_LINEAR) + { if(!AllocInstruments()) return 0; + d = of.instruments; + of.flags |= UF_INST; + + for(t=0; t<mh->smpnum; t++, d++) + { for(u=0; u<120; u++) + d->samplenote[u] += noteindex[d->samplenumber[u]]; + } + } + + + // Figure out how many channels this blasted song actually uses (what + // ever happened to common courtesy of storing this simple value + // somewhere in the damn module, eh!?) + + of.numchn = 0; + memset(remap,-1,64*sizeof(UBYTE)); + + for(t=0; t<of.numpat; t++) + { UWORD packlen; + + // seek to pattern position + if(paraptr[mh->insnum+mh->smpnum+t] != 0) // No parapointer = pattern of 64 rows, EMPTY + { _mm_fseek(modfp,(((long)paraptr[mh->insnum+mh->smpnum+t])),SEEK_SET); + packlen = _mm_read_I_UWORD(modfp); + packlen = _mm_read_I_UWORD(modfp); // read pattern length (# of rows) + _mm_read_I_ULONG(modfp); + if(IT_GetNumChannels(packlen)) return 0; + } + } + + // give each of them a different number + for(t=0; t<64; t++) + { if(remap[t]==0) + { remap[t] = of.numchn; + of.numchn++; + } + } + + of.numtrk = of.numpat*of.numchn; + + + if(!AllocPatterns()) return 0; + if(!AllocTracks()) return 0; + + for(t=0; t<of.numpat; t++) + { UWORD packlen; + + // seek to pattern position + if(paraptr[mh->insnum+mh->smpnum+t] == 0) // No parapointer = pattern of 64 rows, EMPTY + { of.pattrows[t] = 64; + for(u=0; u<of.numchn; u++) + { int k; + UniReset(); + for(k=0; k<64; k++) UniNewline(); + of.tracks[numtrk++] = UniDup(); + } + } else + { _mm_fseek(modfp,(((long)paraptr[mh->insnum+mh->smpnum+t])),SEEK_SET); + packlen = _mm_read_I_UWORD(modfp); + of.pattrows[t] = _mm_read_I_UWORD(modfp); + _mm_read_I_ULONG(modfp); + + if(!IT_ReadPattern(of.pattrows[t])) return 0; + } + } + + return 1; +} + + +CHAR *IT_LoadTitle(void) +{ + CHAR s[26]; + + _mm_fseek(modfp,4,SEEK_SET); + if(!fread(s,26,1,modfp)) return NULL; + + return(DupStr(s,26)); +} + + +MLOADER load_it = +{ NULL, + "IT", + "Portable IT loader v0.2", + IT_Init, + IT_Test, + IT_Load, + IT_Cleanup, + + IT_LoadTitle +}; +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/playercode/load_m15.c Fri Jan 23 16:05:08 1998 +0000 @@ -0,0 +1,410 @@ +/* + + Name: LOAD_M15.C + + Description: + 15 instrument MOD loader + Also supports Ultimate Sound Tracker (old M15 format) + + Portability: + All systems - all compilers (hopefully) + + If this module is found to not be portable to any particular platform, + please contact Jake Stine at dracoirs@epix.net (see MIKMOD.TXT for + more information on contacting the author). + +*/ + +#include <string.h> +#include "mikmod.h" + +/************************************************************************* +*************************************************************************/ + + +typedef struct MSAMPINFO // sample header as it appears in a module +{ CHAR samplename[22]; + UWORD length; + UBYTE finetune; + UBYTE volume; + UWORD reppos; + UWORD replen; +} MSAMPINFO; + + +typedef struct MODULEHEADER // verbatim module header +{ CHAR songname[20]; // the songname.. + MSAMPINFO samples[15]; // all sampleinfo + UBYTE songlength; // number of patterns used + UBYTE magic1; // should be 127 + UBYTE positions[128]; // which pattern to play at pos +} MODULEHEADER; + + +typedef struct MODNOTE +{ UBYTE a,b,c,d; +} MODNOTE; + + +/************************************************************************* +*************************************************************************/ + +static MODULEHEADER *mh = NULL; // raw as-is module header +static MODNOTE *patbuf = NULL; +static BOOL ust_loader = 0; // if TRUE, load as a ust module. +static CHAR nulls[3] = {0,0,0}; + +static BOOL LoadModuleHeader(MODULEHEADER *mh) +{ + int t; + + _mm_read_string(mh->songname,20,modfp); + + for(t=0; t<15; t++) + { MSAMPINFO *s = &mh->samples[t]; + _mm_read_string(s->samplename,22,modfp); + s->length =_mm_read_M_UWORD(modfp); + s->finetune =_mm_read_UBYTE(modfp); + s->volume =_mm_read_UBYTE(modfp); + s->reppos =_mm_read_M_UWORD(modfp); + s->replen =_mm_read_M_UWORD(modfp); + } + + mh->songlength =_mm_read_UBYTE(modfp); + mh->magic1 =_mm_read_UBYTE(modfp); // should be 127 + _mm_read_UBYTES(mh->positions,128,modfp); + + return(!feof(modfp)); +} + + +static int CheckPatternType(int numpat) + +// Checks the patterns in the modfile for UST / 15-inst indications. +// For example, if an effect 3xx is found, it is assumed that the song +// is 15-inst. If a 1xx effect has dat greater than 0x20, it is UST. +// Returns: 0 indecisive; 1 = UST; 2 = 15-inst + +{ + int t; + UBYTE eff, dat; + + ust_loader = 1; + + for(t=0; t<numpat*(64U*4); t++) + { // Load the pattern into the temp buffer + // and convert it + + _mm_read_UBYTE(modfp); // read note + _mm_read_UBYTE(modfp); // read inst + eff = _mm_read_UBYTE(modfp); + dat = _mm_read_UBYTE(modfp); + + if((eff==3) && (dat!=0) || (eff >= 2)) return 2; + if(eff==1) + { if(dat > 0x1f) return 1; + if(dat < 0x3) return 2; + } + if((eff==2) && (dat > 0x1f)) return 1; + } + + return 0; +} + + +BOOL M15_Test(void) +{ + int t, numpat; + MODULEHEADER mh; + + ust_loader = 0; + + if(!LoadModuleHeader(&mh)) return 0; + if(mh.magic1>127) return 0; + + for(t=0; t<15; t++) + { // all finetunes should be zero + if(mh.samples[t].finetune != 0) return 0; + + // all volumes should be <= 64 + if(mh.samples[t].volume > 64) return 0; + + // all instrument names should begin with s, st-, or a number + if(mh.samples[t].samplename[0] == 's') + { if((memcmp(mh.samples[t].samplename,"st-",3) != 0) && + (memcmp(mh.samples[t].samplename,"ST-",3) != 0) && + (memcmp(mh.samples[t].samplename,nulls,3) != 0)) + ust_loader = 1; + } else if((mh.samples[t].samplename[0] < '0') || (mh.samples[t].samplename[0] > '9')) + ust_loader = 1; + + if(mh.samples[t].length > 4999) + { ust_loader = 0; + if(mh.samples[t].length > 32768) return 0; + } + + if(!ust_loader) return 1; + + if(((mh.samples[t].reppos) + mh.samples[t].replen) > (mh.samples[t].length + 10)) + { ust_loader = 1; + return 1; + } + + } + + for(numpat=0, t=0; t<mh.songlength; t++) + { if(mh.positions[t] > numpat) + numpat = mh.positions[t]; + } + + numpat++; + switch(CheckPatternType(numpat)) + { case 0: // indecisive, so check more clues... + + break; + + case 1: ust_loader = 1; break; + case 2: ust_loader = 0; break; + } + + return 1; +} + + +BOOL M15_Init(void) +{ + if(!(mh=(MODULEHEADER *)_mm_calloc(1,sizeof(MODULEHEADER)))) return 0; + return 1; +} + + +void M15_Cleanup(void) +{ + if(mh!=NULL) free(mh); + if(patbuf!=NULL) free(patbuf); + + mh = NULL; + patbuf = NULL; +} + + +/* +Old (amiga) noteinfo: + + _____byte 1_____ byte2_ _____byte 3_____ byte4_ +/ \ / \ / \ / \ +0000 0000-00000000 0000 0000-00000000 + +Upper four 12 bits for Lower four Effect command. +bits of sam- note period. bits of sam- +ple number. ple number. + +*/ + + +static void M15_ConvertNote(MODNOTE *n) +{ + UBYTE instrument,effect,effdat,note; + UWORD period; + + // extract the various information from the 4 bytes that + // make up a single note + + instrument = (n->a&0x10)|(n->c>>4); + period = (((UWORD)n->a&0xf)<<8)+n->b; + effect = n->c&0xf; + effdat = n->d; + + // Convert the period to a note number + + note=0; + if(period != 0) + { for(note=0; note<60; note++) + if(period >= npertab[note]) break; + note++; + if(note==61) note = 0; + } + + if(instrument!=0) UniInstrument(instrument-1); + if(note!=0) UniNote(note+23); + + // Convert pattern jump from Dec to Hex + if(effect == 0xd) + effdat = (((effdat&0xf0)>>4)*10)+(effdat&0xf); + + if(ust_loader) + { switch(effect) + { case 0: break; + case 1: + UniPTEffect(0,effdat); + break; + + case 2: + if(effdat&0xf) UniPTEffect(1,effdat&0xf); + if(effdat>>2) UniPTEffect(2,effdat>>2); + break; + + case 3: break; + + default: + UniPTEffect(effect,effdat); + break; + } + } else UniPTEffect(effect,effdat); +} + + +static UBYTE *M15_ConvertTrack(MODNOTE *n) +{ + int t; + + UniReset(); + for(t=0; t<64; t++) + { M15_ConvertNote(n); + UniNewline(); + n += 4; + } + return UniDup(); +} + + + +static BOOL M15_LoadPatterns(void) +// Loads all patterns of a modfile and converts them into the +// 3 byte format. +{ + int t,s,tracks=0; + + if(!AllocPatterns()) return 0; + if(!AllocTracks()) return 0; + + // Allocate temporary buffer for loading + // and converting the patterns + + if(!(patbuf=(MODNOTE *)_mm_calloc(64U*4,sizeof(MODNOTE)))) return 0; + + for(t=0; t<of.numpat; t++) + { // Load the pattern into the temp buffer + // and convert it + + for(s=0; s<(64U*4); s++) + { patbuf[s].a=_mm_read_UBYTE(modfp); + patbuf[s].b=_mm_read_UBYTE(modfp); + patbuf[s].c=_mm_read_UBYTE(modfp); + patbuf[s].d=_mm_read_UBYTE(modfp); + } + + for(s=0; s<4; s++) + if(!(of.tracks[tracks++]=M15_ConvertTrack(patbuf+s))) return 0; + } + + return 1; +} + + +BOOL M15_Load(void) +{ + int t; + SAMPLE *q; + MSAMPINFO *s; // old module sampleinfo + + // try to read module header + + if(!LoadModuleHeader(mh)) + { _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + + if(ust_loader) + of.modtype = strdup("Ultimate Soundtracker"); + else + of.modtype = strdup("Soundtracker"); + + // set module variables + + of.initspeed = 6; + of.inittempo = 125; + of.numchn = 4; // get number of channels + of.songname = DupStr(mh->songname,20); // make a cstr of songname + of.numpos = mh->songlength; // copy the songlength + + if(!AllocPositions(of.numpos)) return 0; + for(t=0; t<of.numpos; t++) + of.positions[t] = mh->positions[t]; + + + // Count the number of patterns + + of.numpat = 0; + + for(t=0; t<of.numpos; t++) + { if(of.positions[t] > of.numpat) + of.numpat = of.positions[t]; + } + of.numpat++; + of.numtrk = of.numpat*4; + + // Finally, init the sampleinfo structures + + of.numins = of.numsmp = 15; + if(!AllocSamples()) return 0; + + s = mh->samples; // init source pointer + q = of.samples; + + for(t=0; t<of.numins; t++) + { // convert the samplename + q->samplename = DupStr(s->samplename,22); + + // init the sampleinfo variables and + // convert the size pointers to longword format + + q->speed = finetune[s->finetune&0xf]; + q->volume = s->volume; + if(ust_loader) + q->loopstart = s->reppos; + else + q->loopstart = s->reppos<<1; + q->loopend = q->loopstart+(s->replen<<1); + q->length = s->length<<1; + + q->flags = SF_SIGNED | SF_UST_LOOP; + if(s->replen>1) q->flags |= SF_LOOP; + + // fix replen if repend>length + + if(q->loopend>q->length) q->loopend = q->length; + + s++; // point to next source sampleinfo + q++; + } + + if(!M15_LoadPatterns()) return 0; + + ust_loader = 0; + return 1; +} + + +CHAR *M15_LoadTitle(void) +{ + CHAR s[20]; + + fseek(modfp,0,SEEK_SET); + if(!fread(s,22,1,modfp)) return NULL; + + return(DupStr(s,20)); +} + + +MLOADER load_m15 = +{ NULL, + "15-instrument module", + "Portable MOD-15 loader v0.1", + M15_Init, + M15_Test, + M15_Load, + M15_Cleanup, + M15_LoadTitle +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/playercode/load_med.c Fri Jan 23 16:05:08 1998 +0000 @@ -0,0 +1,490 @@ +/* + + Name: LOAD_MED.C + + Description: + Amiga MED module loader + + Portability: + All systems - all compilers (hopefully) + + If this module is found to not be portable to any particular platform, + please contact Jake Stine at dracoirs@epix.net (see MIKMOD.TXT for + more information on contacting the author). + +*/ + +#include <string.h> +#include "mikmod.h" + +#define MMD0_string 0x4D4D4430 +#define MMD1_string 0x4D4D4431 + +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; + + +static MMD0 *mh = NULL; +static MMD0song *ms = NULL; +static ULONG *ba = NULL; +static MMD0NOTE *mmd0pat = NULL; +static MMD1NOTE *mmd1pat = NULL; + +#define d0note(row,col) mmd0pat[(row*(UWORD)of.numchn)+col] +#define d1note(row,col) mmd1pat[(row*(UWORD)of.numchn)+col] + + +static CHAR MED_Version[] = "MED"; + + +BOOL MED_Test(void) +{ + UBYTE id[4]; + + if(!_mm_read_UBYTES(id,4,modfp)) return 0; + if(!memcmp(id,"MMD0",4)) return 1; + if(!memcmp(id,"MMD1",4)) return 1; + return 0; +} + + +BOOL MED_Init(void) +{ + if(!(mh=(MMD0 *)_mm_calloc(1,sizeof(MMD0)))) return 0; + if(!(ms=(MMD0song *)_mm_calloc(1,sizeof(MMD0song)))) return 0; + return 1; +} + + +void MED_Cleanup(void) +{ + if(mh!=NULL) free(mh); + if(ms!=NULL) free(ms); + if(ba!=NULL) free(ba); + if(mmd0pat!=NULL) free(mmd0pat); + if(mmd1pat!=NULL) free(mmd1pat); + + mh = NULL; + ms = NULL; + ba = NULL; // blockarr + 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; + + case 0x6: // not used + case 0x7: // not used + case 0x8: // midi hold/decay + break; + + case 0x9: + if(dat<=0x20) UniPTEffect(0xf,dat); + break; + + // 0xa 0xb 0xc all PT effects + + case 0xd: // same as PT volslide + UniPTEffect(0xa,dat); + break; + + case 0xe: // synth jmp - midi + break; + + case 0xf: + // F00 does patternbreak with med + if(dat==0) UniPTEffect(0xd,0); + else if(dat<=0xa) UniPTEffect(0xf,dat); + else if(dat<0xf1) UniPTEffect(0xf,((UWORD)dat*125)/33); + else if(dat==0xff) UniPTEffect(0xc,0); // stop note + break; + + default: // all normal PT effects are handled here :) + // Convert pattern jump from Dec to Hex + if(eff == 0xd) + dat = (((dat&0xf0)>>4)*10)+(dat&0xf); + UniPTEffect(eff,dat); + break; + } +} + + + +UBYTE *MED_Convert1(int col) +{ + int t; + UBYTE a,b,c,d,inst,note,eff,dat; + MMD1NOTE *n; + + UniReset(); + for(t=0; t<64; t++) + { n = &d1note(t,col); + a = n->a; + b = n->b; + c = n->c; + d = n->d; + + note = a&0x7f; + inst = b&0x3f; + eff = c&0xf; + dat = d; + + if(inst!=0) UniInstrument(inst-1); + if(note!=0) UniNote(note+23); + + EffectCvt(eff,dat); + UniNewline(); + } + + return UniDup(); +} + + +UBYTE *MED_Convert0(int col) +{ + int t; + UBYTE a,b,c,inst,note,eff,dat; + MMD0NOTE *n; + + UniReset(); + for(t=0;t<64;t++) + { n = &d0note(t,col); + a = n->a; + b = n->b; + c = n->c; + + note = a & 0x3f; + a >>= 6; + a = ((a & 1) << 1) | (a >> 1); + + inst = (b >> 4) | (a << 4); + eff = b & 0xf; + dat = c; + + if(inst!=0) UniInstrument(inst-1); + if(note!=0) UniNote(note+35); + + EffectCvt(eff,dat); + 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: no more mr. nice guy, + // really 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+1; + memset(mmdp=mmd0pat,0,of.numchn*maxlines*sizeof(MMD0NOTE)); + for(row=numlines+1; 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(col); + track++; + } + } + 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); + + _mm_fseek(modfp,sizeof(ULONG),SEEK_CUR); + 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: no more mr. nice guy, 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+1; 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(col); + track++; + } + } + return 1; +} + + + +BOOL MED_Load(void) +{ + 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]; + + of.initspeed = 6; + of.inittempo = ((UWORD)ms->deftempo*125)/33; + 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; + if(!AllocSamples()) return 0; + q = of.samples; + + for(t=0; t<of.numins; t++) + { _mm_fseek(modfp,sa[t],SEEK_SET); + s.length = _mm_read_M_ULONG(modfp); + s.type = _mm_read_M_SWORD(modfp); + + if(feof(modfp)) + { _mm_errno = MMERR_LOADING_SAMPLEINFO; + return 0; + } + + q->samplename = NULL; + 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); + q->flags = SF_SIGNED; + q->speed = 8363; + q->volume = 64; + + if(ms->sample[t].replen>1) q->flags|=SF_LOOP; + + // don't load sample if length>='MMD0' hah.. hah.. very funny.. NOT! + if(q->length >= MMD0_string) q->length = 0; + + q++; + } + + + if(mh->id==MMD0_string) + { if(!LoadMMD0Patterns()) return 0; + } else if(mh->id==MMD1_string) + { if(!LoadMMD1Patterns()) return 0; + } else + { _mm_errno = MMERR_NOT_A_MODULE; + return 0; + } + + return 1; +} + + +MLOADER load_med = +{ NULL, + "MED", + "MED loader v0.1", + MED_Init, + MED_Test, + MED_Load, + MED_Cleanup, + NULL +}; + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/playercode/load_mod.c Fri Jan 23 16:05:08 1998 +0000 @@ -0,0 +1,341 @@ +/* + + Name: LOAD_MOD.C + + Description: + Generic MOD loader (Protracker, StarTracker, FastTracker, etc) + + Portability: + All systems - all compilers (hopefully) + + If this module is found to not be portable to any particular platform, + please contact Jake Stine at dracoirs@epix.net (see MIKMOD.TXT for + more information on contacting the author). + +*/ + +#include <string.h> +#include "mikmod.h" + +/************************************************************************* +*************************************************************************/ + + +typedef struct MSAMPINFO // sample header as it appears in a module +{ CHAR samplename[22]; + UWORD length; + UBYTE finetune; + UBYTE volume; + UWORD reppos; + UWORD replen; +} MSAMPINFO; + + +typedef struct MODULEHEADER // verbatim module header +{ CHAR songname[20]; // the songname.. + MSAMPINFO samples[31]; // all sampleinfo + UBYTE songlength; // number of patterns used + UBYTE magic1; // should be 127 + UBYTE positions[128]; // which pattern to play at pos + UBYTE magic2[4]; // string "M.K." or "FLT4" or "FLT8" +} MODULEHEADER; + +#define MODULEHEADERSIZE 1084 + + +typedef struct MODTYPE // struct to identify type of module +{ CHAR id[5]; + UBYTE channels; + CHAR *name; +} MODTYPE; + + +typedef struct MODNOTE +{ UBYTE a,b,c,d; +} MODNOTE; + + +/************************************************************************* +*************************************************************************/ + + +CHAR protracker[] = "Protracker"; +CHAR startracker[] = "Startracker"; +CHAR fasttracker[] = "Fasttracker"; +CHAR ins15tracker[] = "15-instrument"; +CHAR oktalyzer[] = "Oktalyzer"; +CHAR taketracker[] = "TakeTracker"; + + +MODTYPE modtypes[] = +{ "M.K.",4,protracker, // protracker 4 channel + "M!K!",4,protracker, // protracker 4 channel + "FLT4",4,startracker, // startracker 4 channel + "2CHN",2,fasttracker, // fasttracker 2 channel + "4CHN",4,fasttracker, // fasttracker 4 channel + "6CHN",6,fasttracker, // fasttracker 6 channel + "8CHN",8,fasttracker, // fasttracker 8 channel + "10CH",10,fasttracker, // fasttracker 10 channel + "12CH",12,fasttracker, // fasttracker 12 channel + "14CH",14,fasttracker, // fasttracker 14 channel + "16CH",16,fasttracker, // fasttracker 16 channel + "18CH",18,fasttracker, // fasttracker 18 channel + "20CH",20,fasttracker, // fasttracker 20 channel + "22CH",22,fasttracker, // fasttracker 22 channel + "24CH",24,fasttracker, // fasttracker 24 channel + "26CH",26,fasttracker, // fasttracker 26 channel + "28CH",28,fasttracker, // fasttracker 28 channel + "30CH",30,fasttracker, // fasttracker 30 channel + "32CH",32,fasttracker, // fasttracker 32 channel + "CD81",8,oktalyzer, // atari oktalyzer 8 channel + "OKTA",8,oktalyzer, // atari oktalyzer 8 channel + "16CN",16,taketracker, // taketracker 16 channel + "32CN",32,taketracker, // taketracker 32 channel + " ",4,ins15tracker // 15-instrument 4 channel +}; + +static MODULEHEADER *mh = NULL; // raw as-is module header +static MODNOTE *patbuf = NULL; +static int modtype = 0; + +BOOL MOD_Test(void) +{ + UBYTE id[4]; + + _mm_fseek(modfp,MODULEHEADERSIZE-4,SEEK_SET); + if(!fread(id,4,1,modfp)) return 0; + + // find out which ID string + + for(modtype=0; modtype<23; modtype++) + if(!memcmp(id,modtypes[modtype].id,4)) return 1; + + return 0; +} + + +BOOL MOD_Init(void) +{ + if(!(mh=(MODULEHEADER *)_mm_calloc(1,sizeof(MODULEHEADER)))) return 0; + return 1; +} + + +void MOD_Cleanup(void) +{ + if(mh!=NULL) free(mh); + if(patbuf!=NULL) free(patbuf); + + mh = NULL; + patbuf = NULL; +} + + +/* +Old (amiga) noteinfo: + + _____byte 1_____ byte2_ _____byte 3_____ byte4_ +/ \ / \ / \ / \ +0000 0000-00000000 0000 0000-00000000 + +Upper four 12 bits for Lower four Effect command. +bits of sam- note period. bits of sam- +ple number. ple number. + +*/ + + +void ConvertNote(MODNOTE *n) +{ + UBYTE instrument,effect,effdat,note; + UWORD period; + + // extract the various information from the 4 bytes that + // make up a single note + + instrument = (n->a&0x10)|(n->c>>4); + period = (((UWORD)n->a&0xf)<<8)+n->b; + effect = n->c&0xf; + effdat = n->d; + + // Convert the period to a note number + + note=0; + if(period!=0) + { for(note=0; note<60; note++) + if(period >= npertab[note]) break; + note++; + if(note==61) note = 0; + } + + if(instrument!=0) UniInstrument(instrument-1); + if(note!=0) UniNote(note+23); + + // Convert pattern jump from Dec to Hex + if(effect == 0xd) + effdat = (((effdat&0xf0)>>4)*10)+(effdat&0xf); + + UniPTEffect(effect,effdat); +} + + +UBYTE *ConvertTrack(MODNOTE *n) +{ + int t; + + UniReset(); + for(t=0;t<64;t++) + { ConvertNote(n); + UniNewline(); + n+=of.numchn; + } + return UniDup(); +} + + +BOOL ML_LoadPatterns(void) +// Loads all patterns of a modfile and converts them into the +// 3 byte format. +{ + int t,s,tracks = 0; + + if(!AllocPatterns()) return 0; + if(!AllocTracks()) return 0; + + // Allocate temporary buffer for loading + // and converting the patterns + + if(!(patbuf=(MODNOTE *)_mm_calloc(64U*of.numchn,sizeof(MODNOTE)))) return 0; + + for(t=0; t<of.numpat; t++) + { // Load the pattern into the temp buffer + // and convert it + + for(s=0; s<(64U*of.numchn); s++) + { patbuf[s].a = _mm_read_UBYTE(modfp); + patbuf[s].b = _mm_read_UBYTE(modfp); + patbuf[s].c = _mm_read_UBYTE(modfp); + patbuf[s].d = _mm_read_UBYTE(modfp); + } + + for(s=0; s<of.numchn; s++) + if(!(of.tracks[tracks++]=ConvertTrack(patbuf+s))) return 0; + } + + return 1; +} + + +BOOL MOD_Load(void) +{ + int t; + SAMPLE *q; + MSAMPINFO *s; // old module sampleinfo + + // try to read module header + + _mm_read_string((CHAR *)mh->songname,20,modfp); + + for(t=0; t<31; t++) + { s = &mh->samples[t]; + _mm_read_string(s->samplename,22,modfp); + s->length =_mm_read_M_UWORD(modfp); + s->finetune =_mm_read_UBYTE(modfp); + s->volume =_mm_read_UBYTE(modfp); + s->reppos =_mm_read_M_UWORD(modfp); + s->replen =_mm_read_M_UWORD(modfp); + } + + mh->songlength =_mm_read_UBYTE(modfp); + mh->magic1 =_mm_read_UBYTE(modfp); + + _mm_read_UBYTES(mh->positions,128,modfp); + _mm_read_UBYTES(mh->magic2,4,modfp); + + if(feof(modfp)) + { _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + // set module variables + + of.initspeed = 6; + of.inittempo = 125; + of.numchn = modtypes[modtype].channels; // get number of channels + of.modtype = strdup(modtypes[modtype].name); // get ascii type of mod + of.songname = DupStr(mh->songname,20); // make a cstr of songname + of.numpos = mh->songlength; // copy the songlength + + if(!AllocPositions(of.numpos)) return 0; + for(t=0; t<of.numpos; t++) + of.positions[t] = mh->positions[t]; + + // Count the number of patterns + + of.numpat = 0; + + for(t=0; t<of.numpos; t++) + { if(of.positions[t] > of.numpat) + of.numpat = of.positions[t]; + } + of.numpat++; + of.numtrk = of.numpat*of.numchn; + + // Finally, init the sampleinfo structures + of.numins = of.numsmp = 31; + + if(!AllocSamples()) return 0; + + s = mh->samples; // init source pointer + q = of.samples; + + for(t=0; t<of.numins; t++) + { // convert the samplename + q->samplename = DupStr(s->samplename, 22); + + // init the sampleinfo variables and + // convert the size pointers to longword format + + q->speed = finetune[s->finetune & 0xf]; + q->volume = s->volume; + q->loopstart = (ULONG)s->reppos << 1; + q->loopend = q->loopstart + ((ULONG)s->replen << 1); + q->length = (ULONG)s->length << 1; + + q->flags = SF_SIGNED; + if(s->replen > 1) q->flags |= SF_LOOP; + + // fix replen if repend > length + if(q->loopend > q->length) q->loopend = q->length; + + s++; // point to next source sampleinfo + q++; + } + + if(!ML_LoadPatterns()) return 0; + return 1; +} + + +CHAR *MOD_LoadTitle(void) +{ + CHAR s[20]; + + _mm_fseek(modfp,0,SEEK_SET); + if(!fread(s,20,1,modfp)) return NULL; + + return(DupStr(s,20)); +} + + +MLOADER load_mod = +{ NULL, + "Standard module", + "Portable MOD loader v0.11", + MOD_Init, + MOD_Test, + MOD_Load, + MOD_Cleanup, + MOD_LoadTitle +}; +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/playercode/load_mtm.c Fri Jan 23 16:05:08 1998 +0000 @@ -0,0 +1,282 @@ +/* + + Name: LOAD_MTM.C + + Description: + MTM module loader + + Portability: + All systems - all compilers (hopefully) + + If this module is found to not be portable to any particular platform, + please contact Jake Stine at dracoirs@epix.net (see MIKMOD.TXT for + more information on contacting the author). + +*/ + +#include <string.h> +#include "mikmod.h" + +/************************************************************************** +**************************************************************************/ + + +typedef struct MTMSAMPLE +{ CHAR samplename[22]; + ULONG length; + ULONG reppos; + ULONG repend; + UBYTE finetune; + UBYTE volume; + UBYTE attribute; +} MTMSAMPLE; + + + +typedef struct MTMHEADER +{ UBYTE id[3]; // MTM file marker + UBYTE version; // upper major, lower nibble minor version number + char songname[20]; // ASCIIZ songname + UWORD numtracks; // number of tracks saved + UBYTE lastpattern; // last pattern number saved + UBYTE lastorder; // last order number to play (songlength-1) + UWORD commentsize; // length of comment field + UBYTE numsamples; // number of samples saved + UBYTE attribute; // attribute byte (unused) + UBYTE beatspertrack; // + UBYTE numchannels; // number of channels used + UBYTE panpos[32]; // voice pan positions +} MTMHEADER; + + +typedef struct MTMNOTE +{ UBYTE a,b,c; +} MTMNOTE; + + +/************************************************************************** +**************************************************************************/ + + +static MTMHEADER *mh = NULL; +static MTMNOTE *mtmtrk = NULL; +static UWORD pat[32]; + +char MTM_Version[] = "MTM"; + + + +BOOL MTM_Test(void) +{ + UBYTE id[3]; + if(!_mm_read_UBYTES(id,3,modfp)) return 0; + if(!memcmp(id,"MTM",3)) return 1; + return 0; +} + + +BOOL MTM_Init(void) +{ + if(!(mtmtrk=(MTMNOTE *)_mm_calloc(64,sizeof(MTMNOTE)))) return 0; + if(!(mh=(MTMHEADER *)_mm_calloc(1,sizeof(MTMHEADER)))) return 0; + + return 1; +} + + +void MTM_Cleanup(void) +{ + if(mtmtrk!=NULL) free(mtmtrk); + if(mh!=NULL) free(mh); + + mtmtrk = NULL; + mh = NULL; +} + + +UBYTE *MTM_Convert(void) +{ + int t; + UBYTE a,b,c,inst,note,eff,dat; + + UniReset(); + for(t=0; t<64; t++) + { a = mtmtrk[t].a; + b = mtmtrk[t].b; + c = mtmtrk[t].c; + + inst = ((a&0x3)<<4)|(b>>4); + note = a>>2; + + eff = b&0xf; + dat = c; + + if(inst!=0) UniInstrument(inst-1); + if(note!=0) UniNote(note+24); + + // mtm bug bugfix: when the effect is volslide, + // slide-up _always_ overrides slide-dn. + + if(eff==0xa && dat&0xf0) dat&=0xf0; + + // Convert pattern jump from Dec to Hex + if(eff == 0xd) + dat = (((dat&0xf0)>>4)*10)+(dat&0xf); + UniPTEffect(eff,dat); + UniNewline(); + } + return UniDup(); +} + + +BOOL MTM_Load(void) +{ + MTMSAMPLE s; + SAMPLE *q; + + int t,u; + + // try to read module header + + _mm_read_UBYTES(mh->id,3,modfp); + mh->version =_mm_read_UBYTE(modfp); + _mm_read_string(mh->songname,20,modfp); + mh->numtracks =_mm_read_I_UWORD(modfp); + mh->lastpattern =_mm_read_UBYTE(modfp); + mh->lastorder =_mm_read_UBYTE(modfp); + mh->commentsize =_mm_read_I_UWORD(modfp); + mh->numsamples =_mm_read_UBYTE(modfp); + mh->attribute =_mm_read_UBYTE(modfp); + mh->beatspertrack=_mm_read_UBYTE(modfp); + mh->numchannels =_mm_read_UBYTE(modfp); + _mm_read_UBYTES(mh->panpos,32,modfp); + + if(feof(modfp)) + { _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + // set module variables + + of.initspeed = 6; + of.inittempo = 125; + of.modtype = strdup(MTM_Version); + of.numchn = mh->numchannels; + of.numtrk = mh->numtracks+1; // get number of channels + of.songname = DupStr(mh->songname,20); // make a cstr of songname + of.numpos = mh->lastorder+1; // copy the songlength + of.numpat = mh->lastpattern+1; + for(t=0; t<32; t++) of.panning[t] = mh->panpos[t] << 4; + + of.numins = of.numsmp = mh->numsamples; + if(!AllocSamples()) return 0; + + q = of.samples; + + for(t=0; t<of.numins; t++) + { // try to read sample info + _mm_read_string(s.samplename,22,modfp); + s.length =_mm_read_I_ULONG(modfp); + s.reppos =_mm_read_I_ULONG(modfp); + s.repend =_mm_read_I_ULONG(modfp); + s.finetune =_mm_read_UBYTE(modfp); + s.volume =_mm_read_UBYTE(modfp); + s.attribute =_mm_read_UBYTE(modfp); + + if(feof(modfp)) + { _mm_errno = MMERR_LOADING_SAMPLEINFO; + return 0; + } + + q->samplename = DupStr(s.samplename,22); + q->seekpos = 0; + q->speed = finetune[s.finetune]; + q->length = s.length; + q->loopstart = s.reppos; + q->loopend = s.repend; + q->volume = s.volume; + + if((s.repend-s.reppos) > 2) q->flags |= SF_LOOP; + + if(s.attribute & 1) + { // If the sample is 16-bits, convert the length + // and replen byte-values into sample-values + + q->flags|=SF_16BITS; + q->length>>=1; + q->loopstart>>=1; + q->loopend>>=1; + } + + q++; + } + + if(!AllocPositions(of.numpos)) return 0; + for(t=0; t<of.numpos; t++) + of.positions[t] = _mm_read_UBYTE(modfp); + for(; t<128; t++) _mm_read_UBYTE(modfp); + + if(feof(modfp)) + { _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + if(!AllocTracks()) return 0; + if(!AllocPatterns()) return 0; + + of.tracks[0] = MTM_Convert(); // track 0 is empty + + for(t=1; t<of.numtrk; t++) + { int s; + + for(s=0; s<64; s++) + { mtmtrk[s].a=_mm_read_UBYTE(modfp); + mtmtrk[s].b=_mm_read_UBYTE(modfp); + mtmtrk[s].c=_mm_read_UBYTE(modfp); + } + + if(feof(modfp)) + { _mm_errno = MMERR_LOADING_TRACK; + return 0; + } + + if(!(of.tracks[t]=MTM_Convert())) return 0; + } + + for(t=0; t<of.numpat; t++) + { _mm_read_I_UWORDS(pat,32,modfp); + + for(u=0; u<of.numchn; u++) + of.patterns[((long)t*of.numchn)+u]=pat[u]; + } + + // read comment field + + if(!ReadComment(mh->commentsize)) return 0; + + return 1; +} + + +CHAR *MTM_LoadTitle(void) +{ + CHAR s[20]; + + _mm_fseek(modfp,4,SEEK_SET); + if(!fread(s,20,1,modfp)) return NULL; + + return(DupStr(s,20)); +} + + +MLOADER load_mtm = +{ NULL, + "MTM", + "Portable MTM loader v0.1", + MTM_Init, + MTM_Test, + MTM_Load, + MTM_Cleanup, + MTM_LoadTitle +}; +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/playercode/load_s3m.c Fri Jan 23 16:05:08 1998 +0000 @@ -0,0 +1,466 @@ +/* + + Name: LOAD_S3M.C + + Description: + Screamtracker (S3M) module loader + + Portability: + All systems - all compilers (hopefully) + + If this module is found to not be portable to any particular platform, + please contact Jake Stine at dracoirs@epix.net (see MIKMOD.TXT for + more information on contacting the author). + +*/ + +#include <string.h> +#include "mikmod.h" + +/************************************************************************** +**************************************************************************/ + +typedef struct S3MNOTE +{ UBYTE note,ins,vol,cmd,inf; +} S3MNOTE; + +typedef S3MNOTE S3MTRACK[64]; + + +// Raw S3M header struct: + +typedef struct S3MHEADER +{ CHAR songname[28]; + UBYTE t1a; + UBYTE type; + UBYTE unused1[2]; + UWORD ordnum; + UWORD insnum; + UWORD patnum; + UWORD flags; + UWORD tracker; + UWORD fileformat; + CHAR scrm[4]; + UBYTE mastervol; + UBYTE initspeed; + UBYTE inittempo; + UBYTE mastermult; + UBYTE ultraclick; + UBYTE pantable; + UBYTE unused2[8]; + UWORD special; + UBYTE channels[32]; +} S3MHEADER; + + +// Raw S3M sampleinfo struct: + +typedef struct S3MSAMPLE +{ UBYTE type; + CHAR filename[12]; + UBYTE memsegh; + UWORD memsegl; + ULONG length; + ULONG loopbeg; + ULONG loopend; + UBYTE volume; + UBYTE dsk; + UBYTE pack; + UBYTE flags; + ULONG c2spd; + UBYTE unused[12]; + CHAR sampname[28]; + CHAR scrs[4]; +} S3MSAMPLE; + +/************************************************************************** +**************************************************************************/ + + +extern UBYTE *poslookup; // S3M/IT fix - removing blank patterns needs a + // lookup table to fix position-jump commands +extern SBYTE remap[64]; // for removing empty channels + +static S3MNOTE *s3mbuf = NULL; // pointer to a complete S3M pattern +static S3MHEADER *mh = NULL; +static UWORD *paraptr = NULL; // parapointer array (see S3M docs) + +CHAR S3M_Version[] = "Screamtracker 3.xx"; + +BOOL S3M_Test(void) +{ + UBYTE id[4]; + + _mm_fseek(modfp,0x2c,SEEK_SET); + if(!_mm_read_UBYTES(id,4,modfp)) return 0; + if(!memcmp(id,"SCRM",4)) return 1; + return 0; +} + +BOOL S3M_Init(void) +{ + if(!(s3mbuf = (S3MNOTE *)_mm_malloc(16*64*sizeof(S3MNOTE)))) return 0; + if(!(mh = (S3MHEADER *)_mm_calloc(1,sizeof(S3MHEADER)))) return 0; + if(!(poslookup = (UBYTE *)_mm_malloc(sizeof(UBYTE)*128))) return 0; + + return 1; +} + +void S3M_Cleanup(void) +{ + if(s3mbuf!=NULL) free(s3mbuf); + if(paraptr!=NULL) free(paraptr); + if(poslookup!=NULL) free(poslookup); + if(mh!=NULL) free(mh); + + paraptr = NULL; + s3mbuf = NULL; + poslookup = NULL; + mh = NULL; +} + + +BOOL S3M_GetNumChannels(void) + +// Because so many s3m files have 16 channels as the set number used, but really +// only use far less (usually 8 to 12 still), I had to make this function, +// which determines the number of channels that are actually USED by a pattern. +// +// For every channel that's used, it sets the appropriate array entry of the +// global varialbe 'isused' +// +// NOTE: You must first seek to the file location of the pattern before calling +// this procedure. +// Returns 1 on fail. + +{ + int row=0,flag,ch; + + while(row<64) + { flag = _mm_read_UBYTE(modfp); + + if(feof(modfp)) + { _mm_errno = MMERR_LOADING_PATTERN; + return 1; + } + + if(flag) + { ch = flag&31; + if(mh->channels[ch] < 16) remap[ch] = 0; + + if(flag&32) + { _mm_read_UBYTE(modfp); + _mm_read_UBYTE(modfp); + } + + if(flag&64) + _mm_read_UBYTE(modfp); + + if(flag&128) + { _mm_read_UBYTE(modfp); + _mm_read_UBYTE(modfp); + } + } else row++; + } + + return 0; +} + + +BOOL S3M_ReadPattern(void) +{ + int row=0,flag,ch; + S3MNOTE *n; + S3MNOTE dummy; + + // clear pattern data + memset(s3mbuf,255,16*64*sizeof(S3MNOTE)); + + while(row<64) + { flag = _mm_read_UBYTE(modfp); + + if(flag==EOF) + { _mm_errno = MMERR_LOADING_PATTERN; + return 0; + } + + if(flag) + { ch = remap[flag&31]; + + if(ch != -1) + n = &s3mbuf[(64U*ch)+row]; + else + n = &dummy; + + if(flag&32) + { n->note = _mm_read_UBYTE(modfp); + n->ins = _mm_read_UBYTE(modfp); + } + + if(flag&64) + n->vol = _mm_read_UBYTE(modfp); + + if(flag&128) + { n->cmd = _mm_read_UBYTE(modfp); + n->inf = _mm_read_UBYTE(modfp); + } + } else row++; + } + return 1; +} + + +void S3MIT_ProcessCmd(UBYTE cmd, UBYTE inf, BOOL oldeffect); + +UBYTE *S3M_ConvertTrack(S3MNOTE *tr) +{ + int t; + + UBYTE note,ins,vol; + + UniReset(); + for(t=0; t<64; t++) + { + note = tr[t].note; + ins = tr[t].ins; + vol = tr[t].vol; + + + if(ins!=0 && ins!=255) UniInstrument(ins-1); + if(note!=255) + { if(note==254) UniPTEffect(0xc,0); // <- note off command + else UniNote(((note>>4)*12)+(note&0xf)); // <- normal note + } + + if(vol<255) + UniPTEffect(0xc,vol); + + S3MIT_ProcessCmd(tr[t].cmd,tr[t].inf,1); + UniNewline(); + } + + return UniDup(); +} + + +BOOL S3M_Load(void) +{ + int t,u,track = 0; + SAMPLE *q; + UBYTE pan[32]; + + // try to read module header + + _mm_read_string(mh->songname,28,modfp); + mh->t1a =_mm_read_UBYTE(modfp); + mh->type =_mm_read_UBYTE(modfp); + _mm_read_UBYTES(mh->unused1,2,modfp); + mh->ordnum =_mm_read_I_UWORD(modfp); + mh->insnum =_mm_read_I_UWORD(modfp); + mh->patnum =_mm_read_I_UWORD(modfp); + mh->flags =_mm_read_I_UWORD(modfp); + mh->tracker =_mm_read_I_UWORD(modfp); + mh->fileformat =_mm_read_I_UWORD(modfp); + _mm_read_string(mh->scrm,4,modfp); + + mh->mastervol =_mm_read_UBYTE(modfp); + mh->initspeed =_mm_read_UBYTE(modfp); + mh->inittempo =_mm_read_UBYTE(modfp); + mh->mastermult =_mm_read_UBYTE(modfp); + mh->ultraclick =_mm_read_UBYTE(modfp); + mh->pantable =_mm_read_UBYTE(modfp); + _mm_read_UBYTES(mh->unused2,8,modfp); + mh->special =_mm_read_I_UWORD(modfp); + _mm_read_UBYTES(mh->channels,32,modfp); + + if(feof(modfp)) + { _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + // set module variables + + of.modtype = strdup(S3M_Version); + of.modtype[14] = ((mh->tracker >> 8) &0xf) + 0x30; + of.modtype[16] = ((mh->tracker >> 4)&0xf) + 0x30; + of.modtype[17] = ((mh->tracker)&0xf) + 0x30; + of.songname = DupStr(mh->songname,28); + of.numpat = mh->patnum; + of.reppos = 0; + of.numins = of.numsmp = mh->insnum; + of.initspeed = mh->initspeed; + of.inittempo = mh->inittempo; + of.initvolume = mh->mastervol<<1; + + // read the order data + if(!AllocPositions(mh->ordnum)) return 0; + for(t=0; t<mh->ordnum; t++) + of.positions[t] = _mm_read_UBYTE(modfp); + + of.numpos = 0; + for(t=0; t<mh->ordnum; t++) + { of.positions[of.numpos] = of.positions[t]; + poslookup[t] = of.numpos; // bug fix for FREAKY S3Ms + if(of.positions[t]<254) of.numpos++; + } + + if((paraptr=(UWORD *)_mm_malloc((of.numins+of.numpat)*sizeof(UWORD)))==NULL) return 0; + + // read the instrument+pattern parapointers + _mm_read_I_UWORDS(paraptr,of.numins+of.numpat,modfp); + + + if(mh->pantable==252) + { // read the panning table (ST 3.2 addition. See below for further + // portions of channel panning [past reampper]). + _mm_read_UBYTES(pan,32,modfp); + } + + + // now is a good time to check if the header was too short :) + + if(feof(modfp)) + { _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + + // ============================================== + // Load those darned Samples! (no insts in ST3) + + if(!AllocSamples()) return 0; + + q = of.samples; + + for(t=0; t<of.numins; t++) + { S3MSAMPLE s; + + // seek to instrument position + + _mm_fseek(modfp,((long)paraptr[t])<<4,SEEK_SET); + + // and load sample info + + s.type =_mm_read_UBYTE(modfp); + _mm_read_string(s.filename,12,modfp); + s.memsegh =_mm_read_UBYTE(modfp); + s.memsegl =_mm_read_I_UWORD(modfp); + s.length =_mm_read_I_ULONG(modfp); + s.loopbeg =_mm_read_I_ULONG(modfp); + s.loopend =_mm_read_I_ULONG(modfp); + s.volume =_mm_read_UBYTE(modfp); + s.dsk =_mm_read_UBYTE(modfp); + s.pack =_mm_read_UBYTE(modfp); + s.flags =_mm_read_UBYTE(modfp); + s.c2spd =_mm_read_I_ULONG(modfp); + _mm_read_UBYTES(s.unused,12,modfp); + _mm_read_string(s.sampname,28,modfp); + _mm_read_string(s.scrs,4,modfp); + + if(feof(modfp)) + { _mm_errno = MMERR_LOADING_SAMPLEINFO; + return 0; + } + + q->samplename = DupStr(s.sampname,28); + q->speed = s.c2spd; + q->length = s.length; + q->loopstart = s.loopbeg; + q->loopend = s.loopend; + q->volume = s.volume; + q->seekpos = (((long)s.memsegh)<<16|s.memsegl)<<4; + + if(s.flags&1) q->flags |= SF_LOOP; + if(s.flags&4) q->flags |= SF_16BITS; + if(mh->fileformat==1) q->flags |= SF_SIGNED; + + // DON'T load sample if it doesn't have the SCRS tag + if(memcmp(s.scrs,"SCRS",4)!=0) q->length = 0; + + q++; + } + + // ==================================== + // Determine the number of channels actually used. (what ever happened + // to the concept of a single "numchn" variable, eh?! + + of.numchn = 0; + memset(remap,-1,32*sizeof(UBYTE)); + + for(t=0; t<of.numpat; t++) + { // seek to pattern position ( + 2 skip pattern length ) + _mm_fseek(modfp,(long)((paraptr[of.numins+t])<<4)+2,SEEK_SET); + if(S3M_GetNumChannels()) return 0; + } + + // build the remap array + for(t=0; t<32; t++) + { if(remap[t]==0) + { remap[t] = of.numchn; + of.numchn++; + } + } + + // ============================================================ + // set panning positions AFTER building remap chart! + + for(t=0; t<32; t++) + { if((mh->channels[t]<16) && (remap[t]!=-1)) + { if(mh->channels[t]<8) + of.panning[remap[t]] = 0x20; // 0x30 = std s3m val + else + of.panning[remap[t]] = 0xd0; // 0xc0 = std s3m val + } + } + + if(mh->pantable==252) + { // set panning positions according to panning table (new for st3.2) + for(t=0; t<32; t++) + { if((pan[t]&0x20) && (mh->channels[t]<16) && (remap[t]!=-1)) + of.panning[remap[t]] = (pan[t]&0xf)<<4; + } + } + + + // ============================== + // Load the pattern info now! + + of.numtrk = of.numpat*of.numchn; + if(!AllocTracks()) return 0; + if(!AllocPatterns()) return 0; + + for(t=0; t<of.numpat; t++) + { // seek to pattern position ( + 2 skip pattern length ) + _mm_fseek(modfp,(((long)paraptr[of.numins+t])<<4)+2,SEEK_SET); + if(!S3M_ReadPattern()) return 0; + for(u=0; u<of.numchn; u++) + if(!(of.tracks[track++]=S3M_ConvertTrack(&s3mbuf[u*64]))) return 0; + } + + return 1; +} + + +CHAR *S3M_LoadTitle(void) +{ + CHAR s[28]; + + _mm_fseek(modfp,0,SEEK_SET); + if(!fread(s,28,1,modfp)) return NULL; + + return(DupStr(s,28)); +} + + +MLOADER load_s3m = +{ NULL, + "S3M", + "S3M loader v0.3", + S3M_Init, + S3M_Test, + S3M_Load, + S3M_Cleanup, + + S3M_LoadTitle +}; +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/playercode/load_stm.c Fri Jan 23 16:05:08 1998 +0000 @@ -0,0 +1,374 @@ +/* + + Name: LOAD_STM.C + + Description: + ScreamTracker 2 (STM) module Loader - Version 1.oOo Release 2 + A Coding Nightmare by Rao and Air Richter of HaRDCoDE + You can now play all of those wonderful old C.C. Catch STM's! + + Portability: + All systems - all compilers (hopefully) + + If this module is found to not be portable to any particular platform, + please contact Jake Stine at dracoirs@epix.net (see MIKMOD.TXT for + more information on contacting the author). + +*/ + +#include <string.h> +#include <ctype.h> +#include "mikmod.h" + + +typedef struct STMNOTE +{ UBYTE note,insvol,volcmd,cmdinf; +} STMNOTE; + + +// Raw STM sampleinfo struct: + +typedef struct STMSAMPLE +{ CHAR filename[12]; // Can't have long comments - just filename comments :) + UBYTE unused; // 0x00 + UBYTE instdisk; // Instrument disk + UWORD reserved; // ISA in memory when in ST 2 + UWORD length; // Sample length + UWORD loopbeg; // Loop start point + UWORD loopend; // Loop end point + UBYTE volume; // Volume + UBYTE reserved2; // More reserved crap + UWORD c2spd; // Good old c2spd + UBYTE reserved3[4]; // Yet more of PSi's reserved crap + UWORD isa; // Internal Segment Address -> + // contrary to the tech specs, this is NOT actually + // written to the stm file. +} STMSAMPLE; + +// Raw STM header struct: + +typedef struct STMHEADER +{ CHAR songname[20]; + CHAR trackername[8]; // !SCREAM! for ST 2.xx + UBYTE unused; // 0x1A + UBYTE filetype; // 1=song, 2=module (only 2 is supported, of course) :) + UBYTE ver_major; // Like 2 + UBYTE ver_minor; // "ditto" + UBYTE inittempo; // initspeed= stm inittempo>>4 + UBYTE numpat; // number of patterns + UBYTE globalvol; // <- WoW! a RiGHT TRiANGLE =8*) + UBYTE reserved[13]; // More of PSi's internal crap + STMSAMPLE sample[31]; // STM sample data + UBYTE patorder[128]; // Docs say 64 - actually 128 +} STMHEADER; + + +static STMNOTE *stmbuf = NULL; +static STMHEADER *mh = NULL; + +static CHAR STM_Version[] = "Screamtracker 2"; + + +BOOL STM_Test(void) +{ + UBYTE str[9],filetype; + + _mm_fseek(modfp,21,SEEK_SET); + _mm_read_UBYTES(str,9,modfp); + filetype = _mm_read_UBYTE(modfp); + if(!memcmp(str,"!SCREAM!",8) || (filetype!=2)) // STM Module = filetype 2 + return 0; + return 1; +} + + + +BOOL STM_Init(void) +{ + if(!(mh=(STMHEADER *)_mm_calloc(1,sizeof(STMHEADER)))) return 0; + if(!(stmbuf=(STMNOTE *)_mm_calloc(64U*of.numchn,sizeof(STMNOTE)))) return 0; + + return 1; +} + +void STM_Cleanup(void) +{ + if(mh!=NULL) free(mh); + if(stmbuf!=NULL) free(stmbuf); + + stmbuf = NULL; + mh = NULL; +} + + + +void STM_ConvertNote(STMNOTE *n) +{ + UBYTE note,ins,vol,cmd,inf; + + // extract the various information from the 4 bytes that + // make up a single note + + note = n->note; + ins = n->insvol>>3; + vol = (n->insvol&7)+(n->volcmd>>1); + cmd = n->volcmd&15; + inf = n->cmdinf; + + if(ins!=0 && ins<32) UniInstrument(ins-1); + + // special values of [SBYTE0] are handled here -> + // we have no idea if these strange values will ever be encountered. + // but it appears as though stms sound correct. + if(note==254 || note==252) UniPTEffect(0xc,0); // <- note off command (???) + else + // if note < 251, then all three bytes are stored in the file + if(note<251) UniNote((((note>>4)+2)*12)+(note&0xf)); // <- normal note and up the octave by two + + if(vol<65) + UniPTEffect(0xc,vol); + + if(cmd!=255){ + switch(cmd){ + + case 1: // Axx set speed to xx and add 0x1c to fix StoOoPiD STM 2.x + UniPTEffect(0xf,inf>>4); + break; + + case 2: // Bxx position jump + UniPTEffect(0xb,inf); + break; + + case 3: // Cxx patternbreak to row xx + UniPTEffect(0xd,(((inf&0xf0)>>4)*10)+(inf&0xf)); + break; + + case 4: // Dxy volumeslide + UniWrite(UNI_S3MEFFECTD); + UniWrite(inf); + break; + + case 5: // Exy toneslide down + UniWrite(UNI_S3MEFFECTE); + UniWrite(inf); + break; + + case 6: // Fxy toneslide up + UniWrite(UNI_S3MEFFECTF); + UniWrite(inf); + break; + + case 7: // Gxx Tone portamento,speed xx + UniPTEffect(0x3,inf); + break; + + case 8: // Hxy vibrato + UniPTEffect(0x4,inf); + break; + + case 9: // Ixy tremor, ontime x, offtime y + UniWrite(UNI_S3MEFFECTI); + UniWrite(inf); + break; + + case 0xa: // Jxy arpeggio + UniPTEffect(0x0,inf); + break; + + case 0xb: // Kxy Dual command H00 & Dxy + UniPTEffect(0x4,0); + UniWrite(UNI_S3MEFFECTD); + UniWrite(inf); + break; + + case 0xc: // Lxy Dual command G00 & Dxy + UniPTEffect(0x3,0); + UniWrite(UNI_S3MEFFECTD); + UniWrite(inf); + break; + + // Support all these above, since ST2 can LOAD these values + // but can actually only play up to J - and J is only + // half-way implemented in ST2 + + case 0x18: // Xxx amiga command 8xx - What the hell, support panning. :) + UniPTEffect(0x8,inf); + break; + } + } +} + + +UBYTE *STM_ConvertTrack(STMNOTE *n) +{ + int t; + + UniReset(); + for(t=0; t<64; t++) + { STM_ConvertNote(n); + UniNewline(); + n+=of.numchn; + } + return UniDup(); +} + + +BOOL STM_LoadPatterns(void) +{ + int t,s,tracks=0; + + if(!AllocPatterns()) return 0; + if(!AllocTracks()) return 0; + + // Allocate temporary buffer for loading + // and converting the patterns + + for(t=0; t<of.numpat; t++) + { for(s=0; s<(64U*of.numchn); s++) + { stmbuf[s].note = _mm_read_UBYTE(modfp); + stmbuf[s].insvol = _mm_read_UBYTE(modfp); + stmbuf[s].volcmd = _mm_read_UBYTE(modfp); + stmbuf[s].cmdinf = _mm_read_UBYTE(modfp); + } + + if(feof(modfp)) + { _mm_errno = MMERR_LOADING_PATTERN; + return 0; + } + + for(s=0;s<of.numchn;s++) + { if(!(of.tracks[tracks++]=STM_ConvertTrack(stmbuf+s))) return 0; + } + } + + return 1; +} + + + +BOOL STM_Load(void) +{ + int t; + ULONG MikMod_ISA; // We MUST generate our own ISA - NOT stored in the stm + SAMPLE *q; + + // try to read stm header + + _mm_read_string(mh->songname,20,modfp); + _mm_read_string(mh->trackername,8,modfp); + mh->unused =_mm_read_UBYTE(modfp); + mh->filetype =_mm_read_UBYTE(modfp); + mh->ver_major =_mm_read_UBYTE(modfp); + mh->ver_minor =_mm_read_UBYTE(modfp); + mh->inittempo =_mm_read_UBYTE(modfp); + mh->numpat =_mm_read_UBYTE(modfp); + mh->globalvol =_mm_read_UBYTE(modfp); + _mm_read_UBYTES(mh->reserved,13,modfp); + + for(t=0;t<31;t++) + { STMSAMPLE *s = &mh->sample[t]; // STM sample data + + _mm_read_string(s->filename,12,modfp); + s->unused =_mm_read_UBYTE(modfp); + s->instdisk =_mm_read_UBYTE(modfp); + s->reserved =_mm_read_I_UWORD(modfp); + s->length =_mm_read_I_UWORD(modfp); + s->loopbeg =_mm_read_I_UWORD(modfp); + s->loopend =_mm_read_I_UWORD(modfp); + s->volume =_mm_read_UBYTE(modfp); + s->reserved2=_mm_read_UBYTE(modfp); + s->c2spd =_mm_read_I_UWORD(modfp); + _mm_read_UBYTES(s->reserved3,4,modfp); + s->isa =_mm_read_I_UWORD(modfp); + } + _mm_read_UBYTES(mh->patorder,128,modfp); + + if(feof(modfp)) + { _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + // set module variables + + of.modtype = strdup(STM_Version); + of.songname = DupStr(mh->songname,20); // make a cstr of songname + of.numpat = mh->numpat; + of.inittempo = 125; // mh->inittempo+0x1c; + of.initspeed = mh->inittempo>>4; + of.numchn = 4; // get number of channels + + t=0; + if(!AllocPositions(0x80)) return 0; + while(mh->patorder[t]!=99) // 99 terminates the patorder list + { of.positions[t] = mh->patorder[t]; + t++; + } + of.numpos = --t; + of.numtrk = of.numpat*of.numchn; + + // Finally, init the sampleinfo structures + of.numins = of.numsmp = 31; // always this + + if(!AllocSamples()) return 0; + if(!STM_LoadPatterns()) return 0; + + q = of.samples; + + MikMod_ISA = _mm_ftell(modfp); + MikMod_ISA = (MikMod_ISA+15)&0xfffffff0; + + for(t=0; t<of.numins; t++) + { // load sample info + + q->samplename = DupStr(mh->sample[t].filename,12); + q->speed = mh->sample[t].c2spd; + q->volume = mh->sample[t].volume; + q->length = mh->sample[t].length; + if (!mh->sample[t].volume || q->length==1 ) q->length = 0; // if vol = 0 or length = 1, then no sample + q->loopstart = mh->sample[t].loopbeg; + q->loopend = mh->sample[t].loopend; + q->seekpos = MikMod_ISA; + + MikMod_ISA+=q->length; + MikMod_ISA = (MikMod_ISA+15)&0xfffffff0; + + // Once again, contrary to the STM specs, all the sample data is + // actually SIGNED! Sheesh + + q->flags = SF_SIGNED; + + if(mh->sample[t].loopend>0 && mh->sample[t].loopend!=0xffff) q->flags|=SF_LOOP; + + // fix replen if repend>length + + if(q->loopend > q->length) q->loopend = q->length; + + q++; + } + + return 1; +} + + +CHAR *STM_LoadTitle(void) +{ + CHAR s[20]; + + _mm_fseek(modfp,0,SEEK_SET); + if(!fread(s,20,1,modfp)) return NULL; + + return(DupStr(s,20)); +} + + +MLOADER load_stm = +{ NULL, + "STM", + "Portable STM Loader - V 1.2", + STM_Init, + STM_Test, + STM_Load, + STM_Cleanup, + STM_LoadTitle +}; +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/playercode/load_ult.c Fri Jan 23 16:05:08 1998 +0000 @@ -0,0 +1,292 @@ +/* + + Name: LOAD_ULT.C + + Description: + Ultratracker (ULT) module loader + + Portability: + All systems - all compilers (hopefully) + + If this module is found to not be portable to any particular platform, + please contact Jake Stine at dracoirs@epix.net (see MIKMOD.TXT for + more information on contacting the author). + +*/ + +#include <string.h> +#include "mikmod.h" + + +#define ULTS_16BITS 4 +#define ULTS_LOOP 8 +#define ULTS_REVERSE 16 + + +// Raw ULT header struct: + +typedef struct ULTHEADER +{ CHAR id[16]; + CHAR songtitle[32]; + UBYTE reserved; +} ULTHEADER; + + +// Raw ULT sampleinfo struct: + +typedef struct ULTSAMPLE +{ CHAR samplename[32]; + CHAR dosname[12]; + SLONG loopstart; + SLONG loopend; + SLONG sizestart; + SLONG sizeend; + UBYTE volume; + UBYTE flags; + SWORD finetune; +} ULTSAMPLE; + + +typedef struct ULTEVENT +{ UBYTE note,sample,eff,dat1,dat2; +} ULTEVENT; + + +CHAR *ULT_Version[]= +{ "Ultra Tracker V1.3", + "Ultra Tracker V1.4", + "Ultra Tracker V1.5", + "Ultra Tracker V1.6" +}; + + +BOOL ULT_Test(void) +{ + CHAR id[16]; + + if(!_mm_read_string(id,15,modfp)) return 0; + return(!strncmp(id,"MAS_UTrack_V00",14)); +} + + +BOOL ULT_Init(void) +{ + return 1; +} + + +void ULT_Cleanup(void) +{ +} + +ULTEVENT ev; + + + +int ReadUltEvent(ULTEVENT *event) +{ + UBYTE flag,rep=1; + + flag = _mm_read_UBYTE(modfp); + + if(flag==0xfc) + { rep = _mm_read_UBYTE(modfp); + event->note =_mm_read_UBYTE(modfp); + } else + event->note = flag; + + event->sample =_mm_read_UBYTE(modfp); + event->eff =_mm_read_UBYTE(modfp); + event->dat1 =_mm_read_UBYTE(modfp); + event->dat2 =_mm_read_UBYTE(modfp); + + return rep; +} + + +BOOL ULT_Load(void) +{ + int t,u,tracks=0; + SAMPLE *q; + ULTSAMPLE s; + ULTHEADER mh; + UBYTE nos,noc,nop; + + // try to read module header + + _mm_read_string(mh.id,15,modfp); + _mm_read_string(mh.songtitle,32,modfp); + mh.reserved = _mm_read_UBYTE(modfp); + + if(feof(modfp)) + { _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + if(mh.id[14]<'1' || mh.id[14]>'4') + { _mm_errno = MMERR_NOT_A_MODULE; + return 0; + } + + of.modtype = strdup(ULT_Version[mh.id[14]-'1']); + of.initspeed = 6; + of.inittempo = 125; + + // read songtext + + if(!ReadComment((UWORD)mh.reserved*32)) return 0; + + nos = _mm_read_UBYTE(modfp); + + if(feof(modfp)) + { _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + of.songname = DupStr(mh.songtitle,32); + of.numins = nos; + + if(!AllocSamples()) return 0; + + q = of.samples; + + for(t=0; t<nos; t++) + { // try to read sample info + + _mm_read_string(s.samplename,32,modfp); + _mm_read_string(s.dosname,12,modfp); + s.loopstart =_mm_read_I_ULONG(modfp); + s.loopend =_mm_read_I_ULONG(modfp); + s.sizestart =_mm_read_I_ULONG(modfp); + s.sizeend =_mm_read_I_ULONG(modfp); + s.volume =_mm_read_UBYTE(modfp); + s.flags =_mm_read_UBYTE(modfp); + s.finetune =_mm_read_I_SWORD(modfp); + + if(feof(modfp)) + { _mm_errno = MMERR_LOADING_SAMPLEINFO; + return 0; + } + + q->samplename = DupStr(s.samplename,32); + q->speed = 8363; + + if(mh.id[14]>='4') + { _mm_read_I_UWORD(modfp); // read 1.6 extra info(??) word + q->speed=s.finetune; + } + + q->length = s.sizeend-s.sizestart; + q->volume = s.volume>>2; + q->loopstart = s.loopstart; + q->loopend = s.loopend; + + q->flags = SF_SIGNED; + + if(s.flags&ULTS_LOOP) + q->flags|=SF_LOOP; + + if(s.flags&ULTS_16BITS) + { q->flags|=SF_16BITS; + q->loopstart>>=1; + q->loopend>>=1; + } + + q++; + } + + if(!AllocPositions(256)) return 0; + for(t=0; t<256; t++) + of.positions[t] = _mm_read_UBYTE(modfp); + for(t=0; t<256; t++) + if(of.positions[t]==255) break; + + of.numpos = t; + + noc = _mm_read_UBYTE(modfp); + nop = _mm_read_UBYTE(modfp); + + of.numchn = noc+1; + of.numpat = nop+1; + of.numtrk = of.numchn*of.numpat; + + if(!AllocTracks()) return 0; + if(!AllocPatterns()) return 0; + + for(u=0; u<of.numchn; u++) + { for(t=0; t<of.numpat; t++) + of.patterns[(t*of.numchn)+u]=tracks++; + } + + // read pan position table for v1.5 and higher + + if(mh.id[14]>='3') + for(t=0; t<of.numchn; t++) of.panning[t]=_mm_read_UBYTE(modfp)<<4; + + for(t=0; t<of.numtrk; t++) + { int rep,s,done; + + UniReset(); + done=0; + + while(done<64) + { rep=ReadUltEvent(&ev); + + if(feof(modfp)) + { _mm_errno = MMERR_LOADING_TRACK; + return 0; + } + + for(s=0; s<rep; s++) + { UBYTE eff; + + if(ev.sample) UniInstrument(ev.sample-1); + if(ev.note) UniNote(ev.note+23); + + eff = ev.eff>>4; + + // ULT panning effect fixed by Alexander Kerkhove : + + if(eff==0xc) UniPTEffect(eff,ev.dat2>>2); + else if(eff==0xb) UniPTEffect(8,ev.dat2*0xf); + else UniPTEffect(eff,ev.dat2); + + eff=ev.eff&0xf; + + if(eff==0xc) UniPTEffect(eff,ev.dat1>>2); + else if(eff==0xb) UniPTEffect(8,ev.dat1*0xf); + else UniPTEffect(eff,ev.dat1); + + UniNewline(); + done++; + } + } + if(!(of.tracks[t]=UniDup())) return 0; + } + + return 1; +} + + +CHAR *ULT_LoadTitle(void) +{ + CHAR s[32]; + + _mm_fseek(modfp,15,SEEK_SET); + if(!fread(s,32,1,modfp)) return NULL; + + return(DupStr(s,32)); +} + + +MLOADER load_ult = +{ NULL, + "ULT", + "Portable ULT loader v0.1", + ULT_Init, + ULT_Test, + ULT_Load, + ULT_Cleanup, + ULT_LoadTitle +}; +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/playercode/load_uni.c Fri Jan 23 16:05:08 1998 +0000 @@ -0,0 +1,229 @@ +/* + +Name: LOAD_UNI.C + +Description: +UNIMOD (mikmod's internal format) module loader. +Currently only supports UniMOD 06 - the internal format for MikMod 3.0. + +Portability: +All systems - all compilers (hopefully) + +*/ + +#include <string.h> +#include "mikmod.h" + + +BOOL UNI_Test(void) +{ + UBYTE id[4]; + + _mm_read_UBYTES(id,4,modfp); + if(!memcmp(id, "UN06", 4)) return 1; + + return 0; +} + + +BOOL UNI_Init(void) +{ + return 1; +} + + +void UNI_Cleanup(void) +{ +} + + +UBYTE *TrkRead(void) +{ + UBYTE *t; + UWORD len; + + len = _mm_read_M_UWORD(modfp); + t = (UBYTE *)malloc(len); + _mm_read_UBYTES(t,len,modfp); + + return t; +} + + +BOOL UNI_Load(void) +{ + int t,v,w; + INSTRUMENT *i; + SAMPLE *s; + + // UNI format version 3.0 (#6) + + of.modtype = strdup("MikMod UniFormat 3.0"); + + _mm_fseek(modfp,5,SEEK_SET); // skip the header + + of.flags = _mm_read_M_UWORD(modfp); + of.numchn = _mm_read_UBYTE(modfp); + of.numvoices = _mm_read_UBYTE(modfp); + of.numpos = _mm_read_M_UWORD(modfp); + of.numpat = _mm_read_M_UWORD(modfp); + of.numtrk = _mm_read_M_UWORD(modfp); + of.numins = _mm_read_M_UWORD(modfp); + of.numsmp = _mm_read_M_UWORD(modfp); + of.reppos = _mm_read_M_UWORD(modfp); + of.initspeed = _mm_read_UBYTE(modfp); + of.inittempo = _mm_read_UBYTE(modfp); + of.initvolume = _mm_read_UBYTE(modfp); + + if(feof(modfp)) + { _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + of.songname = StringRead(modfp); + of.composer = StringRead(modfp); + of.comment = StringRead(modfp); + + if(feof(modfp)) + { _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + if(!AllocSamples()) return 0; + if(!AllocTracks()) return 0; + if(!AllocPatterns()) return 0; + if(!AllocPositions(of.numpos)) return 0; + + _mm_read_UBYTES(of.positions,of.numpos,modfp); + _mm_read_M_UWORDS(of.panning,of.numchn,modfp); + _mm_read_UBYTES(of.chanvol,of.numchn,modfp); + + + // Load sample headers + + s = of.samples; + for(v=0; v<of.numsmp; v++, s++) + { s->flags = _mm_read_M_UWORD(modfp); + s->speed = _mm_read_M_ULONG(modfp); + s->volume = _mm_read_UBYTE(modfp); + s->panning = _mm_read_M_UWORD(modfp); + s->length = _mm_read_M_ULONG(modfp); + s->loopstart = _mm_read_M_ULONG(modfp); + s->loopend = _mm_read_M_ULONG(modfp); + s->susbegin = _mm_read_M_ULONG(modfp); + s->susend = _mm_read_M_ULONG(modfp); + + s->globvol = _mm_read_UBYTE(modfp); + s->vibflags = _mm_read_UBYTE(modfp); + s->vibtype = _mm_read_UBYTE(modfp); + s->vibsweep = _mm_read_UBYTE(modfp); + s->vibdepth = _mm_read_UBYTE(modfp); + s->vibrate = _mm_read_UBYTE(modfp); + s->samplename = StringRead(modfp); + + if(feof(modfp)) + { _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + } + + // Load instruments + + if(of.flags & UF_INST) + { if(!AllocInstruments()) return 0; + i = of.instruments; + + for(v=0; v<of.numins; v++, i++) + { i->flags = _mm_read_UBYTE(modfp); + i->nnatype = _mm_read_UBYTE(modfp); + i->dca = _mm_read_UBYTE(modfp); + i->dct = _mm_read_UBYTE(modfp); + i->globvol = _mm_read_UBYTE(modfp); + i->panning = _mm_read_M_UWORD(modfp); + i->pitpansep = _mm_read_UBYTE(modfp); + i->pitpancenter = _mm_read_UBYTE(modfp); + i->rvolvar = _mm_read_UBYTE(modfp); + i->rpanvar = _mm_read_UBYTE(modfp); + + i->volfade = _mm_read_M_UWORD(modfp); + + i->volflg = _mm_read_UBYTE(modfp); + i->volpts = _mm_read_UBYTE(modfp); + i->volsusbeg = _mm_read_UBYTE(modfp); + i->volsusend = _mm_read_UBYTE(modfp); + i->volbeg = _mm_read_UBYTE(modfp); + i->volend = _mm_read_UBYTE(modfp); + + for(w=0; w<i->volpts; w++) + { i->volenv[w].pos = _mm_read_M_SWORD(modfp); + i->volenv[w].val = _mm_read_M_SWORD(modfp); + } + + i->panflg = _mm_read_UBYTE(modfp); + i->panpts = _mm_read_UBYTE(modfp); + i->pansusbeg = _mm_read_UBYTE(modfp); + i->pansusend = _mm_read_UBYTE(modfp); + i->panbeg = _mm_read_UBYTE(modfp); + i->panend = _mm_read_UBYTE(modfp); + + for(w=0; w<i->panpts; w++) + { i->panenv[w].pos = _mm_read_M_SWORD(modfp); + i->panenv[w].val = _mm_read_M_SWORD(modfp); + } + + i->pitflg = _mm_read_UBYTE(modfp); + i->pitpts = _mm_read_UBYTE(modfp); + i->pitsusbeg = _mm_read_UBYTE(modfp); + i->pitsusend = _mm_read_UBYTE(modfp); + i->pitbeg = _mm_read_UBYTE(modfp); + i->pitend = _mm_read_UBYTE(modfp); + + for(w=0; w<i->pitpts; w++) + { i->pitenv[w].pos = _mm_read_M_SWORD(modfp); + i->pitenv[w].val = _mm_read_M_SWORD(modfp); + } + + _mm_read_UBYTES(i->samplenumber, 120, modfp); + _mm_read_UBYTES(i->samplenote, 120, modfp); + + i->insname = StringRead(modfp); + + if(feof(modfp)) + { _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + } + } + + // Read patterns + + _mm_read_M_UWORDS(of.pattrows,of.numpat,modfp); + _mm_read_M_UWORDS(of.patterns,of.numpat*of.numchn,modfp); + + // Read tracks + + for(t=0; t<of.numtrk; t++) + of.tracks[t] = TrkRead(); + + return 1; +} + + +CHAR *UNI_LoadTitle(void) +{ + _mm_fseek(modfp,24,SEEK_SET); + return(StringRead(modfp)); +} + + +MLOADER load_uni = +{ NULL, + "UNI", + "Portable UniFormat 3.0 Loader", + UNI_Init, + UNI_Test, + UNI_Load, + UNI_Cleanup, + UNI_LoadTitle +}; +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/playercode/load_wav.c Fri Jan 23 16:05:08 1998 +0000 @@ -0,0 +1,97 @@ +/* + + Name: LOAD_WAV.C + + Description: + WAV Streaming Audio Loader / Player + + Portability: + All compilers -- All systems (hopefully) + +*/ + +#include "mikmod.h" +#include <string.h> + + +typedef struct +{ CHAR rID[4]; + ULONG rLen; + CHAR wID[4]; + CHAR fID[4]; + ULONG fLen; + UWORD wFormatTag; + UWORD nChannels; + ULONG nSamplesPerSec; + ULONG nAvgBytesPerSec; + UWORD nBlockAlign; + UWORD nFormatSpecific; +} WAV; + + +BOOL WAV_Load(void) +{ + SAMPLE *si; + static WAV wh; + static CHAR dID[4]; + + // read wav header + + _mm_read_string(wh.rID,4,stream_fp); + wh.rLen = _mm_read_I_ULONG(stream_fp); + _mm_read_string(wh.wID,4,stream_fp); + _mm_read_string(wh.fID,4,stream_fp); + wh.fLen = _mm_read_I_ULONG(stream_fp); + wh.wFormatTag = _mm_read_I_UWORD(stream_fp); + wh.nChannels = _mm_read_I_UWORD(stream_fp); + wh.nSamplesPerSec = _mm_read_I_ULONG(stream_fp); + wh.nAvgBytesPerSec = _mm_read_I_ULONG(stream_fp); + wh.nBlockAlign = _mm_read_I_UWORD(stream_fp); + wh.nFormatSpecific = _mm_read_I_UWORD(stream_fp); + + // check it + + if( feof(stream_fp) || + memcmp(wh.rID,"RIFF",4) || + memcmp(wh.wID,"WAVE",4) || + memcmp(wh.fID,"fmt ",4) ) + { + _mm_errno = MMERR_UNKNOWN_WAVE_TYPE; + return NULL; + } + + // skip other crap + + _mm_fseek(stream_fp,wh.fLen-16,SEEK_CUR); + _mm_read_string(dID,4,stream_fp); + + if( memcmp(dID,"data",4) ) + { _mm_errno = MMERR_UNKNOWN_WAVE_TYPE; + return NULL; + } + + if(wh.nChannels > 1) + { _mm_errno = MMERR_UNKNOWN_WAVE_TYPE; + return NULL; + } + +// printf("wFormatTag: %x\n",wh.wFormatTag); +// printf("blockalign: %x\n",wh.nBlockAlign); +// prinff("nFormatSpc: %x\n",wh.nFormatSpecific); + + if((si=(SAMPLE *)_mm_calloc(1,sizeof(SAMPLE)))==NULL) return NULL; + + si->speed = wh.nSamplesPerSec/wh.nChannels; + si->volume = 64; + + si->length = _mm_read_I_ULONG(stream_fp); + + if(wh.nBlockAlign==2) + { si->flags = SF_16BITS | SF_SIGNED; + si->length>>=1; + } + + return 0; +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/playercode/load_xm.c Fri Jan 23 16:05:08 1998 +0000 @@ -0,0 +1,679 @@ +/* + + Name: LOAD_XM.C + + Description: + Fasttracker (XM) module loader + + Portability: + All systems - all compilers (hopefully) + + If this module is found to not be portable to any particular platform, + please contact Jake Stine at dracoirs@epix.net (see MIKMOD.TXT for + more information on contacting the author). + +*/ + +#include <string.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 + UWORD volenv[24]; // (byte) Points for volume envelope + UWORD panenv[24]; // (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 + + UBYTE vibtype; // (byte) Vibrato type + UBYTE vibsweep; // (byte) Vibrato sweep + UBYTE vibdepth; // (byte) Vibrato depth + UBYTE vibrate; // (byte) Vibrato rate +} XMWAVHEADER; + + +typedef struct XMPATHEADE +{ 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; + +/************************************************************************** +**************************************************************************/ + +static XMNOTE *xmpat = NULL; +static XMHEADER *mh = NULL; + +BOOL XM_Test(void) +{ + UBYTE id[17]; + + if(!_mm_read_UBYTES(id,17,modfp)) return 0; + if(!memcmp(id,"Extended Module: ",17)) return 1; + return 0; +} + + +BOOL XM_Init(void) +{ + if(!(mh=(XMHEADER *)_mm_calloc(1,sizeof(XMHEADER)))) return 0; + return 1; +} + + +void XM_Cleanup(void) +{ + if(mh!=NULL) 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!=0) + { if(note==97) + { UniWrite(UNI_KEYFADE); + UniWrite(0); + } else + UniNote(note-1); + } + + if(ins!=0) 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 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); + } + + switch(eff) + { + case 0x4: // Effect 4: Vibrato + UniWrite(UNI_XMEFFECT4); + UniWrite(dat); + break; + + case 0xa: + UniWrite(UNI_XMEFFECTA); + UniWrite(dat); + break; + + case 0xe: + 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 + if(dat>64) dat = 64; + UniWrite(UNI_XMEFFECTG); + UniWrite(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 !! (== S3M effect I) + UniWrite(UNI_S3MEFFECTI); + UniWrite(dat); + break; + + case 'X'-55: + if((dat>>4) == 1) // X1 - Extra Fine Porta up + { UniWrite(UNI_XMEFFECTX1); + UniWrite(dat & 0xf); + } else if((dat>>4) == 2) // X2 - Extra Fine Porta down + { UniWrite(UNI_XMEFFECTX2); + UniWrite(dat & 0xf); + } + break; + + default: + if(eff <= 0xf) + { // Convert pattern jump from Dec to Hex + if(eff == 0xd) + dat = (((dat&0xf0)>>4)*10)+(dat&0xf); + UniPTEffect(eff,dat); + } + break; + } + + UniNewline(); + xmtrack++; + } + return UniDup(); +} + + + +BOOL XM_Load(void) +{ + INSTRUMENT *d; + SAMPLE *q; + XMWAVHEADER *wh,*s; + int t,u,v,p,numtrk; + long next; + ULONG nextwav[256]; + 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); + 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)) + { _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); // make a cstr of songname + of.numpos = mh->songlength; // copy the songlength + of.reppos = mh->restart; + 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+3)) return 0; + for(t=0; t<of.numpos; t++) + of.positions[t] = mh->orders[t]; + +/* + WHY THIS CODE HERE?? I CAN'T REMEMBER! + + Well, I do know why, mikmak! Seems that FT2 doesn't always count blank + patterns AT ALL if they are at the END of the song. So, we have to check + for any patter numbers in the order list greater than the number of pat- + terns total. If one or more is found, we set it equal to the pattern total + and make a dummy pattern to accomidate for the discrepency! +*/ + + 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); + ph.numrows =_mm_read_I_UWORD(modfp); + ph.packsize =_mm_read_I_UWORD(modfp); + + 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=(XMNOTE *)_mm_calloc(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]); + } + } + + if(feof(modfp)) + { _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); + } + + 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; + if((wh = (XMWAVHEADER *)_mm_calloc(256,sizeof(XMWAVHEADER))) == NULL) return 0; + d = of.instruments; + s = wh; + + + for(t=0; t<of.numins; t++) + { XMINSTHEADER ih; + int headend; + + memset(d->samplenumber,255,120); + + // 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(ih.size > 29) + { ih.ssize = _mm_read_I_ULONG(modfp); + if(ih.numsmp > 0) + { XMPATCHHEADER pth; + + _mm_read_UBYTES (pth.what, 96, modfp); + _mm_read_I_UWORDS (pth.volenv, 24, modfp); + _mm_read_I_UWORDS (pth.panenv, 24, 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)) + { _mm_errno = MMERR_LOADING_SAMPLEINFO; + return 0; + } + + for(u=0; u<96; u++) + d->samplenumber[u] = pth.what[u] + of.numsmp; + + d->volfade = pth.volfade; + + memcpy(d->volenv,pth.volenv,24); + 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<12; p++) + d->volenv[p].val <<= 2; + + if((d->volflg & EF_ON) && (d->volpts < 2)) + d->volflg &= ~EF_ON; + + memcpy(d->panenv,pth.panenv,24); + 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<12; 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++) + { 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)) + { _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++; + } + + if(!AllocSamples()) 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; + q->flags|=SF_SIGNED; + } + + d = of.instruments; + s = wh; + for(u=0; u<of.numins; u++, d++) + { /*for(t=0; t<3; t++) + if((s[d->samplenumber[t]].relnote / 12) > ) + { s[d->samplenumber[t]].relnote -= 12; + of.samples[d->samplenumber[t]].speed <<= 1; + } + */ + for(t=0; t<96; t++) + d->samplenote[t] = (d->samplenumber[t]==of.numsmp) ? 255 : (t+s[d->samplenumber[t]].relnote); + } + + free(wh); + 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)); +} + + + +MLOADER load_xm = +{ NULL, + "XM", + "Portable XM loader v0.5", + XM_Init, + XM_Test, + XM_Load, + XM_Cleanup, + XM_LoadTitle +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/playercode/mloader.c Fri Jan 23 16:05:08 1998 +0000 @@ -0,0 +1,399 @@ +/* + +Name: MLOADER.C + +Description: + These routines are used to access the available module loaders + +Portability: + All systems - all compilers + +*/ + +#include <string.h> +#include "mikmod.h" + + +FILE *modfp; +UNIMOD of; + +static MLOADER *firstloader = NULL; + +UWORD finetune[16] = +{ 8363, 8413, 8463, 8529, 8581, 8651, 8723, 8757, + 7895, 7941, 7985, 8046, 8107, 8169, 8232, 8280 +}; + + +void ML_InfoLoader(void) +{ + int t; + MLOADER *l; + + // list all registered devicedrivers: + + for(t=1,l=firstloader; l!=NULL; l=l->next, t++) + printf("%d. %s\n",t,l->version); +} + + +void ML_RegisterLoader(MLOADER *ldr) +{ + MLOADER *cruise = firstloader; + + if(cruise!=NULL) + { while(cruise->next!=NULL) cruise = cruise->next; + cruise->next = ldr; + } else + firstloader = ldr; +} + + +BOOL ReadComment(UWORD len) +{ + //int t; + + if(len) + { if(!(of.comment=(CHAR *)_mm_malloc(len+1))) return 0; + fread(of.comment,len,1,modfp); + of.comment[len] = 0; + } + return 1; +} + + +BOOL AllocPositions(int total) +{ + if((of.positions = _mm_calloc(total,sizeof(UWORD))) == NULL) return 0; + return 1; +} + + +BOOL AllocPatterns(void) +{ + int s,t,tracks = 0; + + // Allocate track sequencing array + + if(!(of.patterns = (UWORD *)_mm_calloc((ULONG)(of.numpat+1)*of.numchn,sizeof(UWORD)))) return 0; + if(!(of.pattrows = (UWORD *)_mm_calloc(of.numpat+1,sizeof(UWORD)))) return 0; + + for(t=0; t<of.numpat+1; t++) + { of.pattrows[t] = 64; + for(s=0; s<of.numchn; s++) + of.patterns[(t*of.numchn)+s] = tracks++; + } + + return 1; +} + + +BOOL AllocTracks(void) +{ + if(!(of.tracks=(UBYTE **)_mm_calloc(of.numtrk,sizeof(UBYTE *)))) return 0; + return 1; +} + + +BOOL AllocInstruments(void) +{ + int t,n; + + if((of.instruments=(INSTRUMENT *)_mm_calloc(of.numins,sizeof(INSTRUMENT)))==NULL) return 0; + + for(t=0; t<of.numins; t++) + { for(n=0; n<120; n++) // Init note / sample lookup table + { of.instruments[t].samplenote[n] = n; + of.instruments[t].samplenumber[n] = t; + } + of.instruments[t].globvol = 64; + } + return 1; +} + + +BOOL AllocSamples(void) +{ + UWORD u; + + if((of.samples = (SAMPLE *)_mm_calloc(of.numsmp,sizeof(SAMPLE)))==NULL) return 0; + + for(u=0; u<of.numsmp; u++) + { of.samples[u].panning = 128; + of.samples[u].handle = -1; + of.samples[u].globvol = 64; + of.samples[u].volume = 64; + } + return 1; +} + + +BOOL ML_LoadSamples(void) +{ + SAMPLE *s; + int u; + + for(u=of.numsmp, s=of.samples; u; u--, s++) + if(s->length) SL_RegisterSample(s,MD_MUSIC,modfp); + + return 1; +} + + +CHAR *DupStr(CHAR *s, UWORD len) +// Creates a CSTR out of a character buffer of 'len' bytes, but strips +// any terminating non-printing characters like 0, spaces etc. +{ + UWORD t; + CHAR *d = NULL; + + // Scan for first printing char in buffer [includes high ascii up to 254] + while(len) + { if(s[len-1] > 0x20) break; + len--; + } + + // When the buffer wasn't completely empty, allocate + // a cstring and copy the buffer into that string, except + // for any control-chars + +#ifdef __GNUC__ + if(len<16) len = 16; +#endif + + if((d=(CHAR *)_mm_malloc(len+1)) != NULL) + { for(t=0; t<len; t++) d[t] = (s[t]<32) ? ' ' : s[t]; + d[t] = 0; + } + + return d; +} + + +static void ML_XFreeSample(SAMPLE *s) +{ + if(s->handle>=0) + MD_SampleUnLoad(s->handle); + if(s->samplename!=NULL) free(s->samplename); +} + + +static void ML_XFreeInstrument(INSTRUMENT *i) +{ + if(i->insname!=NULL) free(i->insname); +} + + +static void ML_FreeEx(UNIMOD *mf) +{ + UWORD t; + + if(mf->songname!=NULL) free(mf->songname); + if(mf->composer!=NULL) free(mf->composer); + if(mf->comment!=NULL) free(mf->comment); + + if(mf->modtype!=NULL) free(mf->modtype); + if(mf->positions!=NULL) free(mf->positions); + if(mf->patterns!=NULL) free(mf->patterns); + if(mf->pattrows!=NULL) free(mf->pattrows); + + if(mf->tracks!=NULL) + { for(t=0; t<mf->numtrk; t++) + if(mf->tracks[t]!=NULL) free(mf->tracks[t]); + free(mf->tracks); + } + + if(mf->instruments != NULL) + { for(t=0; t<mf->numins; t++) + ML_XFreeInstrument(&mf->instruments[t]); + free(mf->instruments); + } + + if(mf->samples != NULL) + { for(t=0; t<mf->numsmp; t++) + if(mf->samples[t].length) ML_XFreeSample(&mf->samples[t]); + free(mf->samples); + } + + memset(mf,0,sizeof(UNIMOD)); +} + + +static UNIMOD *ML_AllocUniMod(void) +{ + UNIMOD *mf; + + if((mf=_mm_calloc(1,sizeof(UNIMOD))) == NULL) return NULL; + return mf; +} + + +/****************************************** + + Next are the user-callable functions + +******************************************/ + + +void MikMod_FreeSong(UNIMOD *mf) +{ + if(mf!=NULL) + { Player_Exit(mf); + ML_FreeEx(mf); + } +} + + +CHAR *MikMod_LoadSongTitle(CHAR *filename) +{ + MLOADER *l; + CHAR *retval; + FILE *fp; + + if((fp = _mm_fopen(filename,"rb"))==NULL) return NULL; + + _mm_errno = 0; + _mm_critical = 0; + _mm_iobase_setcur(modfp); + + // Try to find a loader that recognizes the module + + for(l=firstloader; l!=NULL; l=l->next) + { _mm_rewind(modfp); + if(l->Test()) break; + } + + if(l==NULL) + { _mm_errno = MMERR_NOT_A_MODULE; + _mm_iobase_revert(); + if(_mm_errorhandler!=NULL) _mm_errorhandler(); + return NULL; + } + + retval = l->LoadTitle(); + + fclose(fp); + return(retval); +} + + +UNIMOD *MikMod_LoadSongFP(FILE *fp, int maxchan) + +// Loads a module given a file pointer. +// File is loaded from the current file seek position. + +{ + int t; + MLOADER *l; + BOOL ok; + UNIMOD *mf; + + modfp = fp; + _mm_errno = 0; + _mm_critical = 0; + + _mm_iobase_setcur(modfp); + + // Try to find a loader that recognizes the module + + for(l=firstloader; l!=NULL; l=l->next) + { _mm_rewind(modfp); + if(l->Test()) break; + } + + if(l==NULL) + { _mm_errno = MMERR_NOT_A_MODULE; + _mm_iobase_revert(); + if(_mm_errorhandler!=NULL) _mm_errorhandler(); + return NULL; + } + + // init unitrk routines + if(!UniInit()) + { if(_mm_errorhandler!=NULL) _mm_errorhandler(); + return NULL; + } + + // load the song using the song's loader variable + memset(&of,0,sizeof(UNIMOD)); + of.initvolume = 128; + + // init panning array + for(t=0; t<64; t++) of.panning[t] = ((t+1)&2) ? 255 : 0; + for(t=0; t<64; t++) of.chanvol[t] = 64; + + // init module loader and load the header / patterns + if(l->Init()) + { _mm_rewind(modfp); + ok = l->Load(); + } else ok = 0; + + // free loader and unitrk allocations + l->Cleanup(); + UniCleanup(); + + if(!ok) + { ML_FreeEx(&of); + _mm_iobase_revert(); + if(_mm_errorhandler!=NULL) _mm_errorhandler(); + return NULL; + } + + if(!ML_LoadSamples()) + { ML_FreeEx(&of); + _mm_iobase_revert(); + if(_mm_errorhandler!=NULL) _mm_errorhandler(); + return NULL; + } + + if((mf=ML_AllocUniMod()) == NULL) + { ML_FreeEx(&of); + _mm_iobase_revert(); + if(_mm_errorhandler!=NULL) _mm_errorhandler(); + return NULL; + } + + // Copy the static UNIMOD contents into the dynamic UNIMOD struct. + memcpy(mf,&of,sizeof(UNIMOD)); + + _mm_iobase_revert(); + + if(maxchan > 0) + { if(!(mf->flags & UF_NNA) && (mf->numchn < maxchan)) + maxchan = mf->numchn; + else if((mf->numvoices!=0) && mf->numvoices < maxchan) + maxchan = mf->numvoices; + + if(maxchan < mf->numchn) mf->flags |= UF_NNA; + + if(MikMod_SetNumVoices(maxchan,-1)) + { MikMod_FreeSong(mf); + return NULL; + } + } + + return mf; +} + + +UNIMOD *MikMod_LoadSong(CHAR *filename, int maxchan) + +// Open a module via it's filename. The loader will initialize the specified +// song-player 'player'. + +{ + FILE *fp; + UNIMOD *mf; + + if((fp = _mm_fopen(filename,"rb"))==NULL) return NULL; + if((mf = MikMod_LoadSongFP(fp, maxchan)) != NULL) + { if(SL_LoadSamples() || Player_Init(mf)) + { MikMod_FreeSong(mf); + mf = NULL; + } + } + + fclose(fp); + return mf; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/playercode/stream.c Fri Jan 23 16:05:08 1998 +0000 @@ -0,0 +1,134 @@ +/* + File: STREAM.C + + Description: + Streaming Audio module for MikMod. + + Portability: + All compilers - All systems + +*/ + + +#include "mikmod.h" + + +int stream_bufsize = 100; // in 1/100th seconds + +static BOOL is_streaming = 0; +static MSTREAM *firststream = NULL; + +static BOOL active; + +FILE *stream_fp; +SLONG stream_seekpos; +SLONG stream_reppos; + +void MikMod_RegisterStream(MSTREAM *stream) +{ + MSTREAM *cruise = firststream; + + if(cruise!=NULL) + { while(cruise->next!=NULL) cruise = cruise->next; + cruise->next = stream; + } else + firststream = stream; +} + + +BOOL Stream_PlayFN(CHAR *filename) +{ + return 0; +} + + +BOOL Stream_PlayFP(FILE *fp) +{ + BOOL ok; + MSTREAM *l; + + stream_fp = fp; + _mm_errno = 0; + _mm_critical = 0; + + _mm_iobase_setcur(stream_fp); + + // Try to find a loader that recognizes the stream + + for(l=firststream; l!=NULL; l=l->next) + { _mm_rewind(stream_fp); + if(l->Test()) break; + } + + if(l==NULL) + { _mm_errno = MMERR_NOT_A_STREAM; + if(_mm_errorhandler!=NULL) _mm_errorhandler(); + _mm_iobase_revert(); + return 1; + } + + // init stream loader and load the header + if(l->Init()) + { _mm_rewind(stream_fp); + ok = l->Load(); + } + + // free loader allocations + l->Cleanup(); + + if(!ok) + { _mm_iobase_revert(); + return 1; + } + + //loadbuf = _mm_malloc(8192); + + _mm_iobase_revert(); + active = 1; + return 0; +} + + +BOOL Stream_Init(ULONG speed, UWORD flags) + +// size = size of buffer in 1/100th seconds + +{ + if(is_streaming) md_driver->StreamExit(); + if(md_driver->StreamInit(speed,flags)) return 1; + is_streaming = 1; + + return 0; +} + + +void Stream_Exit(void) +{ + if(is_streaming) md_driver->StreamExit(); + is_streaming = 0; +} + + +static ULONG last = 0; + +void Stream_Update(void) +{ + ULONG curr; + ULONG todo; + + curr = md_driver->StreamGetPosition(); + if(curr==last) return; + + if(curr>last) + { todo = curr-last; + //SL_LoadStream(,vstream.infmt,vstream.flags,todo,stream_fp); + last += todo; + //if(last>=vstream.size) last = 0; + } else + { //todo = vstream.size-last; + //SL_LoadStream(&vstream.buffer[last],vstream.infmt,vstream.flags,todo,stream_fp); + //SL_LoadStream(vstream.buffer,vstream.infmt,vstream.flags,curr,stream_fp); + last = curr; + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/playercode/virtch2.c Fri Jan 23 16:05:08 1998 +0000 @@ -0,0 +1,927 @@ +/* + +Name: VIRTCH2.C + +Description: + All-c sample mixing routines, using a 32 bits mixing buffer, interpolation, + and sample smoothing [improves sound quality and removes clicks]. + + Future Additions: + Low-Pass filter to remove annoying staticy buzz. + + +C Mixer Portability: + All Systems -- All compilers. + +Assembly Mixer Portability: + + MSDOS: BC(?) Watcom(y) DJGPP(y) + Win95: ? + Os2: ? + Linux: y + + (y) - yes + (n) - no (not possible or not useful) + (?) - may be possible, but not tested + +*/ + +#include <stddef.h> +#include <string.h> +#include "mikmod.h" + + +// REVERBERATION : Larger numbers result in shorter reverb duration. +// Longer reverb durations can cause unwanted static and make the +// reverb sound more like an echo. + +#define REVERBERATION 28000l + +// SAMPLING_SHIFT : Specified the shift multiplier which controls by how +// much the mixing rate is multiplied while mixing. Higher values +// can improve quality by smoothing the soudn and reducing pops and +// clicks. Note, this is a shift value, so a value of 2 becomes a +// mixing-rate multiplier of 4, and a value of 3 = 8, etc. + +#define SAMPLING_SHIFT 2 +#define SAMPLING_FACTOR (SLONG)(1<<SAMPLING_SHIFT) + + +// FRACBITS : the number of bits per integer devoted to the fractional +// part of the number. This value HAS to be changed if the compiler +// does not support 64 bit integers. If 32 bit integers are used, +// FRACBITS _must_ be 9 or smaller. + +#define FRACBITS 28 +#define FRACMASK ((1l<<FRACBITS)-1) + +#define TICKLSIZE 4096 +#define TICKWSIZE (TICKLSIZE*2) +#define TICKBSIZE (TICKWSIZE*2) + +#ifndef MIN +#define MIN(a,b) (((a)<(b)) ? (a) : (b)) +#endif + +#ifndef MAX +#define MAX(a,b) (((a)>(b))?(a):(b)) +#endif + + +typedef struct +{ UBYTE kick; // =1 -> sample has to be restarted + UBYTE active; // =1 -> sample is playing + UWORD flags; // 16/8 bits looping/one-shot + SWORD handle; // identifies the sample + ULONG start; // start index + ULONG size; // samplesize + ULONG reppos; // loop start + ULONG repend; // loop end + ULONG frq; // current frequency + UWORD vol; // current volume + UWORD pan; // current panning position + SDOUBLE current; // current index in the sample + SDOUBLE increment; // fixed-point increment value +} VINFO; + + +static SWORD **Samples; + +static VINFO *vinf = NULL; +static VINFO *vnf; +static ULONG samplesthatfit; +static int vc_memory, vc_softchn; +static SDOUBLE idxsize,idxlpos,idxlend; +static SLONG TICKLEFT; +static SLONG *VC2_TICKBUF = NULL; +static UWORD vc_mode; +static int bitshift; // Amplification shift (amount to decrease 32 bit mixing buffer by!) +static SLONG lvolsel, rvolsel; // Volume factor .. range 0-255 + + +// Reverb control variables +// ======================== + +static int RVc1, RVc2, RVc3, RVc4, RVc5, RVc6, RVc7, RVc8; +static ULONG RVRindex; + +// For Mono or Left Channel +static SLONG *RVbuf1 = NULL, *RVbuf2 = NULL, *RVbuf3 = NULL, + *RVbuf4 = NULL, *RVbuf5 = NULL, *RVbuf6 = NULL, + *RVbuf7 = NULL, *RVbuf8 = NULL; + +// For Stereo only (Right Channel) +static SLONG *RVbuf9 = NULL, *RVbuf10 = NULL, *RVbuf11 = NULL, + *RVbuf12 = NULL, *RVbuf13 = NULL, *RVbuf14 = NULL, + *RVbuf15 = NULL, *RVbuf16 = NULL; + + +// ============================================================== +// 16 bit sample mixers! + +static SDOUBLE MixStereoNormal(SWORD *srce, SLONG *dest, SDOUBLE index, SDOUBLE increment, ULONG todo) +{ + SWORD sample; + + for(; todo; todo--) + { sample = (index & FRACBITS) ? + (((srce[index >> FRACBITS] * (FRACBITS - (index & FRACBITS))) + + (srce[(index >> FRACBITS)+1] * (index & FRACBITS))) / FRACBITS) + : srce[index >> FRACBITS]; + index += increment; + + *dest++ += lvolsel * sample; + *dest++ += rvolsel * sample; + } + + return index; +} + + +static SDOUBLE MixStereoSurround(SWORD *srce, SLONG *dest, SDOUBLE index, SDOUBLE increment, ULONG todo) +{ + SWORD sample; + + for(dest--; todo; todo--) + { sample = (index & FRACBITS) ? + (((srce[index >> FRACBITS] * (FRACBITS - (index & FRACBITS))) + + (srce[(index >> FRACBITS)+1] * (index & FRACBITS))) / FRACBITS) + : srce[index >> FRACBITS]; + index += increment; + + *dest++ += lvolsel * sample; + *dest++ -= lvolsel * sample; + } + + return index; +} + + +static SDOUBLE MixMonoNormal(SWORD *srce, SLONG *dest, SDOUBLE index, SDOUBLE increment, SLONG todo) +{ + SWORD sample; + + for(; todo; todo--) + { sample = (index & FRACBITS) ? + (((srce[index >> FRACBITS] * (FRACBITS - (index & FRACBITS))) + + (srce[(index >> FRACBITS)+1] * (index & FRACBITS))) / FRACBITS) + : srce[index >> FRACBITS]; + index += increment; + + *dest++ += lvolsel * sample; + } + + return index; +} + + +static void (*Mix32to16)(SWORD *dste, SLONG *srce, SLONG count); +static void (*Mix32to8)(SBYTE *dste, SLONG *srce, SLONG count); +static void (*MixReverb)(SLONG *srce, SLONG count); + + +static void MixReverb_Normal(SLONG *srce, SLONG count) +{ + SLONG speedup; + int ReverbPct; + unsigned int loc1, loc2, loc3, loc4, + loc5, loc6, loc7, loc8; + + ReverbPct = 63 + (md_reverb*4); + + loc1 = RVRindex % RVc1; + loc2 = RVRindex % RVc2; + loc3 = RVRindex % RVc3; + loc4 = RVRindex % RVc4; + loc5 = RVRindex % RVc5; + loc6 = RVRindex % RVc6; + loc7 = RVRindex % RVc7; + loc8 = RVRindex % RVc8; + + for(; count; count--) + { + // Compute the LEFT CHANNEL echo buffers! + + speedup = *srce >> 3; + + RVbuf1[loc1] = speedup + ((ReverbPct * RVbuf1[loc1]) / 128); + RVbuf2[loc2] = speedup + ((ReverbPct * RVbuf2[loc2]) / 128); + RVbuf3[loc3] = speedup + ((ReverbPct * RVbuf3[loc3]) / 128); + RVbuf4[loc4] = speedup + ((ReverbPct * RVbuf4[loc4]) / 128); + RVbuf5[loc5] = speedup + ((ReverbPct * RVbuf5[loc5]) / 128); + RVbuf6[loc6] = speedup + ((ReverbPct * RVbuf6[loc6]) / 128); + RVbuf7[loc7] = speedup + ((ReverbPct * RVbuf7[loc7]) / 128); + RVbuf8[loc8] = speedup + ((ReverbPct * RVbuf8[loc8]) / 128); + + // Prepare to compute actual finalized data! + + RVRindex++; + loc1 = RVRindex % RVc1; + loc2 = RVRindex % RVc2; + loc3 = RVRindex % RVc3; + loc4 = RVRindex % RVc4; + loc5 = RVRindex % RVc5; + loc6 = RVRindex % RVc6; + loc7 = RVRindex % RVc7; + loc8 = RVRindex % RVc8; + + // Left Channel! + + *srce++ += (RVbuf1[loc1] - RVbuf2[loc2] + RVbuf3[loc3] - RVbuf4[loc4] + + RVbuf5[loc5] - RVbuf6[loc6] + RVbuf7[loc7] - RVbuf8[loc8]); + } +} + + +static void MixReverb_Stereo(SLONG *srce, SLONG count) +{ + SLONG speedup; + int ReverbPct; + unsigned int loc1, loc2, loc3, loc4, + loc5, loc6, loc7, loc8; + + ReverbPct = 63 + (md_reverb*4); + + loc1 = RVRindex % RVc1; + loc2 = RVRindex % RVc2; + loc3 = RVRindex % RVc3; + loc4 = RVRindex % RVc4; + loc5 = RVRindex % RVc5; + loc6 = RVRindex % RVc6; + loc7 = RVRindex % RVc7; + loc8 = RVRindex % RVc8; + + for(; count; count--) + { + // Compute the LEFT CHANNEL echo buffers! + + speedup = *srce >> 3; + + RVbuf1[loc1] = speedup + ((ReverbPct * RVbuf1[loc1]) / 128); + RVbuf2[loc2] = speedup + ((ReverbPct * RVbuf2[loc2]) / 128); + RVbuf3[loc3] = speedup + ((ReverbPct * RVbuf3[loc3]) / 128); + RVbuf4[loc4] = speedup + ((ReverbPct * RVbuf4[loc4]) / 128); + RVbuf5[loc5] = speedup + ((ReverbPct * RVbuf5[loc5]) / 128); + RVbuf6[loc6] = speedup + ((ReverbPct * RVbuf6[loc6]) / 128); + RVbuf7[loc7] = speedup + ((ReverbPct * RVbuf7[loc7]) / 128); + RVbuf8[loc8] = speedup + ((ReverbPct * RVbuf8[loc8]) / 128); + + // Compute the RIGHT CHANNEL echo buffers! + + speedup = srce[1] >> 3; + + RVbuf9[loc1] = speedup + ((ReverbPct * RVbuf9[loc1]) / 128); + RVbuf10[loc2] = speedup + ((ReverbPct * RVbuf11[loc2]) / 128); + RVbuf11[loc3] = speedup + ((ReverbPct * RVbuf12[loc3]) / 128); + RVbuf12[loc4] = speedup + ((ReverbPct * RVbuf12[loc4]) / 128); + RVbuf13[loc5] = speedup + ((ReverbPct * RVbuf13[loc5]) / 128); + RVbuf14[loc6] = speedup + ((ReverbPct * RVbuf14[loc6]) / 128); + RVbuf15[loc7] = speedup + ((ReverbPct * RVbuf15[loc7]) / 128); + RVbuf16[loc8] = speedup + ((ReverbPct * RVbuf16[loc8]) / 128); + + // Prepare to compute actual finalized data! + + RVRindex++; + loc1 = RVRindex % RVc1; + loc2 = RVRindex % RVc2; + loc3 = RVRindex % RVc3; + loc4 = RVRindex % RVc4; + loc5 = RVRindex % RVc5; + loc6 = RVRindex % RVc6; + loc7 = RVRindex % RVc7; + loc8 = RVRindex % RVc8; + + // Left Channel! + + *srce++ += (RVbuf1[loc1] - RVbuf2[loc2] + RVbuf3[loc3] - RVbuf4[loc4] + + RVbuf5[loc5] - RVbuf6[loc6] + RVbuf7[loc7] - RVbuf8[loc8]); + + // Right Channel! + + *srce++ += (RVbuf9[loc1] - RVbuf10[loc2] + RVbuf11[loc3] - RVbuf12[loc4] + + RVbuf13[loc5] - RVbuf14[loc6] + RVbuf15[loc7] - RVbuf16[loc8]); + } +} + + +static void Mix32To16_Normal(SWORD *dste, SLONG *srce, SLONG count) +{ + SLONG x1, x2, tmpx; + int i; + + for(count/=SAMPLING_FACTOR; count; count--) + { tmpx = 0; + + for(i=SAMPLING_FACTOR/2; i; i--) + { x1 = *srce++ / bitshift; + x2 = *srce++ / bitshift; + + x1 = (x1 > 32767) ? 32767 : (x1 < -32768) ? -32768 : x1; + x2 = (x2 > 32767) ? 32767 : (x2 < -32768) ? -32768 : x2; + + tmpx += x1 + x2; + } + + *dste++ = tmpx / SAMPLING_FACTOR; + } +} + + +static void Mix32To16_Stereo(SWORD *dste, SLONG *srce, SLONG count) +{ + SLONG x1, x2, x3, x4, tmpx, tmpy; + int i; + + for(count/=SAMPLING_FACTOR; count; count--) + { tmpx = tmpy = 0; + + for(i=SAMPLING_FACTOR/2; i; i--) + { x1 = *srce++ / bitshift; + x2 = *srce++ / bitshift; + x3 = *srce++ / bitshift; + x4 = *srce++ / bitshift; + + x1 = (x1 > 32767) ? 32767 : (x1 < -32768) ? -32768 : x1; + x2 = (x2 > 32767) ? 32767 : (x2 < -32768) ? -32768 : x2; + x3 = (x3 > 32767) ? 32767 : (x3 < -32768) ? -32768 : x3; + x4 = (x4 > 32767) ? 32767 : (x4 < -32768) ? -32768 : x4; + + tmpx += x1 + x3; + tmpy += x2 + x4; + } + + *dste++ = tmpx / SAMPLING_FACTOR; + *dste++ = tmpy / SAMPLING_FACTOR; + } +} + + +static void Mix32To8_Normal(SBYTE *dste, SLONG *srce, SLONG count) +{ + int x1, x2; + int i, tmpx; + + for(count/=SAMPLING_FACTOR; count; count--) + { tmpx = 0; + + for(i=SAMPLING_FACTOR/2; i; i--) + { x1 = *srce++ / bitshift; + x2 = *srce++ / bitshift; + + x1 = (x1 > 127) ? 127 : (x1 < -128) ? -128 : x1; + x2 = (x2 > 127) ? 127 : (x2 < -128) ? -128 : x2; + + tmpx += x1 + x2; + } + + *dste++ = (tmpx / SAMPLING_FACTOR) + 128; + } +} + + +static void Mix32To8_Stereo(SBYTE *dste, SLONG *srce, SLONG count) +{ + int x1, x2, x3, x4; + int i, tmpx, tmpy; + + for(count/=SAMPLING_FACTOR; count; count--) + { tmpx = tmpy = 0; + + for(i=SAMPLING_FACTOR/2; i; i--) + { x1 = *srce++ / bitshift; + x2 = *srce++ / bitshift; + x3 = *srce++ / bitshift; + x4 = *srce++ / bitshift; + + x1 = (x1 > 127) ? 127 : (x1 < -128) ? -128 : x1; + x2 = (x2 > 127) ? 127 : (x2 < -128) ? -128 : x2; + x3 = (x3 > 127) ? 127 : (x3 < -128) ? -128 : x3; + x4 = (x4 > 127) ? 127 : (x4 < -128) ? -128 : x4; + + tmpx += x1 + x3; + tmpy += x2 + x4; + } + + *dste++ = (tmpx / SAMPLING_FACTOR) + 128; + *dste++ = (tmpy / SAMPLING_FACTOR) + 128; + } +} + + +static ULONG samples2bytes(ULONG samples) +{ + if(vc_mode & DMODE_16BITS) samples <<= 1; + if(vc_mode & DMODE_STEREO) samples <<= 1; + + return samples; +} + + +static ULONG bytes2samples(ULONG bytes) +{ + if(vc_mode & DMODE_16BITS) bytes >>= 1; + if(vc_mode & DMODE_STEREO) bytes >>= 1; + + return bytes; +} + + +static void AddChannel(SLONG *ptr, SLONG todo) +{ + SDOUBLE end; + SLONG done; + SWORD *s; + + while(todo > 0) + { // update the 'current' index so the sample loops, or + // stops playing if it reached the end of the sample + + if(vnf->flags & SF_REVERSE) + { + // The sample is playing in reverse + + if((vnf->flags & SF_LOOP) && (vnf->current < idxlpos)) + { + // the sample is looping, and it has + // reached the loopstart index + + if(vnf->flags & SF_BIDI) + { + // sample is doing bidirectional loops, so 'bounce' + // the current index against the idxlpos + + vnf->current = idxlpos + (idxlpos - vnf->current); + vnf->flags &= ~SF_REVERSE; + vnf->increment = -vnf->increment; + } else + // normal backwards looping, so set the + // current position to loopend index + + vnf->current = idxlend - (idxlpos-vnf->current); + } else + { + // the sample is not looping, so check + // if it reached index 0 + + if(vnf->current < 0) + { + // playing index reached 0, so stop + // playing this sample + + vnf->current = 0; + vnf->active = 0; + break; + } + } + } else + { + // The sample is playing forward + + if((vnf->flags & SF_LOOP) && (vnf->current > idxlend)) + { + // the sample is looping, so check if + // it reached the loopend index + + if(vnf->flags & SF_BIDI) + { + // sample is doing bidirectional loops, so 'bounce' + // the current index against the idxlend + + vnf->flags |= SF_REVERSE; + vnf->increment = -vnf->increment; + vnf->current = idxlend-(vnf->current-idxlend); + } else + // normal backwards looping, so set the + // current position to loopend index + + vnf->current = idxlpos + (vnf->current-idxlend); + } else + { + // sample is not looping, so check + // if it reached the last position + + if(vnf->current > idxsize) + { + // yes, so stop playing this sample + + vnf->current = 0; + vnf->active = 0; + break; + } + } + } + + if(!(s=Samples[vnf->handle])) + { vnf->current = 0; + vnf->active = 0; + break; + } + + end = (vnf->flags & SF_REVERSE) ? + (vnf->flags & SF_LOOP) ? idxlpos : 0 : + (vnf->flags & SF_LOOP) ? idxlend : idxsize; + + done = MIN((end - vnf->current) / vnf->increment + 1, todo); + + if(!done) + { vnf->active = 0; + break; + } + + if(vnf->vol) + { if(vc_mode & DMODE_STEREO) + if(vnf->pan == PAN_SURROUND && vc_mode&DMODE_SURROUND) + vnf->current = MixStereoSurround(s,ptr,vnf->current,vnf->increment,done); + else + vnf->current = MixStereoNormal(s,ptr,vnf->current,vnf->increment,done); + else + vnf->current = MixMonoNormal(s,ptr,vnf->current,vnf->increment,done); + } + todo -= done; + ptr += (vc_mode & DMODE_STEREO) ? (done<<1) : done; + } +} + + +void VC2_WriteSamples(SBYTE *buf, ULONG todo) +{ + int left, portion = 0; + SBYTE *buffer; + int t; + int pan, vol; + + todo *= SAMPLING_FACTOR; + + while(todo) + { if(TICKLEFT==0) + { if(vc_mode & DMODE_SOFT_MUSIC) md_player(); + TICKLEFT = (md_mixfreq*125l*SAMPLING_FACTOR) / (md_bpm*50l); + TICKLEFT &= ~(SAMPLING_FACTOR-1); + } + + left = MIN(TICKLEFT, todo); + + buffer = buf; + TICKLEFT -= left; + todo -= left; + + buf += samples2bytes(left) / SAMPLING_FACTOR; + + while(left) + { portion = MIN(left, samplesthatfit); + memset(VC2_TICKBUF, 0, portion << ((vc_mode & DMODE_STEREO) ? 3 : 2)); + + for(t=0; t<vc_softchn; t++) + { vnf = &vinf[t]; + + if(vnf->kick) + { vnf->current = (SDOUBLE)(vnf->start) << FRACBITS; + vnf->kick = 0; + vnf->active = 1; + } + + if(vnf->frq == 0) vnf->active = 0; + + if(vnf->active) + { vnf->increment = ((SDOUBLE)(vnf->frq) << (FRACBITS-SAMPLING_SHIFT)) / (SDOUBLE)md_mixfreq; + if(vnf->flags & SF_REVERSE) vnf->increment=-vnf->increment; + + vol = vnf->vol; pan = vnf->pan; + + if((vc_mode & DMODE_STEREO) && (pan!=PAN_SURROUND)) + { lvolsel = (vol * (255-pan)) >> 8; + rvolsel = (vol * pan) >> 8; + } else + lvolsel = (vol*256l) / 480; + + idxsize = (vnf->size) ? ((SDOUBLE)(vnf->size) << FRACBITS)-1 : 0; + idxlend = (vnf->repend) ? ((SDOUBLE)(vnf->repend) << FRACBITS)-1 : 0; + idxlpos = (SDOUBLE)(vnf->reppos) << FRACBITS; + AddChannel(VC2_TICKBUF, portion); + } + } + + if(md_reverb) MixReverb(VC2_TICKBUF, portion); + + if(vc_mode & DMODE_16BITS) + Mix32to16((SWORD *) buffer, VC2_TICKBUF, portion); + else + Mix32to8((SBYTE *) buffer, VC2_TICKBUF, portion); + + buffer += samples2bytes(portion) / SAMPLING_FACTOR; + left -= portion; + } + } +} + + +void VC2_SilenceBytes(SBYTE *buf, ULONG todo) + +// Fill the buffer with 'todo' bytes of silence (it depends on the mixing +// mode how the buffer is filled) + +{ + // clear the buffer to zero (16 bits + // signed ) or 0x80 (8 bits unsigned) + + if(vc_mode & DMODE_16BITS) + memset(buf,0,todo); + else + memset(buf,0x80,todo); +} + + +ULONG VC2_WriteBytes(SBYTE *buf, ULONG todo) + +// Writes 'todo' mixed SBYTES (!!) to 'buf'. It returns the number of +// SBYTES actually written to 'buf' (which is rounded to number of samples +// that fit into 'todo' bytes). + +{ + if(vc_softchn == 0) + { VC2_SilenceBytes(buf,todo); + return todo; + } + + todo = bytes2samples(todo); + VC2_WriteSamples(buf,todo); + + return samples2bytes(todo); +} + + +BOOL VC2_PlayStart(void) +{ + if(vc_softchn > 0) + { bitshift = vc_softchn + 257; + if(!(vc_mode & DMODE_16BITS)) + bitshift *= 256; + if(md_reverb) bitshift++; + } + + samplesthatfit = TICKLSIZE; + if(vc_mode & DMODE_STEREO) samplesthatfit >>= 1; + TICKLEFT = 0; + + /* Original Reverb Code! + The stuff I use avoids floating point (below). + + RVc1 = (SLONG)(500 * md_mixfreq) / 11000; + RVc2 = (SLONG)(507.8125 * md_mixfreq) / 11000; + RVc3 = (SLONG)(531.25 * md_mixfreq) / 11000; + RVc4 = (SLONG)(570.3125 * md_mixfreq) / 11000; + RVc5 = (SLONG)(625 * md_mixfreq) / 11000; + RVc6 = (SLONG)(695.3125 * md_mixfreq) / 11000; + RVc7 = (SLONG)(781.25 * md_mixfreq) / 11000; + RVc8 = (SLONG)(882.8125 * md_mixfreq) / 11000; + ReverbPct = 99 - (md_reverb*2); + */ + + RVc1 = (5000L * md_mixfreq) / REVERBERATION; + RVc2 = (5078L * md_mixfreq) / REVERBERATION; + RVc3 = (5313L * md_mixfreq) / REVERBERATION; + RVc4 = (5703L * md_mixfreq) / REVERBERATION; + RVc5 = (6250L * md_mixfreq) / REVERBERATION; + RVc6 = (6953L * md_mixfreq) / REVERBERATION; + RVc7 = (7813L * md_mixfreq) / REVERBERATION; + RVc8 = (8828L * md_mixfreq) / REVERBERATION; + + if((RVbuf1 = (SLONG *)_mm_calloc((RVc1+1),sizeof(SLONG))) == NULL) return 1; + if((RVbuf2 = (SLONG *)_mm_calloc((RVc2+1),sizeof(SLONG))) == NULL) return 1; + if((RVbuf3 = (SLONG *)_mm_calloc((RVc3+1),sizeof(SLONG))) == NULL) return 1; + if((RVbuf4 = (SLONG *)_mm_calloc((RVc4+1),sizeof(SLONG))) == NULL) return 1; + if((RVbuf5 = (SLONG *)_mm_calloc((RVc5+1),sizeof(SLONG))) == NULL) return 1; + if((RVbuf6 = (SLONG *)_mm_calloc((RVc6+1),sizeof(SLONG))) == NULL) return 1; + if((RVbuf7 = (SLONG *)_mm_calloc((RVc7+1),sizeof(SLONG))) == NULL) return 1; + if((RVbuf8 = (SLONG *)_mm_calloc((RVc8+1),sizeof(SLONG))) == NULL) return 1; + + if(vc_mode & DMODE_STEREO) + { if((RVbuf9 = (SLONG *)_mm_calloc((RVc1+1),sizeof(SLONG))) == NULL) return 1; + if((RVbuf10 = (SLONG *)_mm_calloc((RVc2+1),sizeof(SLONG))) == NULL) return 1; + if((RVbuf11 = (SLONG *)_mm_calloc((RVc3+1),sizeof(SLONG))) == NULL) return 1; + if((RVbuf12 = (SLONG *)_mm_calloc((RVc4+1),sizeof(SLONG))) == NULL) return 1; + if((RVbuf13 = (SLONG *)_mm_calloc((RVc5+1),sizeof(SLONG))) == NULL) return 1; + if((RVbuf14 = (SLONG *)_mm_calloc((RVc6+1),sizeof(SLONG))) == NULL) return 1; + if((RVbuf15 = (SLONG *)_mm_calloc((RVc7+1),sizeof(SLONG))) == NULL) return 1; + if((RVbuf16 = (SLONG *)_mm_calloc((RVc8+1),sizeof(SLONG))) == NULL) return 1; + } + + RVRindex = 0; + return 0; +} + + +void VC2_PlayStop(void) +{ + if(RVbuf1 != NULL) free(RVbuf1); + if(RVbuf2 != NULL) free(RVbuf2); + if(RVbuf3 != NULL) free(RVbuf3); + if(RVbuf4 != NULL) free(RVbuf4); + if(RVbuf5 != NULL) free(RVbuf5); + if(RVbuf6 != NULL) free(RVbuf6); + if(RVbuf7 != NULL) free(RVbuf7); + if(RVbuf8 != NULL) free(RVbuf8); + if(RVbuf9 != NULL) free(RVbuf9); + if(RVbuf10 != NULL) free(RVbuf10); + if(RVbuf11 != NULL) free(RVbuf11); + if(RVbuf12 != NULL) free(RVbuf12); + if(RVbuf13 != NULL) free(RVbuf13); + if(RVbuf14 != NULL) free(RVbuf14); + if(RVbuf15 != NULL) free(RVbuf15); + if(RVbuf16 != NULL) free(RVbuf16); + + RVbuf1 = NULL; RVbuf2 = NULL; RVbuf3 = NULL; RVbuf4 = NULL; + RVbuf5 = NULL; RVbuf6 = NULL; RVbuf7 = NULL; RVbuf8 = NULL; + RVbuf9 = NULL; RVbuf10 = NULL; RVbuf11 = NULL; RVbuf12 = NULL; + RVbuf13 = NULL; RVbuf14 = NULL; RVbuf15 = NULL; RVbuf16 = NULL; +} + + +BOOL VC2_Init(void) +{ + _mm_errno = MMERR_INITIALIZING_MIXER; + if((Samples = (SWORD **)calloc(MAXSAMPLEHANDLES, sizeof(SWORD *))) == NULL) return 1; + if(VC2_TICKBUF==NULL) if((VC2_TICKBUF=(SLONG *)malloc((TICKLSIZE+32) * sizeof(SLONG))) == NULL) return 1; + + if(md_mode & DMODE_STEREO) + { Mix32to16 = Mix32To16_Stereo; + Mix32to8 = Mix32To8_Stereo; + MixReverb = MixReverb_Stereo; + } else + { Mix32to16 = Mix32To16_Normal; + Mix32to8 = Mix32To8_Normal; + MixReverb = MixReverb_Normal; + } + + vc_mode = md_mode; + + _mm_errno = 0; + return 0; +} + + +void VC2_Exit(void) + +// Yay, he joys and fruits of C and C++ - +// Deallocation of arrays! + +{ + //if(VC2_TICKBUF!=NULL) free(VC2_TICKBUF); + if(vinf!=NULL) free(vinf); + if(Samples!=NULL) free(Samples); + +// VC2_TICKBUF = NULL; + vinf = NULL; + Samples = NULL; +} + + +BOOL VC2_SetNumVoices(void) +{ + int t; + + if((vc_softchn = md_softchn) == 0) return 0; + + if(vinf!=NULL) free(vinf); + if((vinf = _mm_calloc(sizeof(VINFO),vc_softchn)) == NULL) return 1; + + for(t=0; t<vc_softchn; t++) + { vinf[t].frq = 10000; + vinf[t].pan = (t & 1) ? 0 : 255; + } + + return 0; +} + + +void VC2_VoiceSetVolume(UBYTE voice, UWORD vol) +{ + vinf[voice].vol = vol; +} + + +void VC2_VoiceSetFrequency(UBYTE voice, ULONG frq) +{ + vinf[voice].frq = frq; +} + + +void VC2_VoiceSetPanning(UBYTE voice, ULONG pan) +{ + vinf[voice].pan = pan; +} + + +void VC2_VoicePlay(UBYTE voice, SWORD handle, ULONG start, ULONG size, ULONG reppos, ULONG repend, UWORD flags) +{ + vinf[voice].flags = flags; + vinf[voice].handle = handle; + vinf[voice].start = start; + vinf[voice].size = size; + vinf[voice].reppos = reppos; + vinf[voice].repend = repend; + vinf[voice].kick = 1; +} + + +void VC2_VoiceStop(UBYTE voice) +{ + vinf[voice].active = 0; +} + + +BOOL VC2_VoiceStopped(UBYTE voice) +{ + return(vinf[voice].active==0); +} + + +void VC2_VoiceReleaseSustain(UBYTE voice) +{ + +} + + +SLONG VC2_VoiceGetPosition(UBYTE voice) +{ + return(vinf[voice].current>>FRACBITS); +} + + +/************************************************** +*************************************************** +*************************************************** +**************************************************/ + + +void VC2_SampleUnload(SWORD handle) +{ + void *sampleadr = Samples[handle]; + + free(sampleadr); + Samples[handle] = NULL; +} + + +SWORD VC2_SampleLoad(SAMPLOAD *sload, int type, FILE *fp) +{ + SAMPLE *s = sload->sample; + int handle; + ULONG t, length,loopstart,loopend; + + if(type==MD_HARDWARE) return -1; + + // Find empty slot to put sample address in + for(handle=0; handle<MAXSAMPLEHANDLES; handle++) + if(Samples[handle]==NULL) break; + + if(handle==MAXSAMPLEHANDLES) + { _mm_errno = MMERR_OUT_OF_HANDLES; + return -1; + } + + length = s->length; + loopstart = s->loopstart; + loopend = s->loopend; + + SL_SampleSigned(sload); + + SL_Sample8to16(sload); + if((Samples[handle]=(SWORD *)malloc((length+16)<<1))==NULL) + { _mm_errno = MMERR_SAMPLE_TOO_BIG; + return -1; + } + // read sample into buffer. + SL_Load(Samples[handle],sload,length); + + // Unclick samples: + + if(s->flags & SF_LOOP) + { if(s->flags & SF_BIDI) + for(t=0; t<16; t++) Samples[handle][loopend+t] = Samples[handle][(loopend-t)-1]; + else + for(t=0; t<16; t++) Samples[handle][loopend+t] = Samples[handle][t+loopstart]; + } else + for(t=0; t<16; t++) Samples[handle][t+length] = 0; + + return handle; +} + + +ULONG VC2_SampleSpace(int type) +{ + return vc_memory; +} + + +ULONG VC2_SampleLength(int type, SAMPLE *s) +{ + return (s->length * ((s->flags & SF_16BITS) ? 2 : 1)) + 16; +} + + +/************************************************** +*************************************************** +*************************************************** +**************************************************/ +