forums.ps2dev.org Forum Index forums.ps2dev.org
Homebrew PS2, PSP & PS3 Development Discussions
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

A small hack to enable libmikmod to play 2-channel PCM files

 
Post new topic   Reply to topic    forums.ps2dev.org Forum Index -> PSP Development
View previous topic :: View next topic  
Author Message
dr_watson



Joined: 28 Nov 2005
Posts: 42

PostPosted: Fri Sep 29, 2006 11:57 am    Post subject: A small hack to enable libmikmod to play 2-channel PCM files Reply with quote

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 @_@):

Code:


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!
Back to top
View user's profile Send private message
bpoint



Joined: 10 Mar 2005
Posts: 24
Location: Okinawa, Japan

PostPosted: Tue Oct 10, 2006 5:28 pm    Post subject: Reply with quote

Your hack does not do what you think it does. libmikmod does not "somehow" mix the two channels together -- you simply set the playback sample rate to twice the speed of the original sample:

Code:
si->speed  = wh.nSamplesPerSec*wh.nChannels;   // @_@: changed from (/) to (*)


This means a 44100hz sample will be played back at 88200hz, effectively skipping every other sample -- which happens to be the right channel since the data is interleaved. So all your patch is doing is allowing libmikmod to load a stereo sample and play back only the left channel.

If you use this hack and try to change the sample rate to anything other than the default, the skipped right channel will be intermixed with the left and will sound very strange.
Back to top
View user's profile Send private message
dr_watson



Joined: 28 Nov 2005
Posts: 42

PostPosted: Thu Oct 12, 2006 1:28 pm    Post subject: Reply with quote

bpoint wrote:
Your hack does not do what you think it does. libmikmod does not "somehow" mix the two channels together -- you simply set the playback sample rate to twice the speed of the original sample:

Code:
si->speed  = wh.nSamplesPerSec*wh.nChannels;   // @_@: changed from (/) to (*)


This means a 44100hz sample will be played back at 88200hz, effectively skipping every other sample -- which happens to be the right channel since the data is interleaved. So all your patch is doing is allowing libmikmod to load a stereo sample and play back only the left channel.

If you use this hack and try to change the sample rate to anything other than the default, the skipped right channel will be intermixed with the left and will sound very strange.


well... I'm afraid this is not 100% correct though. I tested the hack with a WAV file that has different sounds at left and right channel. When I played it, I could hear BOTH sounds at both ears. From your theory, I should have heard the sound from the left channel only. Any ideas?
Back to top
View user's profile Send private message
Raphael



Joined: 17 Jan 2006
Posts: 646
Location: Germany

PostPosted: Thu Oct 12, 2006 4:53 pm    Post subject: Reply with quote

Quote:
When I played it, I could hear BOTH sounds at both ears. From your theory, I should have heard the sound from the left channel only. Any ideas?
Yes, because it doesn't skip samples when the sample rate is higher then the number of samples in the stream would suggest, it would only read the first half of the stream and output that at double speed. This then compensates for the half playback speed induced when playing double the number of channels without real stereo output (mono played back on stereo speakers will be output at both sides - it would not make sense to play a mono title only on one side, since mono doesn't mean only left or right, but rather "center"). So what you get with your hack is playing the left and right signals on both sides at double rate, thus creating your "mixing" (which only happens in your ears, not in libmikmod or the psp). If you want real stereo playback, you'd need to keep the si->speed at the normal speed (no / or *) and adjust the output functions of libmikmod to tell the psp that the incoming samples are indeed stereo (so the appropriate samples will be sent to the appropriate sides).

Regards,
Raphael
_________________
<Don't push the river, it flows.>
http://wordpress.fx-world.org - my devblog
http://wiki.fx-world.org - VFPU documentation wiki

Alexander Berl
Back to top
View user's profile Send private message Visit poster's website
Display posts from previous:   
Post new topic   Reply to topic    forums.ps2dev.org Forum Index -> PSP Development All times are GMT + 10 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group