A Guide To Starting With OpenAL
by Lee Winder


ADVERTISEMENT

Download the sample code for this article here.

So, What Exactly Is OpenAL?

OpenAL is a (smallish) API that is designed to aid cross platform development, specifically in the area of sound and music.  It is designed to be very simple at the expense of (personally speaking) functionality e.g. Panning is not supported (though some people would claim that today's 3D games would rarely, if ever, use features like that), though through clever coding, it is possible to simulate all the missing features.

Its style is influenced by OpenGL, in regards to function / variable name layout, type definitions etc. so any seasoned OpenGL coder will easily be able to find their way around, but even to novices, it is simple and easy to understand (even the docs are helpful!).

Okay, now you know a little about it, let's see how to use it!

Where Can I Get My Hands On It?

Of course, the first thing you need to do is actually get the OpenAL SDK.  You can do this by going to www.openAL.org .  It's only about 7MB, so it shouldn't take that long to do.  When you've installed it (for the sake of this article, I installed it at C:\oalsdk), have a quick look at the docs and the examples to see how simple it is to use.

Initialization

What we need to do first is to set up OpenAL so we can actually use its feature set.  This is extremely simple to do:

// Init openAL
alutInit(0, NULL);
// Clear Error Code (so we can catch any new errors)
alGetError();

That's it, now we are ready to use it!

Sources, Buffers and Listeners

Before we carry on, I'll explain the main concept behind OpenAL.  The API uses 3 main components: sources, buffers and listeners.  Here is a quick explanation of each -

Sources: A source in OpenAL is exactly what it sounds like, a source of a sound in the world.  A source is linked to one or more buffers, and then asked to play them.  They have position and orientation in the world, as well as velocity, for doppler effects.

Listener: A listener is (simply put) the ears of the world.  It is generally set at the location of the player, so that all sounds will be relative to the person playing.  This could be the actual camera location, or some other random position, whatever you want…

Buffers: Buffers are exactly like any other kind of sound buffers you may have used.  Sound is loaded into the buffer, and then the sound in the buffer is played.  The thing that differs from normal is that no functions directly call the buffer.  Instead, they are linked to a source (or multiple sources) and then the sources themselves play, playing the sound from the buffers in the order that they have been queued.

Okay, now that we know about the things that make OpenAL what it is, how do you use them?  Well, read on…

How Do We Create What We Need?

Buffers

The first things we should create are buffers, so we can actually store the sound we want to play. This is done with one simple call

ALuint buffers[NUM_BUFFERS];

// Create the buffers
alGenBuffers(NUM_BUFFERS, buffers);
if ((error = alGetError()) != AL_NO_ERROR)
{
  printf("alGenBuffers : %d", error);
  return 0;
}

All this does is create however many buffers you wish, ready to be filled with sound.  The error checking is there for completeness.

The buffers do not need to be created at the start of the program; they can be created at any time.  However, it's (in my opinion) neater if you create everything you need when you start.  Of course, this isn't always possible, so create them in exactly the same way if you need to.

Now, let's load in the sound(s) we want to use

ALenum     format;
ALsizei    size;
ALsizei    freq;
ALboolean  loop;
ALvoid*    data;

alutLoadWAVFile("exciting_sound.wav", &format, &data, &size, &freq, &loop);
if ((error = alGetError()) != AL_NO_ERROR)
{
  printf("alutLoadWAVFile exciting_sound.wav : %d", error);
  // Delete Buffers
  alDeleteBuffers(NUM_BUFFERS, buffers);
  return 0;
}

This loads in the file we have requested and fills in the given the data.  Again, the error check is there for completeness.  Note though the call to alDeleteBuffers(…).  Even though it has failed, we need to clean up after ourselves, unless of course we want memory leaks ;)…  There is also a function (alutLoadWAVMemory(…) ) that lets you load the data from memory.  Take a peek at the documentation for notes on that one.

Now that we have loaded in the sound, we need to fill up one of the buffers with the data.

alBufferData(buffers[0],format,data,size,freq);
if ((error = alGetError()) != AL_NO_ERROR)
{
  printf("alBufferData buffer 0 : %d", error);
  // Delete buffers
  alDeleteBuffers(NUM_BUFFERS, buffers);
  return 0;
}

This takes the data we received from alutLoadWAVFile(…) and puts it into the buffer, ready for it to be assigned to a waiting source and played.  Again, notice how we delete the buffers if we fail.

Now that the buffer is filled with data, we still have the sound data in memory also, taking up valuable space.  Get rid of it with:

alutUnloadWAV(format,data,size,freq);
if ((error = alGetError()) != AL_NO_ERROR)
{
  printf("alutUnloadWAV : %d", error);
  // Delete buffers
  alDeleteBuffers(NUM_BUFFERS, buffers);
  return 0;
}

This just deletes the no longer needed sound data from memory, as the data we actually need is stored in the buffer!

Sources

Okay, so we have loaded the sound into the buffer, but as I said earlier, we can't play it until we have attached it to a source.  Here's how to do that!

ALuint source[NUM_SOURCES];
// Generate the sources
alGenSources(NUM_SOURCES, source);
if ((error = alGetError()) != AL_NO_ERROR)
{
  printf("alGenSources : %d", error);
  return 0;
}

The above piece of code creates all the sources we need.  This is exactly the same procedure as alGenBuffers(…), and the same rules apply.    It is common to have far more sources than buffers, as a buffer can be attached to more than one source at the same time.  Now that the source is created we need to attach a buffer to it, so we can play it…

Attaching buffers to sources is again very easy.  There are two ways to do it:  One way is to attach a single buffer to the source, whereas the other way is to queue multiple buffers to the source, and play them in turn.  For the sake of this article, I'm just going to show you the first way.

alSourcei(source[0], AL_BUFFER, buffers[0]);
if ((error = alGetError()) != AL_NO_ERROR)
{
  printf("alSourcei : %d", error);
  return 0;
}

This takes the buffer and attaches it to the source, so that when we play the source this buffer is used.  The function alSourcei(…) is actually used in more than one place (in that it is a generic set integer property function for sources).  I won't really go into this here, maybe another day.

Now that we have set the source and buffer, we need to position the source so that we can actually simulate 3D sound.  The properties we will need to set are position, orientation and velocity.  These are pretty self explanatory, but I'll mention velocity a little.  Velocity is used to simulate a doppler effect.  Personally, I can't stand doppler effects so I set the array to 0's, but if you want the effect, the effect will be calculated in respect to the listener's position (That's coming up next!)

Okay to set these values, you use one simple function.

ALvoid alSourcefv(ALuint source,ALenum pname,ALfloat *values);

This function is similar to the alSourcei(…) function except this is a generic function to set an array of floats as a source property, where ALfloat *values will be an array of 3 ALfloats.  So, to set our position, velocity and orientaion we call :

alSourcefv (source[0], AL_POSITION, sourcePos);
alSourcefv (source[0], AL_VELOCITY, sourceVel);
alSourcefv (source[0], AL_DIRECTION, sourceOri);

These variables default them selves to 0 if you do not set them, although it is more than likely that you will.  I haven't included it here, but it is best to check for errors in the standard way, because it's a good idea to catch them as they happen.

Okay, that is a source set up, now we need one more thing - the Listener.

Listeners

As I said before, the listener is the ears of the world, and as such, you can only have one of them. It wouldn't make sense to have more than one, as OpenAL wouldn't know which one to make the sounds relevant to.  Because there is only ever one, we do not need to create it, as it comes ready made.  All we have to do is set where it is, the direction it is facing and its velocity (again for doppler effects).  This is done in exactly the same way as sources with the one function

ALvoid alListenerfv(ALenum pname,ALfloat *values);

This works in exactly the same way as alSourcefv(…) and alSourcei(…) except it works on the listener.  As I said, the listener comes ready made; you do not have to specify it.  To set the position, orientation and velocity call:

alListenerfv(AL_POSITION,listenerPos);
alListenerfv(AL_VELOCITY,listenerVel);
alListenerfv(AL_ORIENTATION,listenerOri);

Again, it will be a good idea to check for errors.  One thing to note here is that in this case, orientation is an array of 6 ALfloat's .  The first three define its look at direction, while the second three define the 'up' vector.  Once this is done, everything is finally set up (It wasn't that hard was it?).  Now we can get down to the thing we are all here for:  Playing the sound at last

The Hills Are Alive, With The Sound of Music (!)

alSourcePlay(source[0]);

Yup, that's it.  That's all that is required to play your sample.  OpenAL will calculate the volume regarding the distance and the orientation.  If you specified a velocity for either sources or listeners you'll also get doppler effects.  Now that it's playing, we might need it to stop playing…

alSourceStop(source[0]);

Or reset it to the start of the sample…

alSourceRewind(source[0]);

Or pause it…

alSourcePause(source[0]);

To unpause it, just call alSourcePlay(…) on the sample.

I've rushed through those, but they really are that simple.  If you have a look at the OpenAL Specification and Reference document, on page 32, it will tell you exactly the actions that will happen if you call these functions on a source, depending on its state.  It's simple enough, but I'm not going to go into it now.

That's all you need to do to get OpenAL up and running, but what about shutting it down?

Shutting It All Down

Shutting down is even easier than it was to set everything up.  We just need to get rid of our sources, buffers and close OpenAL.

alDeleteSources(NUM_SOURCES, source);
alDeleteBuffers(NUM_BUFFERS, buffers);

This will delete all the sources in that array of sources.  Make sure you have deleted all of them before you call the next line.

alutExit();

And that's it. OpenAL is now shut down and you can carry on with what ever you want to do next

As this is the first article I have written, I hope it is of some use to at least one person.  There's lots more to OpenAL, but this simple how-to should easily get you set up so you can look at the more complicated features (if there are any) of the API.

Lee Winder
Leewinder@hotmail.com

Discuss this article in the forums


Date this article was posted to GameDev.net: 10/23/2003
(Note that this date does not necessarily correspond to the date the article was written)

See Also:
General
Sweet Snippets

© 1999-2011 Gamedev.net. All rights reserved. Terms of Use Privacy Policy
Comments? Questions? Feedback? Click here!