aboutsummaryrefslogtreecommitdiff
path: root/lib/libmodplug/src/load_pat.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libmodplug/src/load_pat.cpp')
-rw-r--r--lib/libmodplug/src/load_pat.cpp1576
1 files changed, 1576 insertions, 0 deletions
diff --git a/lib/libmodplug/src/load_pat.cpp b/lib/libmodplug/src/load_pat.cpp
new file mode 100644
index 0000000000..9f8a6d838c
--- /dev/null
+++ b/lib/libmodplug/src/load_pat.cpp
@@ -0,0 +1,1576 @@
+/*
+
+ MikMod Sound System
+
+ By Jake Stine of Divine Entertainment (1996-2000)
+
+ Support:
+ If you find problems with this code, send mail to:
+ air@divent.org
+
+ Distribution / Code rights:
+ Use this source code in any fashion you see fit. Giving me credit where
+ credit is due is optional, depending on your own levels of integrity and
+ honesty.
+
+ -----------------------------------------
+ Module: LOAD_PAT
+
+ PAT sample loader.
+ by Peter Grootswagers (2006)
+ <email:pgrootswagers@planet.nl>
+
+ It's primary purpose is loading samples for the .abc and .mid modules
+ Can also be used stand alone, in that case a tune (frere Jacques)
+ is generated using al samples available in the .pat file
+
+ Portability:
+ All systems - all compilers (hopefully)
+*/
+
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include <math.h>
+#include <ctype.h>
+#include <unistd.h> // for sleep
+
+#ifdef NEWMIKMOD
+#include "mikmod.h"
+#include "uniform.h"
+typedef UBYTE BYTE;
+typedef UWORD WORD;
+#else
+#include "stdafx.h"
+#include "sndfile.h"
+#endif
+
+#include "load_pat.h"
+
+#ifdef MSC_VER
+#define DIRDELIM '\\'
+#define TIMIDITYCFG "C:\\TIMIDITY\\TIMIDITY.CFG"
+#define PATHFORPAT "C:\\TIMIDITY\\INSTRUMENTS"
+#else
+#define DIRDELIM '/'
+#define TIMIDITYCFG "/usr/local/share/timidity/timidity.cfg"
+#define PATHFORPAT "/usr/local/share/timidity/instruments"
+#endif
+
+#define PAT_ENV_PATH2CFG "MMPAT_PATH_TO_CFG"
+
+// 128 gm and 63 drum
+#define MAXSMP 191
+static char midipat[MAXSMP][40];
+static char pathforpat[128];
+static char timiditycfg[128];
+
+#pragma pack(1)
+
+typedef struct {
+ char header[12]; // ascizz GF1PATCH110
+ char gravis_id[10]; // allways ID#000002
+ char description[60];
+ BYTE instruments;
+ BYTE voices;
+ BYTE channels;
+ WORD waveforms;
+ WORD master_volume;
+ DWORD data_size;
+ char reserved[36];
+} PatchHeader;
+
+typedef struct {
+ WORD instrument_id;
+ char instrument_name[16];
+ DWORD instrument_size;
+ BYTE layers;
+ char reserved[40];
+} InstrumentHeader;
+
+typedef struct {
+ BYTE layer_dup;
+ BYTE layer_id;
+ DWORD layer_size;
+ BYTE samples;
+ char reserved[40];
+} LayerHeader;
+
+typedef struct {
+ char wave_name[7];
+ BYTE fractions;
+ DWORD wave_size;
+ DWORD start_loop;
+ DWORD end_loop;
+ WORD sample_rate;
+ DWORD low_frequency ;
+ DWORD high_frequency;
+ DWORD root_frequency;
+ short int tune;
+ BYTE balance;
+ BYTE envelope_rate[6];
+ BYTE envelope_offset[6];
+ BYTE tremolo_sweep;
+ BYTE tremolo_rate;
+ BYTE tremolo_depth;
+ BYTE vibrato_sweep;
+ BYTE vibrato_rate;
+ BYTE vibrato_depth;
+ BYTE modes;
+ DWORD scale_frequency;
+ DWORD scale_factor;
+ char reserved[32];
+} WaveHeader;
+
+// WaveHeader.modes bits
+#define PAT_16BIT 1
+#define PAT_UNSIGNED 2
+#define PAT_LOOP 4
+#define PAT_PINGPONG 8
+#define PAT_BACKWARD 16
+#define PAT_SUSTAIN 32
+#define PAT_ENVELOPE 64
+#define PAT_CLAMPED 128
+
+#define C4SPD 8363
+#define C4mHz 523251
+#define C4 523.251
+#define PI 3.141592653589793
+#define OMEGA ((2.0 * PI * C4)/(float)C4SPD)
+
+/**************************************************************************
+**************************************************************************/
+#ifdef NEWMIKMOD
+static char PAT_Version[] = "Timidity GUS Patch v1.0";
+#endif
+static BYTE pat_gm_used[MAXSMP];
+static BYTE pat_loops[MAXSMP];
+
+/**************************************************************************
+**************************************************************************/
+
+typedef struct _PATHANDLE
+{
+#ifdef NEWMIKMOD
+ MM_ALLOC *allochandle;
+#endif
+ char patname[16];
+ int samples;
+} PATHANDLE;
+
+// local prototypes
+static int pat_getopt(const char *s, const char *o, int dflt);
+
+static void pat_message(const char *s1, const char *s2)
+{
+ char txt[256];
+ if( strlen(s1) + strlen(s2) > 255 ) return;
+ sprintf(txt, s1, s2);
+#ifdef NEWMIKMOD
+ _mmlog(txt);
+#else
+ fprintf(stderr, "load_pat > %s\n", txt);
+#endif
+}
+
+void pat_resetsmp(void)
+{
+ int i;
+ for( i=0; i<MAXSMP; i++ ) {
+ pat_loops[i] = 0;
+ pat_gm_used[i] = 0;
+ }
+}
+
+int pat_numsmp()
+{
+ return strlen((const char *)pat_gm_used);
+}
+
+int pat_numinstr(void)
+{
+ return strlen((const char *)pat_gm_used);
+}
+
+int pat_smptogm(int smp)
+{
+ if( smp < MAXSMP )
+ return pat_gm_used[smp - 1];
+ return 1;
+}
+
+int pat_gmtosmp(int gm)
+{
+ int smp;
+ for( smp=0; pat_gm_used[smp]; smp++ )
+ if( pat_gm_used[smp] == gm )
+ return smp+1;
+ if( smp < MAXSMP ) {
+ pat_gm_used[smp] = gm;
+ return smp+1;
+ }
+ return 1;
+}
+
+int pat_smplooped(int smp)
+{
+ if( smp < MAXSMP ) return pat_loops[smp - 1];
+ return 1;
+}
+
+const char *pat_gm_name(int gm)
+{
+ static char buf[40];
+ if( gm < 1 || gm > MAXSMP ) {
+ sprintf(buf, "invalid gm %d", gm);
+ return buf;
+ }
+ return midipat[gm - 1];
+}
+
+int pat_gm_drumnr(int n)
+{
+ if( n < 25 ) return 129;
+ if( n+129-25 < MAXSMP )
+ return 129+n-25; // timidity.cfg drum patches start at 25
+ return MAXSMP;
+}
+
+int pat_gm_drumnote(int n)
+{
+ char *p;
+ p = strchr(midipat[pat_gm_drumnr(n)-1], ':');
+ if( p ) return pat_getopt(p+1, "note", n);
+ return n;
+}
+
+static float pat_sinus(int i)
+{
+ float res = sinf(OMEGA * (float)i);
+ return res;
+}
+
+static float pat_square(int i)
+{
+ float res = 30.0 * sinf(OMEGA * (float)i);
+ if( res > 0.99 ) return 0.99;
+ if( res < -0.99 ) return -0.99;
+ return res;
+}
+
+static float pat_sawtooth(int i)
+{
+ float res = OMEGA * (float)i;
+ while( res > 2 * PI )
+ res -= 2 * PI;
+ i = 2;
+ if( res > PI ) {
+ res = PI - res;
+ i = -2;
+ }
+ res = (float)i * res / PI;
+ if( res > 0.9 ) return 1.0 - res;
+ if( res < -0.9 ) return 1.0 + res;
+ return res;
+}
+
+typedef float (*PAT_SAMPLE_FUN)(int);
+
+static PAT_SAMPLE_FUN pat_fun[] = { pat_sinus, pat_square, pat_sawtooth };
+
+#ifdef NEWMIKMOD
+
+#define MMFILE MMSTREAM
+#define mmftell(x) _mm_ftell(x)
+#define mmfseek(f,p,w) _mm_fseek(f,p,w)
+#define mmreadUBYTES(buf,sz,f) _mm_read_UBYTES(buf,sz,f)
+
+#else
+
+#define MMSTREAM FILE
+#define _mm_fopen(name,mode) fopen(name,mode)
+#define _mm_fgets(f,buf,sz) fgets(buf,sz,f)
+#define _mm_fseek(f,pos,whence) fseek(f,pos,whence)
+#define _mm_ftell(f) ftell(f)
+#define _mm_read_UBYTES(buf,sz,f) fread(buf,sz,1,f)
+#define _mm_read_SBYTES(buf,sz,f) fread(buf,sz,1,f)
+#define _mm_feof(f) feof(f)
+#define _mm_fclose(f) fclose(f)
+#define DupStr(h,buf,sz) strdup(buf)
+#define _mm_calloc(h,n,sz) calloc(n,sz)
+#define _mm_recalloc(h,buf,sz,elsz) realloc(buf,sz)
+#define _mm_free(h,p) free(p)
+
+typedef struct {
+ char *mm;
+ int sz;
+ int pos;
+} MMFILE;
+
+static long mmftell(MMFILE *mmfile)
+{
+ return mmfile->pos;
+}
+
+static void mmfseek(MMFILE *mmfile, long p, int whence)
+{
+ switch(whence) {
+ case SEEK_SET:
+ mmfile->pos = p;
+ break;
+ case SEEK_CUR:
+ mmfile->pos += p;
+ break;
+ case SEEK_END:
+ mmfile->pos = mmfile->sz + p;
+ break;
+ }
+}
+
+static void mmreadUBYTES(BYTE *buf, long sz, MMFILE *mmfile)
+{
+ memcpy(buf, &mmfile->mm[mmfile->pos], sz);
+ mmfile->pos += sz;
+}
+
+static void mmreadSBYTES(char *buf, long sz, MMFILE *mmfile)
+{
+ memcpy(buf, &mmfile->mm[mmfile->pos], sz);
+ mmfile->pos += sz;
+}
+
+#endif
+
+void pat_init_patnames(void)
+{
+ int i,j;
+ char *p, *q;
+ char line[80];
+ MMSTREAM *mmcfg;
+ strcpy(pathforpat, PATHFORPAT);
+ strcpy(timiditycfg, TIMIDITYCFG);
+ p = getenv(PAT_ENV_PATH2CFG);
+ if( p ) {
+ strcpy(timiditycfg,p);
+ strcpy(pathforpat,p);
+ strcat(timiditycfg,"/timidity.cfg");
+ strcat(pathforpat,"/instruments");
+ }
+ mmcfg = _mm_fopen(timiditycfg,"r");
+ for( i=0; i<MAXSMP; i++ ) midipat[i][0] = '\0';
+ if( !mmcfg ) {
+ pat_message("can not open %s, use environment variable " PAT_ENV_PATH2CFG " for the directory", timiditycfg);
+ }
+ else {
+ // read in bank 0 and drum patches
+ j = 0;
+ _mm_fgets(mmcfg, line, 80);
+ while( !_mm_feof(mmcfg) ) {
+ if( isdigit(line[0]) ) {
+ i = atoi(line);
+ if( i < MAXSMP && i >= 0 ) {
+ p = strchr(line,'/')+1;
+ if(j)
+ q = midipat[pat_gm_drumnr(i)-1];
+ else
+ q = midipat[i];
+ while( *p && !isspace(*p) ) *q++ = *p++;
+ if( isspace(*p) ) {
+ *q++ = ':';
+ while( isspace(*p) ) {
+ while( isspace(*p) ) p++;
+ while( *p && !isspace(*p) ) *q++ = *p++;
+ if( isspace(*p) ) *q++ = ' ';
+ }
+ }
+ *q++ = '\0';
+ }
+ }
+ if( !strncmp(line,"drumset",7) ) j = 1;
+ _mm_fgets(mmcfg, line, 80);
+ }
+ _mm_fclose(mmcfg);
+ }
+ q = midipat[0];
+ j = 0;
+ for( i=0; i<MAXSMP; i++ ) {
+ if( midipat[i][0] ) q = midipat[i];
+ else {
+ strcpy(midipat[i],q);
+ if( midipat[i][0] == '\0' ) j++;
+ }
+ }
+ if( j ) {
+ for( i=MAXSMP; i-- > 0; ) {
+ if( midipat[i][0] ) q = midipat[i];
+ else strcpy(midipat[i],q);
+ }
+ }
+}
+
+static char *pat_build_path(char *fname, int pat)
+{
+ char *ps;
+ ps = strrchr(midipat[pat], ':');
+ if( ps ) {
+ sprintf(fname, "%s%c%s", pathforpat, DIRDELIM, midipat[pat]);
+ strcpy(strrchr(fname, ':'), ".pat");
+ return ps;
+ }
+ sprintf(fname, "%s%c%s.pat", pathforpat, DIRDELIM, midipat[pat]);
+ return 0;
+}
+
+static void pat_read_patname(PATHANDLE *h, MMFILE *mmpat) {
+ InstrumentHeader ih;
+ mmfseek(mmpat,sizeof(PatchHeader), SEEK_SET);
+ mmreadUBYTES((BYTE *)&ih, sizeof(InstrumentHeader), mmpat);
+ strncpy(h->patname, ih.instrument_name, 16);
+ h->patname[15] = '\0';
+}
+
+static void pat_read_layerheader(MMSTREAM *mmpat, LayerHeader *hl)
+{
+ _mm_fseek(mmpat,sizeof(PatchHeader)+sizeof(InstrumentHeader), SEEK_SET);
+ _mm_read_UBYTES((BYTE *)hl, sizeof(LayerHeader), mmpat);
+}
+
+static void pat_get_layerheader(MMFILE *mmpat, LayerHeader *hl)
+{
+ InstrumentHeader ih;
+ mmfseek(mmpat,sizeof(PatchHeader), SEEK_SET);
+ mmreadUBYTES((BYTE *)&ih, sizeof(InstrumentHeader), mmpat);
+ mmreadUBYTES((BYTE *)hl, sizeof(LayerHeader), mmpat);
+ strncpy(hl->reserved, ih.instrument_name, 40);
+}
+
+static int pat_read_numsmp(MMFILE *mmpat) {
+ LayerHeader hl;
+ pat_get_layerheader(mmpat, &hl);
+ return hl.samples;
+}
+
+static void pat_read_waveheader(MMSTREAM *mmpat, WaveHeader *hw, int layer)
+{
+ long int pos, bestpos=0;
+ LayerHeader hl;
+ ULONG bestfreq, freqdist;
+ int i;
+ // read the very first and maybe only sample
+ pat_read_layerheader(mmpat, &hl);
+ if( hl.samples > 1 ) {
+ if( layer ) {
+ if( layer > hl.samples ) layer = hl.samples; // you don't fool me....
+ for( i=1; i<layer; i++ ) {
+ _mm_read_UBYTES((BYTE *)hw, sizeof(WaveHeader), mmpat);
+ _mm_fseek(mmpat, hw->wave_size, SEEK_CUR);
+ }
+ }
+ else {
+ bestfreq = C4mHz * 1000; // big enough
+ for( i=0; i<hl.samples; i++ ) {
+ pos = _mm_ftell(mmpat);
+ _mm_read_UBYTES((BYTE *)hw, sizeof(WaveHeader), mmpat);
+ if( hw->root_frequency > C4mHz )
+ freqdist = hw->root_frequency - C4mHz;
+ else
+ freqdist = 2 * (C4mHz - hw->root_frequency);
+ if( freqdist < bestfreq ) {
+ bestfreq = freqdist;
+ bestpos = pos;
+ }
+ _mm_fseek(mmpat, hw->wave_size, SEEK_CUR);
+ }
+ _mm_fseek(mmpat, bestpos, SEEK_SET);
+ }
+ }
+ _mm_read_UBYTES((BYTE *)hw, sizeof(WaveHeader), mmpat);
+ strncpy(hw->reserved, hl.reserved, 36);
+ if( hw->start_loop >= hw->wave_size ) {
+ hw->start_loop = 0;
+ hw->end_loop = 0;
+ hw->modes &= ~PAT_LOOP; // mask off loop indicator
+ }
+ if( hw->end_loop > hw->wave_size )
+ hw->end_loop = hw->wave_size;
+}
+
+#ifndef NEWMIKMOD
+static void pat_get_waveheader(MMFILE *mmpat, WaveHeader *hw, int layer)
+{
+ long int pos, bestpos=0;
+ LayerHeader hl;
+ ULONG bestfreq, freqdist;
+ int i;
+ // read the very first and maybe only sample
+ pat_get_layerheader(mmpat, &hl);
+ if( hl.samples > 1 ) {
+ if( layer ) {
+ if( layer > hl.samples ) layer = hl.samples; // you don't fool me....
+ for( i=1; i<layer; i++ ) {
+ mmreadUBYTES((BYTE *)hw, sizeof(WaveHeader), mmpat);
+ mmfseek(mmpat, hw->wave_size, SEEK_CUR);
+ }
+ }
+ else {
+ bestfreq = C4mHz * 1000; // big enough
+ for( i=0; i<hl.samples; i++ ) {
+ pos = mmftell(mmpat);
+ mmreadUBYTES((BYTE *)hw, sizeof(WaveHeader), mmpat);
+ if( hw->root_frequency > C4mHz )
+ freqdist = hw->root_frequency - C4mHz;
+ else
+ freqdist = 2 * (C4mHz - hw->root_frequency);
+ if( freqdist < bestfreq ) {
+ bestfreq = freqdist;
+ bestpos = pos;
+ }
+ mmfseek(mmpat, hw->wave_size, SEEK_CUR);
+ }
+ mmfseek(mmpat, bestpos, SEEK_SET);
+ }
+ }
+ mmreadUBYTES((BYTE *)hw, sizeof(WaveHeader), mmpat);
+ if( hw->start_loop >= hw->wave_size ) {
+ hw->start_loop = 0;
+ hw->end_loop = 0;
+ hw->modes &= ~PAT_LOOP; // mask off loop indicator
+ }
+ if( hw->end_loop > hw->wave_size )
+ hw->end_loop = hw->wave_size;
+}
+#endif
+
+static int pat_readpat_attr(int pat, WaveHeader *hw, int layer)
+{
+ char fname[128];
+ MMSTREAM *mmpat;
+ pat_build_path(fname, pat);
+ mmpat = _mm_fopen(fname, "r");
+ if( !mmpat )
+ return 0;
+ pat_read_waveheader(mmpat, hw, layer);
+ _mm_fclose(mmpat);
+ return 1;
+}
+
+static void pat_amplify(char *b, int num, int amp, int m)
+{
+ char *pb;
+ BYTE *pu;
+ short int *pi;
+ WORD *pw;
+ int i,n,v;
+ n = num;
+ if( m & PAT_16BIT ) { // 16 bit
+ n >>= 1;
+ if( m & 2 ) { // unsigned
+ pw = (WORD *)b;
+ for( i=0; i<n; i++ ) {
+ v = (((int)(*pw) - 0x8000) * amp) / 100;
+ if( v < -0x8000 ) v = -0x8000;
+ if( v > 0x7fff ) v = 0x7fff;
+ *pw++ = v + 0x8000;
+ }
+ }
+ else {
+ pi = (short int *)b;
+ for( i=0; i<n; i++ ) {
+ v = ((*pi) * amp) / 100;
+ if( v < -0x8000 ) v = -0x8000;
+ if( v > 0x7fff ) v = 0x7fff;
+ *pi++ = v;
+ }
+ }
+ }
+ else {
+ if( m & 2 ) { // unsigned
+ pu = (BYTE *)b;
+ for( i=0; i<n; i++ ) {
+ v = (((int)(*pu) - 0x80) * amp) / 100;
+ if( v < -0x80 ) v = -0x80;
+ if( v > 0x7f ) v = 0x7f;
+ *pu++ = v + 0x80;
+ }
+ }
+ else {
+ pb = (char *)b;
+ for( i=0; i<n; i++ ) {
+ v = ((*pb) * amp) / 100;
+ if( v < -0x80 ) v = -0x80;
+ if( v > 0x7f ) v = 0x7f;
+ *pb++ = v;
+ }
+ }
+ }
+}
+
+static int pat_getopt(const char *s, const char *o, int dflt)
+{
+ const char *p;
+ if( !s ) return dflt;
+ p = strstr(s,o);
+ if( !p ) return dflt;
+ return atoi(strchr(p,'=')+1);
+}
+
+static void pat_readpat(int pat, char *dest, int num)
+{
+ static int readlasttime = 0, wavesize = 0;
+ static MMSTREAM *mmpat = 0;
+ static char *opt = 0;
+ int amp;
+ char fname[128];
+ WaveHeader hw;
+#ifdef NEWMIKMOD
+ static int patlast = MAXSMP;
+ if( !dest ) { // reset
+ if( mmpat ) _mm_fclose(mmpat);
+ readlasttime = 0;
+ wavesize = 0;
+ mmpat = 0;
+ patlast = MAXSMP;
+ return;
+ }
+ if( pat != patlast ) { // reset for other instrument
+ if( mmpat ) _mm_fclose(mmpat);
+ readlasttime = 0;
+ patlast = pat;
+ }
+#endif
+ if( !readlasttime ) {
+ opt=pat_build_path(fname, pat);
+ mmpat = _mm_fopen(fname, "r");
+ if( !mmpat )
+ return;
+ pat_read_waveheader(mmpat, &hw, 0);
+ wavesize = hw.wave_size;
+ }
+ _mm_read_SBYTES(dest, num, mmpat);
+ amp = pat_getopt(opt,"amp",100);
+ if( amp != 100 ) pat_amplify(dest, num, amp, hw.modes);
+ readlasttime += num;
+ if( readlasttime < wavesize ) return;
+ readlasttime = 0;
+ _mm_fclose(mmpat);
+ mmpat = 0;
+}
+
+#ifdef NEWMIKMOD
+// next code pinched from dec_raw.c and rebuild to load bytes from different places
+// =====================================================================================
+static void *dec_pat_Init(MMSTREAM *mmfp)
+{
+ pat_readpat(0,0,0); // initialize pat loader
+ return (void *)mmfp;
+}
+
+static void dec_pat_Cleanup(void *raw)
+{
+}
+
+static BOOL dec_pat_Decompress16Bit(void *raw, short int *dest, int cbcount, MMSTREAM *mmfp)
+{
+ long samplenum = _mm_ftell(mmfp) - 1;
+#else
+static BOOL dec_pat_Decompress16Bit(short int *dest, int cbcount, int samplenum)
+{
+#endif
+ int i;
+ PAT_SAMPLE_FUN f;
+ if( samplenum < MAXSMP ) pat_readpat(samplenum, (char *)dest, cbcount*2);
+ else {
+ f = pat_fun[(samplenum - MAXSMP) % 3];
+ for( i=0; i<cbcount; i++ )
+ dest[i] = (short int)(32000.0*f(i));
+ }
+ return cbcount;
+}
+
+// convert 8 bit data to 16 bit!
+// We do the conversion in reverse so that the data we're converting isn't overwritten
+// by the result.
+static void pat_blowup_to16bit(short int *dest, int cbcount) {
+ char *s;
+ short int *d;
+ int t;
+ s = (char *)dest;
+ d = dest;
+ s += cbcount;
+ d += cbcount;
+ for(t=0; t<cbcount; t++)
+ { s--;
+ d--;
+ *d = (*s) << 8;
+ }
+}
+
+#ifdef NEWMIKMOD
+static BOOL dec_pat_Decompress8Bit(void *raw, short int *dest, int cbcount, MMSTREAM *mmfp)
+{
+ long samplenum = _mm_ftell(mmfp) - 1;
+#else
+static BOOL dec_pat_Decompress8Bit(short int *dest, int cbcount, int samplenum)
+{
+#endif
+ int i;
+ PAT_SAMPLE_FUN f;
+ if( samplenum < MAXSMP ) pat_readpat(samplenum, (char *)dest, cbcount);
+ else {
+ f = pat_fun[(samplenum - MAXSMP) % 3];
+ for( i=0; i<cbcount; i++ )
+ dest[i] = (char)(120.0*f(i));
+ }
+ pat_blowup_to16bit(dest, cbcount);
+ return cbcount;
+}
+
+#ifdef NEWMIKMOD
+SL_DECOMPRESS_API dec_pat =
+{
+ NULL,
+ SL_COMPRESS_RAW,
+ dec_pat_Init,
+ dec_pat_Cleanup,
+ dec_pat_Decompress16Bit,
+ dec_pat_Decompress8Bit,
+};
+#endif
+
+// =====================================================================================
+#ifdef NEWMIKMOD
+BOOL PAT_Test(MMSTREAM *mmfile)
+#else
+BOOL CSoundFile::TestPAT(const BYTE *lpStream, DWORD dwMemLength)
+#endif
+// =====================================================================================
+{
+ PatchHeader ph;
+#ifdef NEWMIKMOD
+ _mm_fseek(mmfile,0,SEEK_SET);
+ _mm_read_UBYTES((BYTE *)&ph, sizeof(PatchHeader), mmfile);
+#else
+ if( dwMemLength < sizeof(PatchHeader) ) return 0;
+ memcpy((BYTE *)&ph, lpStream, sizeof(PatchHeader));
+#endif
+ if( !strcmp(ph.header,"GF1PATCH110") && !strcmp(ph.gravis_id,"ID#000002") ) return 1;
+ return 0;
+}
+
+// =====================================================================================
+static PATHANDLE *PAT_Init(void)
+{
+ PATHANDLE *retval;
+#ifdef NEWMIKMOD
+ MM_ALLOC *allochandle;
+
+ allochandle = _mmalloc_create("Load_PAT", NULL);
+ retval = (PATHANDLE *)_mm_calloc(allochandle, 1,sizeof(PATHANDLE));
+ if( !retval ) return NULL;
+ SL_RegisterDecompressor(&dec_raw); // we can not get the samples out of our own routines...!
+#else
+ retval = (PATHANDLE *)calloc(1,sizeof(PATHANDLE));
+ if( !retval ) return NULL;
+#endif
+ return retval;
+}
+
+// =====================================================================================
+static void PAT_Cleanup(PATHANDLE *handle)
+// =====================================================================================
+{
+#ifdef NEWMIKMOD
+ if(handle && handle->allochandle) {
+ _mmalloc_close(handle->allochandle);
+ handle->allochandle = 0;
+ }
+#else
+ if(handle) {
+ free(handle);
+ }
+#endif
+}
+
+static char tune[] = "c d e c|c d e c|e f g..|e f g..|gagfe c|gagfe c|c G c..|c G c..|";
+static int pat_note(int abc)
+{
+ switch( abc ) {
+ case 'C': return 48;
+ case 'D': return 50;
+ case 'E': return 52;
+ case 'F': return 53;
+ case 'G': return 55;
+ case 'A': return 57;
+ case 'B': return 59;
+ case 'c': return 60;
+ case 'd': return 62;
+ case 'e': return 64;
+ case 'f': return 65;
+ case 'g': return 67;
+ case 'a': return 69;
+ case 'b': return 71;
+ default:
+ break;
+ }
+ return 0;
+}
+
+int pat_modnote(int midinote)
+{
+ int n;
+ n = midinote;
+#ifdef NEWMIKMOD
+ if( n < 12 ) n++;
+ else n-=11;
+#else
+ n += 13;
+#endif
+ return n;
+}
+
+// =====================================================================================
+#ifdef NEWMIKMOD
+static void PAT_ReadPatterns(UNIMOD *of, PATHANDLE *h, int numpat)
+// =====================================================================================
+{
+ int pat,row,i,ch;
+ BYTE n,ins,vol;
+ int t;
+ int tt1, tt2;
+ UNITRK_EFFECT eff;
+
+ tt2 = (h->samples - 1) * 16 + 128;
+ for( pat = 0; pat < numpat; pat++ ) {
+ utrk_reset(of->ut);
+ for( row = 0; row < 64; row++ ) {
+ tt1 = (pat * 64 + row);
+ for( ch = 0; ch < h->samples; ch++ ) {
+ t = tt1 - ch * 16;
+ if( t >= 0 ) {
+ i = tt2 - 16 * ((h->samples - 1 - ch) & 3);
+ if( tt1 < i ) {
+ t = t % 64;
+ if( isalpha(tune[t]) ) {
+ utrk_settrack(of->ut, ch);
+ n = pat_modnote(pat_note(tune[t]));
+ ins = ch;
+ vol = 100;
+ if( (t % 16) == 0 ) {
+ vol += vol / 10;
+ if( vol > 127 ) vol = 127;
+ }
+ utrk_write_inst(of->ut, ins);
+ utrk_write_note(of->ut, n); // <- normal note
+ pt_write_effect(of->ut, 0xc, vol);
+ }
+ if( tt1 == i - 1 && ch == 0 && row < 63 ) {
+ eff.effect = UNI_GLOB_PATBREAK;
+ eff.param.u = 0;
+ eff.framedly = UFD_RUNONCE;
+ utrk_write_global(of->ut, &eff, UNIMEM_NONE);
+ }
+ }
+ else {
+ if( tt1 == i ) {
+ eff.param.u = 0;
+ eff.effect = UNI_NOTEKILL;
+ utrk_write_local(of->ut, &eff, UNIMEM_NONE);
+ }
+ }
+ }
+ }
+ utrk_newline(of->ut);
+ }
+ if(!utrk_dup_pattern(of->ut,of)) return;
+ }
+}
+
+#else
+
+static void PAT_ReadPatterns(MODCOMMAND *pattern[], WORD psize[], PATHANDLE *h, int numpat)
+// =====================================================================================
+{
+ int pat,row,i,ch;
+ BYTE n,ins,vol;
+ int t;
+ int tt1, tt2;
+ MODCOMMAND *m;
+ if( numpat > MAX_PATTERNS ) numpat = MAX_PATTERNS;
+
+ tt2 = (h->samples - 1) * 16 + 128;
+ for( pat = 0; pat < numpat; pat++ ) {
+ pattern[pat] = CSoundFile::AllocatePattern(64, h->samples);
+ if( !pattern[pat] ) return;
+ psize[pat] = 64;
+ for( row = 0; row < 64; row++ ) {
+ tt1 = (pat * 64 + row);
+ for( ch = 0; ch < h->samples; ch++ ) {
+ t = tt1 - ch * 16;
+ m = &pattern[pat][row * h->samples + ch];
+ m->param = 0;
+ m->command = CMD_NONE;
+ if( t >= 0 ) {
+ i = tt2 - 16 * ((h->samples - 1 - ch) & 3);
+ if( tt1 < i ) {
+ t = t % 64;
+ if( isalpha(tune[t]) ) {
+ n = pat_modnote(pat_note(tune[t]));
+ ins = ch + 1;
+ vol = 40;
+ if( (t % 16) == 0 ) {
+ vol += vol / 10;
+ if( vol > 64 ) vol = 64;
+ }
+ m->instr = ins;
+ m->note = n; // <- normal note
+ m->volcmd = VOLCMD_VOLUME;
+ m->vol = vol;
+ }
+ if( tt1 == i - 1 && ch == 0 && row < 63 ) {
+ m->command = CMD_PATTERNBREAK;
+ }
+ }
+ else {
+ if( tt1 == i ) {
+ m->param = 0;
+ m->command = CMD_KEYOFF;
+ m->volcmd = VOLCMD_VOLUME;
+ m->vol = 0;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+#endif
+
+// calculate the best speed that approximates the pat root frequency as a C note
+static ULONG pat_patrate_to_C4SPD(ULONG patRate , ULONG patMilliHz)
+{
+ ULONG u;
+ double x, y;
+ u = patMilliHz;
+ x = 0.1 * patRate;
+ x = x * C4mHz;
+ y = u * 0.4;
+ x = x / y;
+ u = (ULONG)(x+0.5);
+ return u;
+}
+
+// return relative position in samples for the rate starting with offset start ending with offset end
+static int pat_envelope_rpos(int rate, int start, int end)
+{
+ int r, p, t, s;
+ // rate byte is 3 bits exponent and 6 bits increment size
+ // eeiiiiii
+ // every 8 to the power ee the volume is incremented/decremented by iiiiii
+ // Thank you Gravis for this weirdness...
+ r = 3 - ((rate >> 6) & 3) * 3;
+ p = rate & 0x3f;
+ if( !p ) return 0;
+ t = end - start;
+ if( !t ) return 0;
+ if (t < 0) t = -t;
+ s = (t << r)/ p;
+ return s;
+}
+
+static void pat_modenv(WaveHeader *hw, int mpos[6], int mvol[6])
+{
+ int i, sum, s;
+ BYTE *prate = hw->envelope_rate, *poffset = hw->envelope_offset;
+ for( i=0; i<6; i++ ) {
+ mpos[i] = 0;
+ mvol[i] = 64;
+ }
+ if( !memcmp(prate, "??????", 6) || poffset[5] >= 100 ) return; // weird rates or high env end volume
+ if( !(hw->modes & PAT_SUSTAIN) ) return; // no sustain thus no need for envelope
+ s = hw->wave_size;
+ if (s == 0) return;
+ if( hw->modes & PAT_16BIT )
+ s >>= 1;
+ // offsets 0 1 2 3 4 5 are distributed over 0 2 4 6 8 10, the odd numbers are set in between
+ sum = 0;
+ for( i=0; i<6; i++ ) {
+ mvol[i] = poffset[i];
+ mpos[i] = pat_envelope_rpos(prate[i], i? poffset[i-1]: 0, poffset[i]);
+ sum += mpos[i];
+ }
+ if( sum == 0 ) return;
+ if( sum > s ) {
+ for( i=0; i<6; i++ )
+ mpos[i] = (s * mpos[i]) / sum;
+ }
+ for( i=1; i<6; i++ )
+ mpos[i] += mpos[i-1];
+ for( i=0; i<6 ; i++ ) {
+ mpos[i] = (256 * mpos[i]) / s;
+ mpos[i]++;
+ if( i > 0 && mpos[i] <= mpos[i-1] ) {
+ if( mvol[i] == mvol[i-1] ) mpos[i] = mpos[i-1];
+ else mpos[i] = mpos[i-1] + 1;
+ }
+ if( mpos[i] > 256 ) mpos[i] = 256;
+ }
+ mvol[5] = 0; // kill Bill....
+}
+
+#ifdef NEWMIKMOD
+static void pat_setpat_inst(WaveHeader *hw, INSTRUMENT *d, int smp)
+{
+ int u, inuse;
+ int envpoint[6], envvolume[6];
+ for(u=0; u<120; u++) {
+ d->samplenumber[u] = smp;
+ d->samplenote[u] = smp;
+ }
+ d->globvol = 64;
+ d->volfade = 0;
+ d->volflg = EF_CARRY;
+ d->panflg = EF_CARRY;
+ if( hw->modes & PAT_ENVELOPE ) d->volflg |= EF_ON;
+ if( hw->modes & PAT_SUSTAIN ) d->volflg |= EF_SUSTAIN;
+ if( (hw->modes & PAT_LOOP) && (hw->start_loop != hw->end_loop) ) d->volflg |= EF_LOOP;
+ d->volsusbeg = 1;
+ d->volsusend = 1;
+ d->volbeg = 1;
+ d->volend = 2;
+ d->volpts = 6;
+ // scale volume envelope:
+ inuse = 0;
+ pat_modenv(hw, envpoint, envvolume);
+ for(u=0; u<6; u++)
+ {
+ if( envvolume[u] != 64 ) inuse = 1;
+ d->volenv[u].val = envvolume[u]<<2;
+ d->volenv[u].pos = envpoint[u];
+ }
+ if(!inuse) d->volpts = 0;
+ d->pansusbeg = 0;
+ d->pansusend = 0;
+ d->panbeg = 0;
+ d->panend = 0;
+ d->panpts = 0;
+ // scale panning envelope:
+ for(u=0; u<12; u++)
+ {
+ d->panenv[u].val = 0;
+ d->panenv[u].pos = 0;
+ }
+ d->panpts = 0;
+}
+#else
+static void pat_setpat_inst(WaveHeader *hw, INSTRUMENTHEADER *d, int smp)
+{
+ int u, inuse;
+ int envpoint[6], envvolume[6];
+ d->nMidiProgram = 0;
+ d->nFadeOut = 0;
+ d->nPan = 128;
+ d->nPPC = 5*12;
+ d->dwFlags = 0;
+ if( hw->modes & PAT_ENVELOPE ) d->dwFlags |= ENV_VOLUME;
+ if( hw->modes & PAT_SUSTAIN ) d->dwFlags |= ENV_VOLSUSTAIN;
+ if( (hw->modes & PAT_LOOP) && (hw->start_loop != hw->end_loop) ) d->dwFlags |= ENV_VOLLOOP;
+ d->nVolEnv = 6;
+ //if (!d->nVolEnv) d->dwFlags &= ~ENV_VOLUME;
+ d->nPanEnv = 0;
+ d->nVolSustainBegin = 1;
+ d->nVolSustainEnd = 1;
+ d->nVolLoopStart = 1;
+ d->nVolLoopEnd = 2;
+ d->nPanSustainBegin = 0;
+ d->nPanSustainEnd = 0;
+ d->nPanLoopStart = 0;
+ d->nPanLoopEnd = 0;
+ d->nGlobalVol = 64;
+ pat_modenv(hw, envpoint, envvolume);
+ inuse = 0;
+ for( u=0; u<6; u++)
+ {
+ if( envvolume[u] != 64 ) inuse = 1;
+ d->VolPoints[u] = envpoint[u];
+ d->VolEnv[u] = envvolume[u];
+ d->PanPoints[u] = 0;
+ d->PanEnv[u] = 0;
+ if (u)
+ {
+ if (d->VolPoints[u] < d->VolPoints[u-1])
+ {
+ d->VolPoints[u] &= 0xFF;
+ d->VolPoints[u] += d->VolPoints[u-1] & 0xFF00;
+ if (d->VolPoints[u] < d->VolPoints[u-1]) d->VolPoints[u] += 0x100;
+ }
+ }
+ }
+ if( !inuse ) d->nVolEnv = 0;
+ for( u=0; u<128; u++)
+ {
+ d->NoteMap[u] = u+1;
+ d->Keyboard[u] = smp;
+ }
+}
+#endif
+#ifdef NEWMIKMOD
+static void PATinst(UNIMOD *of, INSTRUMENT *d, int smp, int gm)
+#else
+static void PATinst(INSTRUMENTHEADER *d, int smp, int gm)
+#endif
+{
+ WaveHeader hw;
+ char s[32];
+ memset(s,0,32);
+ if( pat_readpat_attr(gm-1, &hw, 0) ) {
+ pat_setpat_inst(&hw, d, smp);
+ }
+ else {
+ hw.modes = PAT_16BIT|PAT_ENVELOPE|PAT_SUSTAIN|PAT_LOOP;
+ hw.start_loop = 0;
+ hw.end_loop = 30000;
+ hw.wave_size = 30000;
+// envelope rates and offsets pinched from timidity's acpiano.pat sample no 1
+ hw.envelope_rate[0] = 0x3f;
+ hw.envelope_rate[1] = 0x3f;
+ hw.envelope_rate[2] = 0x3f;
+ hw.envelope_rate[3] = 0x08|(3<<6);
+ hw.envelope_rate[4] = 0x3f;
+ hw.envelope_rate[5] = 0x3f;
+ hw.envelope_offset[0] = 246;
+ hw.envelope_offset[1] = 246;
+ hw.envelope_offset[2] = 246;
+ hw.envelope_offset[3] = 0;
+ hw.envelope_offset[4] = 0;
+ hw.envelope_offset[5] = 0;
+ strncpy(hw.reserved, midipat[gm-1], sizeof(hw.reserved));
+ pat_setpat_inst(&hw, d, smp);
+ }
+ if( hw.reserved[0] )
+ strncpy(s, hw.reserved, 32);
+ else
+ strncpy(s, midipat[gm-1], 32);
+#ifdef NEWMIKMOD
+ d->insname = DupStr(of->allochandle, s,28);
+#else
+ s[31] = '\0';
+ memset(d->name, 0, 32);
+ strcpy((char *)d->name, s);
+ strncpy(s, midipat[gm-1], 12);
+ s[11] = '\0';
+ memset(d->filename, 0, 12);
+ strcpy((char *)d->filename, s);
+#endif
+}
+
+#ifdef NEWMIKMOD
+static void pat_setpat_attr(WaveHeader *hw, UNISAMPLE *q, int gm)
+{
+ q->seekpos = gm; // dec_pat expects the midi samplenumber in this
+ q->speed = pat_patrate_to_C4SPD(hw->sample_rate , hw->root_frequency);
+ q->length = hw->wave_size;
+ q->loopstart = hw->start_loop;
+ q->loopend = hw->end_loop;
+ q->volume = 0x40;
+ if( hw->modes & PAT_16BIT ) {
+ q->format |= SF_16BITS;
+ q->length >>= 1;
+ q->loopstart >>= 1;
+ q->loopend >>= 1;
+ q->speed <<= 1;
+ }
+ if( (hw->modes & PAT_UNSIGNED)==0 ) q->format |= SF_SIGNED;
+ if( hw->modes & PAT_LOOP ) {
+ q->flags |= SL_LOOP;
+ if( hw->modes & PAT_PINGPONG ) q->flags |= SL_SUSTAIN_BIDI;
+ if( hw->modes & PAT_SUSTAIN ) q->flags |= SL_SUSTAIN_LOOP;
+ }
+}
+#else
+static void pat_setpat_attr(WaveHeader *hw, MODINSTRUMENT *q)
+{
+ q->nC4Speed = pat_patrate_to_C4SPD(hw->sample_rate , hw->root_frequency);
+ q->nLength = hw->wave_size;
+ q->nLoopStart = hw->start_loop;
+ q->nLoopEnd = hw->end_loop;
+ q->nVolume = 256;
+ if( hw->modes & PAT_16BIT ) {
+ q->nLength >>= 1;
+ q->nLoopStart >>= 1;
+ q->nLoopEnd >>= 1;
+ }
+ if( hw->modes & PAT_LOOP ) {
+ q->uFlags |= CHN_LOOP;
+ if( hw->modes & PAT_PINGPONG ) q->uFlags |= CHN_PINGPONGSUSTAIN;
+ if( hw->modes & PAT_SUSTAIN ) q->uFlags |= CHN_SUSTAINLOOP;
+ }
+}
+#endif
+
+// ==========================
+// Load those darned Samples!
+#ifdef NEWMIKMOD
+static void PATsample(UNIMOD *of, UNISAMPLE *q, int smp, int gm)
+#else
+static void PATsample(CSoundFile *cs, MODINSTRUMENT *q, int smp, int gm)
+#endif
+{
+ WaveHeader hw;
+ char s[32];
+ sprintf(s, "%d:%s", smp-1, midipat[gm-1]);
+#ifdef NEWMIKMOD
+ q->samplename = DupStr(of->allochandle, s,28);
+ if( pat_readpat_attr(gm-1, &hw, 0) ) {
+ pat_setpat_attr(&hw, q, gm);
+ pat_loops[smp-1] = (q->flags & (SL_LOOP | SL_SUSTAIN_LOOP))? 1: 0;
+ }
+ else {
+ q->seekpos = smp + MAXSMP + 1; // dec_pat expects the samplenumber in this
+ q->speed = C4SPD;
+ q->length = 30000;
+ q->loopstart = 0;
+ q->loopend = 30000;
+ q->volume = 0x40;
+
+ // Enable aggressive declicking for songs that do not loop and that
+ // are long enough that they won't be adversely affected.
+
+ q->flags |= SL_LOOP;
+ q->format |= SF_16BITS;
+ q->format |= SF_SIGNED;
+ }
+ if(!(q->flags & (SL_LOOP | SL_SUSTAIN_LOOP)) && (q->length > 5000))
+ q->flags |= SL_DECLICK;
+#else
+ s[31] = '\0';
+ memset(cs->m_szNames[smp], 0, 32);
+ strcpy(cs->m_szNames[smp], s);
+ q->nGlobalVol = 64;
+ q->nPan = 128;
+ q->uFlags = CHN_16BIT;
+ if( pat_readpat_attr(gm-1, &hw, 0) ) {
+ char *p;
+ pat_setpat_attr(&hw, q);
+ pat_loops[smp-1] = (q->uFlags & CHN_LOOP)? 1: 0;
+ if( hw.modes & PAT_16BIT ) p = (char *)malloc(hw.wave_size);
+ else p = (char *)malloc(hw.wave_size * sizeof(short int));
+ if( p ) {
+ if( hw.modes & PAT_16BIT ) {
+ dec_pat_Decompress16Bit((short int *)p, hw.wave_size>>1, gm - 1);
+ cs->ReadSample(q, (hw.modes&PAT_UNSIGNED)?RS_PCM16U:RS_PCM16S, (LPSTR)p, hw.wave_size);
+ }
+ else {
+ dec_pat_Decompress8Bit((short int *)p, hw.wave_size, gm - 1);
+ cs->ReadSample(q, (hw.modes&PAT_UNSIGNED)?RS_PCM16U:RS_PCM16S, (LPSTR)p, hw.wave_size * sizeof(short int));
+ }
+ free(p);
+ }
+ }
+ else {
+ char *p;
+ q->nC4Speed = C4SPD;
+ q->nLength = 30000;
+ q->nLoopStart = 0;
+ q->nLoopEnd = 30000;
+ q->nVolume = 256;
+ q->uFlags |= CHN_LOOP;
+ q->uFlags |= CHN_16BIT;
+ p = (char *)malloc(q->nLength*sizeof(short int));
+ if( p ) {
+ dec_pat_Decompress8Bit((short int *)p, q->nLength, smp + MAXSMP - 1);
+ cs->ReadSample(q, RS_PCM16S, (LPSTR)p, q->nLength*2);
+ free(p);
+ }
+ }
+#endif
+}
+
+// =====================================================================================
+BOOL PAT_Load_Instruments(void *c)
+{
+ uint32_t t;
+#ifdef NEWMIKMOD
+ UNIMOD *of = (UNIMOD *)c;
+ INSTRUMENT *d;
+ UNISAMPLE *q;
+ if( !pat_numsmp() ) pat_gmtosmp(1); // make sure there is a sample
+ of->numsmp = pat_numsmp();
+ of->numins = pat_numinstr();
+ if(!AllocInstruments(of)) return FALSE;
+ if(!AllocSamples(of, 0)) return FALSE;
+ d = of->instruments;
+ for(t=1; t<=of->numins; t++) {
+ PATinst(of, d, t, pat_smptogm(t));
+ d++;
+ }
+ q = of->samples;
+ for(t=1; t<=of->numsmp; t++) {
+ PATsample(of, q, t, pat_smptogm(t));
+ q++;
+ }
+ SL_RegisterDecompressor(&dec_pat); // fool him to generate samples
+#else
+ CSoundFile *of=(CSoundFile *)c;
+ if( !pat_numsmp() ) pat_gmtosmp(1); // make sure there is a sample
+ of->m_nSamples = pat_numsmp() + 1; // xmms modplug does not use slot zero
+ of->m_nInstruments = pat_numinstr() + 1;
+ for(t=1; t<of->m_nInstruments; t++) { // xmms modplug doesn't use slot zero
+ if( (of->Headers[t] = new INSTRUMENTHEADER) == NULL ) return FALSE;
+ memset(of->Headers[t], 0, sizeof(INSTRUMENTHEADER));
+ PATinst(of->Headers[t], t, pat_smptogm(t));
+ }
+ for(t=1; t<of->m_nSamples; t++) { // xmms modplug doesn't use slot zero
+ PATsample(of, &of->Ins[t], t, pat_smptogm(t));
+ }
+ // copy last of the mohicans to entry 0 for XMMS modinfo to work....
+ t = of->m_nInstruments - 1;
+ if( (of->Headers[0] = new INSTRUMENTHEADER) == NULL ) return FALSE;
+ memcpy(of->Headers[0], of->Headers[t], sizeof(INSTRUMENTHEADER));
+ memset(of->Headers[0]->name, 0, 32);
+ strncpy((char *)of->Headers[0]->name, "Timidity GM patches", 32);
+ t = of->m_nSamples - 1;
+ memcpy(&of->Ins[0], &of->Ins[t], sizeof(MODINSTRUMENT));
+#endif
+ return TRUE;
+}
+// =====================================================================================
+#ifdef NEWMIKMOD
+BOOL PAT_Load(PATHANDLE *h, UNIMOD *of, MMSTREAM *mmfile)
+#else
+BOOL CSoundFile::ReadPAT(const BYTE *lpStream, DWORD dwMemLength)
+#endif
+{
+ static int avoid_reentry = 0;
+ char buf[60];
+ int t;
+#ifdef NEWMIKMOD
+ UNISAMPLE *q;
+ INSTRUMENT *d;
+#define m_nDefaultTempo of->inittempo
+#else
+ PATHANDLE *h;
+ int numpat;
+ MMFILE mm, *mmfile;
+ MODINSTRUMENT *q;
+ INSTRUMENTHEADER *d;
+ if( !TestPAT(lpStream, dwMemLength) ) return FALSE;
+ h = PAT_Init();
+ if( !h ) return FALSE;
+ mmfile = &mm;
+ mm.mm = (char *)lpStream;
+ mm.sz = dwMemLength;
+ mm.pos = 0;
+#endif
+ while( avoid_reentry ) sleep(1);
+ avoid_reentry = 1;
+ pat_read_patname(h, mmfile);
+ h->samples = pat_read_numsmp(mmfile);
+ if( strlen(h->patname) )
+ sprintf(buf,"%s canon %d-v (Fr. Jacques)", h->patname, h->samples);
+ else
+ sprintf(buf,"%d-voice canon (Fr. Jacques)", h->samples);
+#ifdef NEWMIKMOD
+ of->songname = DupStr(of->allochandle, buf, strlen(buf));
+#else
+ if( strlen(buf) > 31 ) buf[31] = '\0'; // chop it of
+ strcpy(m_szNames[0], buf);
+#endif
+ m_nDefaultTempo = 60; // 120 / 2
+ t = (h->samples - 1) * 16 + 128;
+ if( t % 64 ) t += 64;
+ t = t / 64;
+#ifdef NEWMIKMOD
+ of->memsize = PTMEM_LAST; // Number of memory slots to reserve!
+ of->modtype = _mm_strdup(of->allochandle, PAT_Version);
+ of->reppos = 0;
+ of->numins = h->samples;
+ of->numsmp = h->samples;
+ of->initspeed = 6;
+ of->numchn = h->samples;
+ of->numpat = t;
+ of->numpos = of->numpat; // one repeating pattern
+ of->numtrk = of->numpat * of->numchn;
+ of->initvolume = 64;
+ of->pansep=128;
+ // allocate resources
+ if(!AllocPositions(of, of->numpos)) {
+ avoid_reentry = 0;
+ return FALSE;
+ }
+ if(!AllocInstruments(of)) {
+ avoid_reentry = 0;
+ return FALSE;
+ }
+ if(!AllocSamples(of, 0)) {
+ avoid_reentry = 0;
+ return FALSE;
+ }
+ // orderlist
+ for(t=0; t<of->numpos; t++)
+ of->positions[t] = t;
+ d = of->instruments;
+ for(t=1; t<=of->numins; t++) {
+ WaveHeader hw;
+ char s[32];
+ sprintf(s, "%s", h->patname);
+ d->insname = DupStr(of->allochandle, s,28);
+ pat_read_waveheader(mmfile, &hw, t);
+ pat_setpat_inst(&hw, d, t);
+ }
+ q = of->samples;
+ for(t=1; t<=of->numsmp; t++) {
+ WaveHeader hw;
+ char s[28];
+ pat_read_waveheader(mmfile, &hw, t);
+ pat_setpat_attr(&hw, q, _mm_ftell(mmfile));
+ memset(s,0,28);
+ if( hw.wave_name[0] )
+ sprintf(s, "%d:%s", t, hw.wave_name);
+ else
+ sprintf(s, "%d:%s", t, h->patname);
+ q->samplename = DupStr(of->allochandle, s,28);
+ if(!(q->flags & (SL_LOOP | SL_SUSTAIN_LOOP)) && (q->length > 5000))
+ q->flags |= SL_DECLICK;
+ q++;
+ }
+#else
+ m_nType = MOD_TYPE_PAT;
+ m_nInstruments = h->samples + 1; // we know better but use each sample in the pat...
+ m_nSamples = h->samples + 1; // xmms modplug does not use slot zero
+ m_nDefaultSpeed = 6;
+ m_nChannels = h->samples;
+ numpat = t;
+
+ m_dwSongFlags = SONG_LINEARSLIDES;
+ m_nMinPeriod = 28 << 2;
+ m_nMaxPeriod = 1712 << 3;
+ // orderlist
+ for(t=0; t < numpat; t++)
+ Order[t] = t;
+ for(t=1; t<(int)m_nInstruments; t++) { // xmms modplug doesn't use slot zero
+ WaveHeader hw;
+ char s[32];
+ if( (d = new INSTRUMENTHEADER) == NULL ) {
+ avoid_reentry = 0;
+ return FALSE;
+ }
+ memset(d, 0, sizeof(INSTRUMENTHEADER));
+ Headers[t] = d;
+ sprintf(s, "%s", h->patname);
+ s[31] = '\0';
+ memset(d->name, 0, 32);
+ strcpy((char *)d->name, s);
+ s[11] = '\0';
+ memset(d->filename, 0, 12);
+ strcpy((char *)d->filename, s);
+ pat_get_waveheader(mmfile, &hw, t);
+ pat_setpat_inst(&hw, d, t);
+ }
+ for(t=1; t<(int)m_nSamples; t++) { // xmms modplug doesn't use slot zero
+ WaveHeader hw;
+ char s[32];
+ char *p;
+ q = &Ins[t]; // we do not use slot zero
+ q->nGlobalVol = 64;
+ q->nPan = 128;
+ q->uFlags = CHN_16BIT;
+ pat_get_waveheader(mmfile, &hw, t);
+ pat_setpat_attr(&hw, q);
+ memset(s,0,32);
+ if( hw.wave_name[0] )
+ sprintf(s, "%d:%s", t, hw.wave_name);
+ else {
+ if( h->patname[0] )
+ sprintf(s, "%d:%s", t, h->patname);
+ else
+ sprintf(s, "%d:Untitled GM patch", t);
+ }
+ s[31] = '\0';
+ memset(m_szNames[t], 0, 32);
+ strcpy(m_szNames[t], s);
+ if( hw.modes & PAT_16BIT ) p = (char *)malloc(hw.wave_size);
+ else p = (char *)malloc(hw.wave_size * sizeof(short int));
+ if( p ) {
+ mmreadSBYTES(p, hw.wave_size, mmfile);
+ if( hw.modes & PAT_16BIT ) {
+ ReadSample(q, (hw.modes&PAT_UNSIGNED)?RS_PCM16U:RS_PCM16S, (LPSTR)p, hw.wave_size);
+ }
+ else {
+ pat_blowup_to16bit((short int *)p, hw.wave_size);
+ ReadSample(q, (hw.modes&PAT_UNSIGNED)?RS_PCM16U:RS_PCM16S, (LPSTR)p, hw.wave_size * sizeof(short int));
+ }
+ free(p);
+ }
+ }
+ // copy last of the mohicans to entry 0 for XMMS modinfo to work....
+ t = m_nInstruments - 1;
+ if( (Headers[0] = new INSTRUMENTHEADER) == NULL ) {
+ avoid_reentry = 0;
+ return FALSE;
+ }
+ memcpy(Headers[0], Headers[t], sizeof(INSTRUMENTHEADER));
+ memset(Headers[0]->name, 0, 32);
+ if( h->patname[0] )
+ strncpy((char *)Headers[0]->name, h->patname, 32);
+ else
+ strncpy((char *)Headers[0]->name, "Timidity GM patch", 32);
+ t = m_nSamples - 1;
+ memcpy(&Ins[0], &Ins[t], sizeof(MODINSTRUMENT));
+#endif
+#ifdef NEWMIKMOD
+ // ==============================
+ // Load the pattern info now!
+ if(!AllocTracks(of)) return 0;
+ if(!AllocPatterns(of)) return 0;
+ of->ut = utrk_init(of->numchn, h->allochandle);
+ utrk_memory_reset(of->ut);
+ utrk_local_memflag(of->ut, PTMEM_PORTAMENTO, TRUE, FALSE);
+ PAT_ReadPatterns(of, h, of->numpat);
+ // ============================================================
+ // set panning positions
+ for(t=0; t<of->numchn; t++) {
+ of->panning[t] = PAN_LEFT+((t+2)%5)*((PAN_RIGHT - PAN_LEFT)/5); // 0x30 = std s3m val
+ }
+#else
+ // ==============================
+ // Load the pattern info now!
+ PAT_ReadPatterns(Patterns, PatternSize, h, numpat);
+ // ============================================================
+ // set panning positions
+ for(t=0; t<(int)m_nChannels; t++) {
+ ChnSettings[t].nPan = 0x30+((t+2)%5)*((0xD0 - 0x30)/5); // 0x30 = std s3m val
+ ChnSettings[t].nVolume = 64;
+ }
+#endif
+ avoid_reentry = 0; // it is safe now, I'm finished
+#ifndef NEWMIKMOD
+ PAT_Cleanup(h); // we dont need it anymore
+#endif
+ return 1;
+}
+
+#ifdef NEWMIKMOD
+// =====================================================================================
+CHAR *PAT_LoadTitle(MMSTREAM *mmfile)
+// =====================================================================================
+{
+ PATHANDLE dummy;
+ pat_read_patname(&dummy, mmfile);
+ return(DupStr(NULL, dummy.patname,strlen(dummy.patname)));
+}
+
+MLOADER load_pat =
+{
+ "PAT",
+ "PAT loader 1.0",
+ 0x30,
+ NULL,
+ PAT_Test,
+ (void *(*)(void))PAT_Init,
+ (void (*)(ML_HANDLE *))PAT_Cleanup,
+ /* Every single loader seems to need one of these! */
+ (BOOL (*)(ML_HANDLE *, UNIMOD *, MMSTREAM *))PAT_Load,
+ PAT_LoadTitle
+};
+#endif