diff options
Diffstat (limited to 'src/libs/mikmod/sloader.c')
-rw-r--r-- | src/libs/mikmod/sloader.c | 519 |
1 files changed, 519 insertions, 0 deletions
diff --git a/src/libs/mikmod/sloader.c b/src/libs/mikmod/sloader.c new file mode 100644 index 0000000..0b8c685 --- /dev/null +++ b/src/libs/mikmod/sloader.c @@ -0,0 +1,519 @@ +/* MikMod sound library + (c) 1998, 1999, 2000, 2001 Miodrag Vallat and others - see file AUTHORS + for complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + Routines for loading samples. The sample loader utilizes the routines + provided by the "registered" sample loader. + +==============================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include "mikmod_internals.h" + +static int sl_rlength; +static SWORD sl_old; +static SWORD *sl_buffer=NULL; +static SAMPLOAD *musiclist=NULL,*sndfxlist=NULL; + +/* size of the loader buffer in words */ +#define SLBUFSIZE 2048 + +/* IT-Compressed status structure */ +typedef struct ITPACK { + UWORD bits; /* current number of bits */ + UWORD bufbits; /* bits in buffer */ + SWORD last; /* last output */ + UBYTE buf; /* bit buffer */ +} ITPACK; + +BOOL SL_Init(SAMPLOAD* s) +{ + if(!sl_buffer) + if(!(sl_buffer=MikMod_malloc(SLBUFSIZE*sizeof(SWORD)))) return 0; + + sl_rlength = s->length; + if(s->infmt & SF_16BITS) sl_rlength>>=1; + sl_old = 0; + + return 1; +} + +void SL_Exit(SAMPLOAD *s) +{ + if(sl_rlength>0) _mm_fseek(s->reader,sl_rlength,SEEK_CUR); + if(sl_buffer) { + MikMod_free(sl_buffer); + sl_buffer=NULL; + } +} + +/* unpack a 8bit IT packed sample */ +static BOOL read_itcompr8(ITPACK* status,MREADER *reader,SWORD *sl_buffer,UWORD count,UWORD* incnt) +{ + SWORD *dest=sl_buffer,*end=sl_buffer+count; + UWORD x,y,needbits,havebits,new_count=0; + UWORD bits = status->bits; + UWORD bufbits = status->bufbits; + SBYTE last = status->last; + UBYTE buf = status->buf; + + while (dest<end) { + needbits=new_count?3:bits; + x=havebits=0; + while (needbits) { + /* feed buffer */ + if (!bufbits) { + if((*incnt)--) + buf=_mm_read_UBYTE(reader); + else + buf=0; + bufbits=8; + } + /* get as many bits as necessary */ + y = needbits<bufbits?needbits:bufbits; + x|= (buf & ((1<<y)- 1))<<havebits; + buf>>=y; + bufbits-=y; + needbits-=y; + havebits+=y; + } + if (new_count) { + new_count = 0; + if (++x >= bits) + x++; + bits = x; + continue; + } + if (bits<7) { + if (x==(1<<(bits-1))) { + new_count = 1; + continue; + } + } + else if (bits<9) { + y = (0xff >> (9-bits)) - 4; + if ((x>y)&&(x<=y+8)) { + if ((x-=y)>=bits) + x++; + bits = x; + continue; + } + } + else if (bits<10) { + if (x>=0x100) { + bits=x-0x100+1; + continue; + } + } else { + /* error in compressed data... */ + _mm_errno=MMERR_ITPACK_INVALID_DATA; + return 0; + } + + if (bits<8) /* extend sign */ + x = ((SBYTE)(x <<(8-bits))) >> (8-bits); + *(dest++)= (last+=x) << 8; /* convert to 16 bit */ + } + status->bits = bits; + status->bufbits = bufbits; + status->last = last; + status->buf = buf; + return !!(dest-sl_buffer); +} + +/* unpack a 16bit IT packed sample */ +static BOOL read_itcompr16(ITPACK *status,MREADER *reader,SWORD *sl_buffer,UWORD count,UWORD* incnt) +{ + SWORD *dest=sl_buffer,*end=sl_buffer+count; + SLONG x,y,needbits,havebits,new_count=0; + UWORD bits = status->bits; + UWORD bufbits = status->bufbits; + SWORD last = status->last; + UBYTE buf = status->buf; + + while (dest<end) { + needbits=new_count?4:bits; + x=havebits=0; + while (needbits) { + /* feed buffer */ + if (!bufbits) { + if((*incnt)--) + buf=_mm_read_UBYTE(reader); + else + buf=0; + bufbits=8; + } + /* get as many bits as necessary */ + y=needbits<bufbits?needbits:bufbits; + x|=(buf &((1<<y)-1))<<havebits; + buf>>=y; + bufbits-=y; + needbits-=y; + havebits+=y; + } + if (new_count) { + new_count = 0; + if (++x >= bits) + x++; + bits = x; + continue; + } + if (bits<7) { + if (x==(1<<(bits-1))) { + new_count=1; + continue; + } + } + else if (bits<17) { + y=(0xffff>>(17-bits))-8; + if ((x>y)&&(x<=y+16)) { + if ((x-=y)>=bits) + x++; + bits = x; + continue; + } + } + else if (bits<18) { + if (x>=0x10000) { + bits=x-0x10000+1; + continue; + } + } else { + /* error in compressed data... */ + _mm_errno=MMERR_ITPACK_INVALID_DATA; + return 0; + } + + if (bits<16) /* extend sign */ + x = ((SWORD)(x<<(16-bits)))>>(16-bits); + *(dest++)=(last+=x); + } + status->bits = bits; + status->bufbits = bufbits; + status->last = last; + status->buf = buf; + return !!(dest-sl_buffer); +} + +static BOOL SL_LoadInternal(void* buffer,UWORD infmt,UWORD outfmt,int scalefactor,ULONG length,MREADER* reader,BOOL dither) +{ + SBYTE *bptr = (SBYTE*)buffer; + SWORD *wptr = (SWORD*)buffer; + int stodo,t,u; + + int result,c_block=0; /* compression bytes until next block */ + ITPACK status; + UWORD incnt; + + while(length) { + stodo=(length<SLBUFSIZE)?length:SLBUFSIZE; + + if(infmt&SF_ITPACKED) { + sl_rlength=0; + if (!c_block) { + status.bits = (infmt & SF_16BITS) ? 17 : 9; + status.last = status.bufbits = 0; + incnt=_mm_read_I_UWORD(reader); + c_block = (infmt & SF_16BITS) ? 0x4000 : 0x8000; + if(infmt&SF_DELTA) sl_old=0; + } + if (infmt & SF_16BITS) { + if(!(result=read_itcompr16(&status,reader,sl_buffer,stodo,&incnt))) + return 1; + } else { + if(!(result=read_itcompr8(&status,reader,sl_buffer,stodo,&incnt))) + return 1; + } + if(result!=stodo) { + _mm_errno=MMERR_ITPACK_INVALID_DATA; + return 1; + } + c_block -= stodo; + } else { + if(infmt&SF_16BITS) { + if(infmt&SF_BIG_ENDIAN) + _mm_read_M_SWORDS(sl_buffer,stodo,reader); + else + _mm_read_I_SWORDS(sl_buffer,stodo,reader); + } else { + SBYTE *src; + SWORD *dest; + + reader->Read(reader,sl_buffer,sizeof(SBYTE)*stodo); + src = (SBYTE*)sl_buffer; + dest = sl_buffer; + src += stodo;dest += stodo; + + for(t=0;t<stodo;t++) { + src--;dest--; + *dest = (*src)<<8; + } + } + sl_rlength-=stodo; + } + + if(infmt & SF_DELTA) + for(t=0;t<stodo;t++) { + sl_buffer[t] += sl_old; + sl_old = sl_buffer[t]; + } + + if((infmt^outfmt) & SF_SIGNED) + for(t=0;t<stodo;t++) + sl_buffer[t]^= 0x8000; + + if(scalefactor) { + int idx = 0; + SLONG scaleval; + + /* Sample Scaling... average values for better results. */ + t= 0; + while(t<stodo && length) { + scaleval = 0; + for(u=scalefactor;u && t<stodo;u--,t++) + scaleval+=sl_buffer[t]; + sl_buffer[idx++]=scaleval/(scalefactor-u); + length--; + } + stodo = idx; + } else + length -= stodo; + + if (dither) { + if((infmt & SF_STEREO) && !(outfmt & SF_STEREO)) { + /* dither stereo to mono, average together every two samples */ + SLONG avgval; + int idx = 0; + + t=0; + while(t<stodo && length) { + avgval=sl_buffer[t++]; + avgval+=sl_buffer[t++]; + sl_buffer[idx++]=avgval>>1; + length-=2; + } + stodo = idx; + } + } + + if(outfmt & SF_16BITS) { + for(t=0;t<stodo;t++) + *(wptr++)=sl_buffer[t]; + } else { + for(t=0;t<stodo;t++) + *(bptr++)=sl_buffer[t]>>8; + } + } + return 0; +} + +BOOL SL_Load(void* buffer,SAMPLOAD *smp,ULONG length) +{ + return SL_LoadInternal(buffer,smp->infmt,smp->outfmt,smp->scalefactor, + length,smp->reader,0); +} + +/* Registers a sample for loading when SL_LoadSamples() is called. */ +SAMPLOAD* SL_RegisterSample(SAMPLE* s,int type,MREADER* reader) +{ + SAMPLOAD *news,**samplist,*cruise; + + if(type==MD_MUSIC) { + samplist = &musiclist; + cruise = musiclist; + } else + if (type==MD_SNDFX) { + samplist = &sndfxlist; + cruise = sndfxlist; + } else + return NULL; + + /* Allocate and add structure to the END of the list */ + if(!(news=(SAMPLOAD*)MikMod_malloc(sizeof(SAMPLOAD)))) return NULL; + + if(cruise) { + while(cruise->next) cruise=cruise->next; + cruise->next = news; + } else + *samplist = news; + + news->infmt = s->flags & SF_FORMATMASK; + news->outfmt = news->infmt; + news->reader = reader; + news->sample = s; + news->length = s->length; + news->loopstart = s->loopstart; + news->loopend = s->loopend; + + return news; +} + +static void FreeSampleList(SAMPLOAD* s) +{ + SAMPLOAD *old; + + while(s) { + old = s; + s = s->next; + MikMod_free(old); + } +} + +/* Returns the total amount of memory required by the samplelist queue. */ +static ULONG SampleTotal(SAMPLOAD* samplist,int type) +{ + int total = 0; + + while(samplist) { + samplist->sample->flags= + (samplist->sample->flags&~SF_FORMATMASK)|samplist->outfmt; + total += MD_SampleLength(type,samplist->sample); + samplist=samplist->next; + } + + return total; +} + +static ULONG RealSpeed(SAMPLOAD *s) +{ + return(s->sample->speed/(s->scalefactor?s->scalefactor:1)); +} + +static BOOL DitherSamples(SAMPLOAD* samplist,int type) +{ + SAMPLOAD *c2smp=NULL; + ULONG maxsize, speed; + SAMPLOAD *s; + + if(!samplist) return 0; + + if((maxsize=MD_SampleSpace(type)*1024)) + while(SampleTotal(samplist,type)>maxsize) { + /* First Pass - check for any 16 bit samples */ + s = samplist; + while(s) { + if(s->outfmt & SF_16BITS) { + SL_Sample16to8(s); + break; + } + s=s->next; + } + /* Second pass (if no 16bits found above) is to take the sample with + the highest speed and dither it by half. */ + if(!s) { + s = samplist; + speed = 0; + while(s) { + if((s->sample->length) && (RealSpeed(s)>speed)) { + speed=RealSpeed(s); + c2smp=s; + } + s=s->next; + } + if (c2smp) + SL_HalveSample(c2smp,2); + } + } + + /* Samples dithered, now load them ! */ + s = samplist; + while(s) { + /* sample has to be loaded ? -> increase number of samples, allocate + memory and load sample. */ + if(s->sample->length) { + if(s->sample->seekpos) + _mm_fseek(s->reader, s->sample->seekpos, SEEK_SET); + + /* Call the sample load routine of the driver module. It has to + return a 'handle' (>=0) that identifies the sample. */ + s->sample->handle = MD_SampleLoad(s, type); + s->sample->flags = (s->sample->flags & ~SF_FORMATMASK) | s->outfmt; + if(s->sample->handle<0) { + FreeSampleList(samplist); + if(_mm_errorhandler) _mm_errorhandler(); + return 1; + } + } + s = s->next; + } + + FreeSampleList(samplist); + return 0; +} + +BOOL SL_LoadSamples(void) +{ + BOOL ok; + + _mm_critical = 0; + + if((!musiclist)&&(!sndfxlist)) return 0; + ok=DitherSamples(musiclist,MD_MUSIC)||DitherSamples(sndfxlist,MD_SNDFX); + musiclist=sndfxlist=NULL; + + return ok; +} + +void SL_Sample16to8(SAMPLOAD* s) +{ + s->outfmt &= ~SF_16BITS; + s->sample->flags = (s->sample->flags&~SF_FORMATMASK) | s->outfmt; +} + +void SL_Sample8to16(SAMPLOAD* s) +{ + s->outfmt |= SF_16BITS; + s->sample->flags = (s->sample->flags&~SF_FORMATMASK) | s->outfmt; +} + +void SL_SampleSigned(SAMPLOAD* s) +{ + s->outfmt |= SF_SIGNED; + s->sample->flags = (s->sample->flags&~SF_FORMATMASK) | s->outfmt; +} + +void SL_SampleUnsigned(SAMPLOAD* s) +{ + s->outfmt &= ~SF_SIGNED; + s->sample->flags = (s->sample->flags&~SF_FORMATMASK) | s->outfmt; +} + +void SL_HalveSample(SAMPLOAD* s,int factor) +{ + s->scalefactor=factor>0?factor:2; + + s->sample->divfactor = s->scalefactor; + s->sample->length = s->length / s->scalefactor; + s->sample->loopstart = s->loopstart / s->scalefactor; + s->sample->loopend = s->loopend / s->scalefactor; +} + + +/* ex:set ts=4: */ |