Sources Sharing Buffers: Lesson 5
Author: Jesse Maurais | From: devmaster.net
Modified for LWJGL by: Brian Matzon
At this point in the OpenAL series I will show one method of having your
buffers be shared among many sources. This is a very logical and natural
step, and it is so easy that some of you may have already done this yourself.
If you have you may just skip this tutorial in total and move on. But for
those keeners who want to read all of the info I've got to give, you may
find this interesting.
Well, here we go. I've decided to only go over bits of the code that are
significant, since most of the code has been repeated so far in the series.
Check out the full source code in the download.
import java.io.IOException; import java.nio.FloatBuffer; import java.nio.IntBuffer; import org.lwjgl.BufferUtils; import org.lwjgl.LWJGLException; import org.lwjgl.openal.AL; import org.lwjgl.openal.AL10; import org.lwjgl.util.WaveData; public class Lesson5 { /** Index of thunder sound */ public static final int THUNDER = 0; /** Index of waterdrop sound */ public static final int WATERDROP = 1; /** Index of stream sound */ public static final int STREAM = 2; /** Index of rain sound */ public static final int RAIN = 2; /** Index of chimes sound */ public static final int CHIMES = 2; /** Index of ocean sound */ public static final int OCEAN = 2; /** Maximum data buffers we will need. */ public static final int NUM_BUFFERS = 6; /** Buffers hold sound data. */ IntBuffer buffer = BufferUtils.createIntBuffer(NUM_BUFFERS); /** Sources are points emitting sound. */ IntBuffer source = BufferUtils.createIntBuffer(128);
We will be using several wav files so we need quite a few buffers here. We will be using at most 128 sources (typically OpenAL will stop at 32-64 sources, but you shouldn't rely on more than 16!). We can just keep adding sources to the scene until OpenAL runs out of them. This is also the first tutorial where we will deal with sources as being a resource that will run out. And yes, they will run out; they are finite.
/** * boolean LoadALData() * * This function will load our sample data from the disk using the Alut * utility and send the data into OpenAL as a buffer. A source is then * also created to play that buffer. */ int loadALData() { // Load wav data into a buffer. AL10.alGenBuffers(buffer); if(AL10.alGetError() != AL10.AL_NO_ERROR) return AL10.AL_FALSE; WaveData waveFile = WaveData.create("thunder.wav"); AL10.alBufferData(buffer.get(THUNDER), waveFile.format, waveFile.data, waveFile.samplerate); waveFile.dispose(); waveFile = WaveData.create("waterdrop.wav"); AL10.alBufferData(buffer.get(WATERDROP), waveFile.format, waveFile.data, waveFile.samplerate); waveFile.dispose(); waveFile = WaveData.create("stream.wav"); AL10.alBufferData(buffer.get(STREAM), waveFile.format, waveFile.data, waveFile.samplerate); waveFile.dispose(); waveFile = WaveData.create("rain.wav"); AL10.alBufferData(buffer.get(RAIN), waveFile.format, waveFile.data, waveFile.samplerate); waveFile.dispose(); waveFile = WaveData.create("ocean.wav"); AL10.alBufferData(buffer.get(OCEAN), waveFile.format, waveFile.data, waveFile.samplerate); waveFile.dispose(); waveFile = WaveData.create("chimes.wav"); AL10.alBufferData(buffer.get(CHIMES), waveFile.format, waveFile.data, waveFile.samplerate); waveFile.dispose(); // Do another error check and return. if (AL10.alGetError() == AL10.AL_NO_ERROR) return AL10.AL_TRUE; return AL10.AL_FALSE;
We've totally removed the source generation from this function. That's because from now on we will be initializing the sources separately.
/** * void AddSource(ALint type) * * Will add a new water drop source to the audio scene. */ private void addSource(int type) { int position = source.position(); source.limit(position + 1); AL10.alGenSources(source); if (AL10.alGetError() != AL10.AL_NO_ERROR) { System.out.println("Error generating audio source."); System.exit(-1); } AL10.alSourcei(source.get(position), AL10.AL_BUFFER, buffer.get(type) ); AL10.alSourcef(source.get(position), AL10.AL_PITCH, 1.0f ); AL10.alSourcef(source.get(position), AL10.AL_GAIN, 1.0f ); AL10.alSource (source.get(position), AL10.AL_POSITION, sourcePos ); AL10.alSource (source.get(position), AL10.AL_VELOCITY, sourceVel ); AL10.alSourcei(source.get(position), AL10.AL_LOOPING, AL10.AL_TRUE ); AL10.alSourcePlay(source.get(position)); // next index source.position(position+1); }
Here's the function that will generate the sources for us. This function will generate a single source for any one of the loaded buffers we generated in the previous source. Given the buffer index 'type'. We do an error check to make sure we have a source to play (like I said, they are finite). If a source cannot be allocated then the program will exit.
/** * void killALData() * * We have allocated memory for our buffers and sources which needs * to be returned to the system. This function frees that memory. */ void killALData() { // set to 0, num_sources int position = source.position(); source.position(0).limit(position); AL10.alDeleteSources(source); AL10.alDeleteBuffers(buffer); }
This function has been modified a bit to accommodate the number of actually created sources.
// Loop. char c = ' '; while(c != 'q') { try { c = (char) System.in.read(); } catch (IOException ioe) { c = 'q'; } switch(c) { case 'w': addSource(WATERDROP); break; case 't': addSource(THUNDER); break; case 's': addSource(STREAM); break; case 'r': addSource(RAIN); break; case 'o': addSource(OCEAN); break; case 'c': addSource(CHIMES); break; }; } killALData(); } }
Here is the programs inner loop taken straight out of our main. Basically it
waits for some keyboard input and on certain key hits it will create a new
source of a certain type and add it to the audio scene. Essentially what we
have created here is something like one of those nature tapes that people
listen to for relaxation. Ours is a little better since it allows the user
to customize which sounds that they want in the background. Pretty neat eh?
I've been listening to mine while I code. It's a Zen experience (I'm
listening to it right now).
The program can be expanded for using more wav files, and have the added
feature of placing the sources around the scene in arbitrary positions. You
could even allow for sources to play with a given frequency rather than have
them loop. However this would require GUI routines that go beyond the scope
of the tutorial. A full featured "Weathering Engine" would be a nifty
program to make though. ;)
Download source code and resources for this lesson here.