Friday, July 10, 2009

How to play external MP3 Files with libogc's MP3Player

It's hard for me to get into detailing the subject of this post without me providing a little preface detailing my experiences so far with libogc's MP3Player. This way, you'll know where I stand and why it takes such a complex method to play externally loaded .mp3s. Also, be aware that I've spent at least a good 15 hours working with the MP3Player over the course of my Wii Homebrew developments.

Libogc's MP3Player is nice in that it allows the programmer to play MP3 files loaded externally from the SD Card, or loaded internally from the RAM as a buffer. While I've always had surefire success with the latter method, the former has been hit or miss. When I have gotten it to work, I've always been unable to get the .mp3s to play on a loop. Any calls relating to the FILE holding the .mp3's freezes the Wii forcing me to physically get up from my chair and unplug the Wii. To that end, it's quite a conundrum to have such a wonderful MP3Player_PlayBuffer() function and such an unstable MP3Player_PlayFile() function.

However, to this day, I still haven't given up trying to play externally loaded MP3 files with libogc's MP3 Player. And today, I'm pleased to post on my blog that I've finally found a very stable, working, solution. The solution is, to put it plainly, to load an external MP3 file, turn it into a buffer, and then play the buffer with the MP3 Player. The rest of this post will detail the exact the code I used to get this to work.

First, in your makefile, under LIBS:, include the libasnd.a libmad.a libraries:
-lasnd -lmad


Now, wherever necessary, include asnd and the libogc MP3Player: 
#include (asndlib.h)
#include (mp3player.h)

And of course, initialize asnd and the libogc MP3Player before you do anything with them:
ASND_Init();
MP3Player_Init();

Let's declare all of the variables we're going to use:
FILE *BGM = 0;//This will store an MP3 on the SD Card
long lSize;//This will store the size of the FILE BGM
char * buffer;//This is our buffer that will take the place of the external MP3
size_t result;//This is the size of the buffer
bool mp3isready = false;//This keeps the music from trying to loop before the buffer is made

And now prototype a simple function that will keep the MP3 looping:
void LoopTheMP3();

Now let's load an MP3 file:
mp3isready = false;//Keeps LoopTheMP3() from working before the buffer is ready

BGM = fopen("sd:/MUSIC/bgm.mp3", "rb");

Let's obtain the size of said file:
fseek (BGM , 0 , SEEK_END);
lSize = ftell (BGM);
rewind (BGM);//Return to the beginning of the MP3 for good measure

Create the Buffer:
buffer = (char*) malloc (sizeof(char)*lSize);//Allocate memory to contain the whole file
result = fread (buffer,1,lSize,BGM);//Copy the file into the buffer

Play the Buffer:
fclose(BGM);//Since we no longer need BGM, get rid of it
MP3Player_PlayBuffer(buffer, lSize, NULL);//Play the buffer
mp3isready = true;//Let LoopTheMP3() know it can start working

If you're going to change songs, be sure to clear the buffer beforehand:
free(buffer);

Lastly, heres the code for our function to keep the MP3 Playing continuously:
void LoopTheMP3(){

if(mp3isready && !MP3Player_IsPlaying())
MP3Player_PlayBuffer(buffer, lSize, NULL);

}

As is the nature of programming, this code will probably need to be adapted and changed a bit to suit the flow of your program. However, I assure you that it does work and is the most stable way I've encountered yet to play externally loaded MP3 files. I have pastied the exact code I am using in Wii Shooting Gallery here to show you an example of that. I have another app (with source of course) made just to demonstrate the functionality here.

Special thanks go out to scanff who, without his support, I would've given up on libogc's MP3Player before ever even trying a buffer (that means there would've been no sound in WiiBreaker without his help :-O). Thanks to him, I've managed to continually expand my knowledge and bring you the solution I found; detailed in this post.

6 comments:

  1. I do not have much knowledge before reading this article about it but after reading the whole article I got very nice information from it.
    mp3 player

    ReplyDelete
  2. Hey and thanks for this helpful article!

    How would I go about pausing an MP3?
    My idea was to stop it and then use PlayBuffer to continue playing from the point where it stopped - but how would I determine that point?

    ReplyDelete
  3. Hi Arikado,
    Thanks for this informative article.
    I tried using your code but when I run it on the console I get a code dump. Is there something wrong with the code.?
    cheers
    Violino
    if(cycle == 1)
    BGM = fopen("sd:/apps/mp3demo/data/bgm.mp3", "rb");

    if(cycle == 2){
    fseek (BGM , 0 , SEEK_END);
    lSize = ftell (BGM);
    rewind (BGM);//Return to the beginning of the MP3 for good measure
    }

    if(cycle == 3){
    buffer = (char*) malloc (sizeof(char)*lSize);//Allocate memory to contain the whole file
    result = fread (buffer,1,lSize,BGM);//Copy the file into the buffer
    }

    if(cycle == 4){
    fclose(BGM);//Since we no longer need BGM, get rid of it
    MP3Player_PlayBuffer(buffer, lSize, NULL);//Play the buffer
    mp3isready = true;//Let LoopTheMP3() know it can start working
    }

    LoopTheMP3();

    if(cycle > 0 && cycle < 4){
    sleep(1);
    cycle++;
    }

    ReplyDelete
  4. Hm ... Its been awhile since I went through all of this. If you could send me your code ( castlevania7689@yahoo.com ) I'll run through it and try to get back to you before the holidays. Cheers.

    ReplyDelete