PDA

View Full Version : A small hack to enable libmikmod to play 2-channel PCM files



dr_watson
September 29th, 2006, 04:54
libmikmod (or mikmodlib) is pretty good but it only supports mono PCM (Wav) files at the moment. You'll get a NULL pointer if trying to load a Wav file with 2 channels. Here is a small hack to make it load and play 2-channel Wav files. Technically speaking, this is not REAL stereo because, libmikmod somehow mixes the 2 channels together. But still, it's better than just having the sound effect played in your left ear only ;)

All we need to do is just to modify few lines in mwav.c as shown below (marked with @_@):





SAMPLE* Sample_LoadGeneric_internal(MREADER* reader)
{
SAMPLE *si=NULL;
WAV wh;
BOOL have_fmt=0;

/* read wav header */
_mm_read_string(wh.rID,4,reader);
wh.rLen = _mm_read_I_ULONG(reader);
_mm_read_string(wh.wID,4,reader);

/* check for correct header */
if(_mm_eof(reader)|| memcmp(wh.rID,"RIFF",4) || memcmp(wh.wID,"WAVE",4)) {
_mm_errno = MMERR_UNKNOWN_WAVE_TYPE;
return NULL;
}

/* scan all RIFF blocks until we find the sample data */
for(;;) {
CHAR dID[4];
ULONG len,start;

_mm_read_string(dID,4,reader);
len = _mm_read_I_ULONG(reader);
/* truncated file ? */
if (_mm_eof(reader)) {
_mm_errno=MMERR_UNKNOWN_WAVE_TYPE;
return NULL;
}
start = _mm_ftell(reader);

/* sample format block
should be present only once and before a data block */
if(!memcmp(dID,"fmt ",4)) {
wh.wFormatTag = _mm_read_I_UWORD(reader);
wh.nChannels = _mm_read_I_UWORD(reader);
wh.nSamplesPerSec = _mm_read_I_ULONG(reader);
wh.nAvgBytesPerSec = _mm_read_I_ULONG(reader);
wh.nBlockAlign = _mm_read_I_UWORD(reader);
wh.nFormatSpecific = _mm_read_I_UWORD(reader);

#ifdef MIKMOD_DEBUG
fprintf(stderr,"\rwavloader : wFormatTag=%04x blockalign=%04x nFormatSpc=%04x\n",
wh.wFormatTag,wh.nBlockAlign,wh.nFormatSpecific);
#endif

if((have_fmt)||(wh.nChannels>2)) { // @_@: changed 1 to 2
_mm_errno=MMERR_UNKNOWN_WAVE_TYPE;
return NULL;
}
have_fmt=1;
} else
/* sample data block
should be present only once and after a format block */
if(!memcmp(dID,"data",4)) {

int samp_size, num_samp; // @_@: newly added variables

if(!have_fmt) {
_mm_errno=MMERR_UNKNOWN_WAVE_TYPE;
return NULL;
}
if(!(si=(SAMPLE*)_mm_malloc(sizeof(SAMPLE)))) return NULL;
si->speed = wh.nSamplesPerSec*wh.nChannels; // @_@: changed from (/) to (*)
si->volume = 64;
si->length = len;

samp_size = 1;
if((wh.nBlockAlign/wh.nChannels) == 2) { // @_@: take into account the channel as well
si->flags = SF_16BITS | SF_SIGNED;
//si->length >>= 1; // @_@: keep the size
samp_size = 2; // @_@: 16 bit sample
}

if (wh.nChannels == 2) // @_@: stereo
si->flags |= SF_STEREO;

num_samp = si->length/samp_size/wh.nChannels; // @_@: setting up proper values for si
si->loopstart=0;
si->length=num_samp;
si->loopend=num_samp;
si->panning = PAN_CENTER; // @_@: this one is the most important one!
// was not initialized in the original code so
// it default to PAN_LEFT

si->inflags = si->flags;
SL_RegisterSample(si,MD_SNDFX,reader);
SL_LoadSamples();

/* skip any other remaining blocks - so in case of repeated sample
fragments, we'll return the first anyway instead of an error */
break;
}
/* onto next block */
_mm_fseek(reader,start+len,SEEK_SET);
if (_mm_eof(reader))
break;
}

return si;
}




I believe you can do the same hack to mikmodlib as well.

Cheers!

(btw.. the [ CODE ] [/ CODE ] tags seem to be broken)

Destroyer699
September 29th, 2006, 05:01
cool. Nice job.