OpenAL Notes

Imran Rashid
4/20/01

    This is just record of the stuff I did while playing around with OpenAL.  Hopefully I will be able to use these to reconstruct something a little more organized and thought out later.

    Note that I did everything on a Macintosh, using CodeWarrior 6.0.   Hopefully everything would work on other platforms, but the details are going to be different, especially in the set-up section.  However, the second section, where I talk about the actual code, should be the same for all platforms.
    Also, I haven't bothered with doing anything in Carbon yet -- other ambitious mac programmers can try that later.

Step 1: Set-Up

Get the Source

    Go to www.openl.org.

    To get the source, click on the "Downloads" link and read their instructions.  I found that I couldn't download the stuff I needed to from their FTP server, so I had to use CVS.  If you don't know how to use CVS, sorry I can't really provide much of an explanation.  Note that you can get free cvs software at www.wincvs.org (even if you're a mac user, go here!! they have a mac client also).  The directions that are provided on the openAL website to use CVS worked just fine for me.

    Once you've done this, you should find an "openal" directory somewhere on you're hard drive.  For right now, it really doesn't matter where it is.

Look at the docs?

    At this point I figured I would look at the docs, only to find out there were no docs anywhere.  The "docs" directory contains nothing useful, and neither does the OpenAL website -- so this means I'm just going to be doing things by brute force...

Build the library

    At this point I went into the "mac" directory so I could build the OpenAL library.  There appeared to be two CodeWarrior project files: "openal_mac_cw6.mcp" and "openal_mac.mcp".  I figured the "cw6" part stood for "CodeWarrior 6", so I used that one.  If you have an earlier verison of codewarrior, you should probably use the other project.

    After opening up the project, I noticed the project had 4 targets:

[Graphics:Images/OpenALnotes_gr_1.gif]

    I didn't mess with any of the Carbon stuff.

    So first thing first, I built the openAL library.  All I had to do was compile, and it worked perfectly.  Of course, for this work, you've probably already to got to have a variety of headers and libraries already installed in the appropriate places -- I already have tons of stuff installed, so who knows which parts I actually needed.  If you get errors, I'd go to developer.apple.com and download there Universal Headers & Interfaces & Libraries, and the OpenGL SDK too.  That should cover everything.  Than I'd install that somewhere relative to CodeWarrior.

    After you compile, you should notice a file "OpenALLibrary" in the same directory.  This is the library that you're other files need to use.  It seems like this is a dynamically linked library, which is good for rigorous applications, but something of a pain for small applications like mine.  At some point, I want to try and create a statically linked library instead.  (I don't know that much about libraries, so what I just said might be incorrect.)
    The difficulty with a dynamically linked library is that when you create program which uses it, the program has to know where to find the library.  Rather than mess with figuring out where stuff should really go (presumably in your "Extensions" folder, but I'm not positive), I just figure I'll always dump the library in the same folder as my program.

Build DemoApp #1

    The next thing I did was built the Demo App.  This is in the same project file as the OpenALLibrary -- just change the target to Demo App.  Compile the program, and now you've got a working example of an OpenAL program.  Its pretty easy to figure out what it does -- just play with the buttons.
    Since I used the dynamically linked library, whenever I moved the application to another directory, I had to copy the "OpenALLibrary" to the same location to make it work.  This is quite a pain...

Build DemoApp #2

    There's another more complicated DemoApp in another location.   The nice thing about this application is that it shows OpenGL and OpenAL together.

    To build the application, I went to "openal/demos/XLDemo/mac", and opened the file "xldemo_cw6.mcp".  It compiled without any problems -- but getting it to run was a little tricky.

    First I had the problem that it couldn't find the "OpenALLibrary", so I copied the library over and ran it again.  This time, it started up OK, but it immediately quite before doing anything I could see.  To try and figure out what was going wrong, I stepped through the program with the debugger.
    Eventually, I found that it quite in the middle of a call to

void Object::Init (char* FileName)

which is on line 80 of the file "openal:demos:XLDEMO:COMMON:object.cpp".
    The problem appeared to be because the program couldn't open some of the files that it was suppose to load.  (Specifically, the "Sky.mdl" file.)   I realized that almost all of these files were in the directory "openal:demos:XLDEMO:COMMON:", so I decided I would most stuff over there.  Specifically, I copied "xldemoApp", "OpenALLibrary", "radar.wav", and "phaser.wav" into that directory.  Now, when I tried running the program from that location it worked!! yeah!  And the program even had stereo sound, so I was impressed.

Step 2: Playing w/ OpenAL

    Now that I had everything up and running, I decided to start getting into the code and try and figure stuff out.  First I looked at DemoApp #1 in "openal:mac:".

Playing a sound

    I wanted to try and figure out how to get a sound to actually play -- so I traced through the code, and found this line:

if (hControl == (*hDocRec)->hBtnPlay)
{
    // play short sound if not already playing
    alGetSourcei(gSourceID[0], AL_SOURCE_STATE, &tempInt);
    if (tempInt != AL_PLAYING)
    {
        alSourcePlay(gSourceID[0]);
    }
}

(this starts on line 472 of demoapp.c).
    This appeared to be the code that was called when the "Play" button was pushed, so I figured it was responsible for playing sounds.  I noticed that if you pushed the "Play" button while the sound was still playing, nothing happened.  So I tried commenting out the if section of that code, so that the only thing that happened was a call to alSourcePlay:

if (hControl == (*hDocRec)->hBtnPlay)
{
    // play short sound if not already playing
    //alGetSourcei(gSourceID[0], AL_SOURCE_STATE, &tempInt);
    //if (tempInt != AL_PLAYING)
    //{
        alSourcePlay(gSourceID[0]);
    //}
}

    I recompiled the program, and now, everytime I clicked the play button, it started over -- so it worked like I thought.

Setting up the sound

    So now I knew how to get that sound to play, so I get make a call to alSourcePlay wherever I wanted to hear a sound.
    But there's a catch.  Look at the function call:

alSourcePlay(gSourceID[0]);

    Whats the gSourceID[0] part?? Obviously, the sound was somehow tied to the gSourceID array.  Now I needed to figure out where and how it was set-up.
    First, I noted that it was an a global unsigned int array.  As the name suggests, this probably doesn't hold the actual sample, just an integer which is an id to the audio sample.
    After a little bit of searching, it appears that loading of the audio sample is all done in the main function.  Just from reading the code (starting at about line 107), this is my understanding.  First this is called to load the data:

// load up some audio data...
alutLoadWAVFile((char *)"\pboom.wav",&formatBuffer1, (void **) &pBuffer1,(unsigned int *)&lBuffer1Len,&freqBuffer1);

It seems like the first argument is the name of the file to load,
the second is a ptr to an object that will be assigned the format type,
the third is a ptr to an object that will contain the actual data for the sample,
the fourth is a ptr to an object that will hold the length of the sample,
and the fifth argument is a ptr to an object that will hold the sample rate (in Hz) of the sample.
(note: I'm using the terms "ptr" and "object" loosely, I'm just trying to convey the meaning -- look up the actual type.)
    After the data has been read in, then comes the part where the samples are tied to the gSourceID array.  I'm not positive what this line does, probably some sort of initialization for the gSourceID array:

alGenSources(iNumSources, &gSourceID[0]);

here iNumSources is the length of the gSourceID array, which should also be the total number of sources you intend on having.
    Next is this call:

alGenBuffers(iNumSampleSets, &gSampleSet[0]);

here iNumSampleSets is the length of the gSampleSet array, which should also be the total number of sammples you intend on having.  At this point I also realized that gSampleSet was a global array of unsigned ints.  Note that I have no idea what the difference between a sample and a sound is at this point -- but at least in the examples where they just play wave files, it seems like they have a one-to-one correspondence.
    Next comes the part where gSourceID and gSampleSet are tied to the wav file we just loaded:

alBufferData(gSampleSet[0], formatBuffer1, pBuffer1, lBuffer1Len, freqBuffer1);
alSourcei(gSourceID[0], AL_BUFFER, gSampleSet[0]);

Note that formatBuffer1, pBuffer1,lBuggerLen, freqBuffer1 are all the args we used earlier in our call to alutLoadWAVFile (though in that function, we passed the address of the variables, and here we're using their actual values.)

    So now comes the real test: I decided to try and add a third sound, and see if it would work, and it did.  Here's the appropriate snippets of changes I made to the main function:

char *pBuffer1, *pBuffer2, *pBuffer3;
long lBuffer1Len, lBuffer2Len, lBuffer3Len;
ALenum formatBuffer1, formatBuffer2;
ALsizei freqBuffer1, freqBuffer2;

...

alutLoadWAVFile((char *)"\pphaser.wav",&formatBuffer2, (void **) &pBuffer3,(unsigned int *)&lBuffer3Len,&freqBuffer2);

...

iNumSources = 3;
iNumSampleSets = 5;

...

alBufferData(gSampleSet[4], formatBuffer2, pBuffer3, lBuffer3Len, freqBuffer2);
alSourcei(gSourceID[2], AL_BUFFER, gSampleSet[4]);
            

    Also, I incread the size of both gSampleSet and gSourceID by 1.  Then change any of the button behaviors to alSourcePlay(gSourceID[2]);

Step3: More advanced work

    At this point, I decided to start playing around w/ Demo #2, the "XLDEMO" application.  I found something very nice here -- the AudioEnv class -- this class makes it super easy to play sounds!!!  So I basically took this class and slighlty modified it to make my SoundPlayer class.  I cut out some of the stuff that I didn't use, and I added a few important features:
    1) For some reason, the LoadFile() and PlayFile() functions payed no attention to the value of the loop parameter -- they always made the sound loop.  I fixed this.
    2) I added a isSoundPlaying function.  I think this is a really important function to have if you want the behavior of your program to depend on what the sound is doing.  For example, in my Cellular Automata program, I don't advance to the next state until the sound is finished playing.
    3) I don't understand why, but it seems like they switched between 0- and 1-indexed arrays.  This confused me enough that I got lots of unmapped memory exceptions.  So i changed it all to 0-indexed.

    I also spent the LONGEST time trying to figure out one really weird problem.  It seemed like some WAV files would play fine and others wouldn't -- they just gave horrible static.  I didn't understand why, but it seemed that making the SoundPlayer class static instead of dynamically allocated improved the problem somewhat.  This should have clued me in, but I didn't realize until quite a while later that this was just a memory problem.  I hadn't given the program enough memory, and so it couldn't read the sound file properly.  The strange thing is, OpenAL didn't give any memory warnings or anything -- it just kept on going like nothing happened.  Anyway, just make sure you have enough memory -- I think this is probably only a problem for the mac right now.


Converted by Mathematica      May 6, 2001