From: Brian M. <ma...@us...> - 2004-06-21 21:55:30
|
Update of /cvsroot/java-game-lib/LWJGL/www/tutorials/openal/devmaster In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv2559/tutorials/openal/devmaster Added Files: lesson1.php lesson1.zip lesson2.php lesson2.zip lesson3.php lesson3.zip lesson4.php lesson5.php lesson5.zip lesson6.php lesson7.php Log Message: devmaster OpenAL tutorials --- NEW FILE: lesson5.zip --- (This appears to be a binary file; contents omitted.) --- NEW FILE: lesson3.zip --- (This appears to be a binary file; contents omitted.) --- NEW FILE: lesson1.php --- <div class="paragraph_item"> <h2>Single Static Source: Lesson 1</h2> <p class="title" align="center"> Author: <a href="mailto:lig...@ho...">Jesse Maurais</a> | From: <a href="http://www.devmaster.net/articles.php?catID=6" target="_blank">devmaster.net</a><br> Modified for LWJGL by: <a href="mailto:br...@ma...">Brian Matzon</a> </p> <div style="width: 20em; float: right; padding: 10;"><table border="0" bgcolor="#000000" cellpadding="5" cellspacing="1"> <tr> <td width="100%" class="BlockHeader"><p align="center" class="tableHeader"><b>Other Articles in the Series</b></p></td> </tr> <tr> <td width="100%" bgcolor="#ffffff"> <table cellpadding="0" cellspacing="0"> <tr> <td width="10%" align="center">*</td> <td width="90%" class="ArticleDesc"> <a href="documentation.php?page=tutorials/openal/devmaster/lesson1">Lesson 1: Single Static Source</a></td> </tr> <tr> <td width="10%" align="center">*</td> <td width="90%" class="ArticleDesc"> <a href="documentation.php?page=tutorials/openal/devmaster/lesson2">Lesson 2: Looping and Fade-away</a></td> </tr> <tr> <td width="10%" align="center">*</td> <td width="90%" class="ArticleDesc"> <a href="documentation.php?page=tutorials/openal/devmaster/lesson3">Lesson 3: Multiple Sources</a></td> </tr> <tr> <td width="10%" align="center">*</td> <td width="90%" class="ArticleDesc"> <a href="documentation.php?page=tutorials/openal/devmaster/lesson4">Lesson 4: A Closer Look at ALC</a></td> </tr> <tr> <td width="10%" align="center">*</td> <td width="90%" class="ArticleDesc"> <a href="documentation.php?page=tutorials/openal/devmaster/lesson5">Lesson 5: Sources Sharing Buffers</a></td> </tr> <tr> <td width="10%" align="center">*</td> <td width="90%" class="ArticleDesc"> <a href="documentation.php?page=tutorials/openal/devmaster/lesson6">Lesson 6: Advanced Loading and Error Handles</a></td> </tr> <tr> <td width="10%" align="center">*</td> <td width="90%" class="ArticleDesc"> <a href="documentation.php?page=tutorials/openal/devmaster/lesson7">Lesson 7: The Doppler Effect</a></td> </tr> </table> </td> </tr> </table> </div> <p>Welcome to the exciting world of OpenAL! OpenAL is still in a stage of growth, and even though there is an ever larger following to the API it still hasn't reached it's full potential. One of the big reasons for this is that there is still not yet hardware acceleration built in for specific cards. However, Creative Labs is a major contributor to the OpenAL project and also happens to be one of the largest soundcard manufacturers. So there is a promise of hardware accelerated features in the near future. OpenAL's only other major contributor, Loki, has gone the way of the dinosaur. So the future of OpenAL on Linux platforms is uncertain. You can still obtain the Linux binaries on some more obscure websites.</p> <p>OpenAL has also not been seen in many major commercial products, which may have also hurt it's growth. As far as I know the only pc game to use OpenAL has been Metal Gear 2 (although recently I've discovered that Unreal 2 does as well). The popular modeling program, Blender3D, was also known to use OpenAL for all it's audio playback. Aside from these however the only other OpenAL uses have been in the sdk examples and a few obscure tutorials on the internet.</p> <p>But lets face it, OpenAL has a lot of potential. There are many other audio libraries that claim to work with the hardware on a lower level (and this may be true), but the designers of OpenAL did several things in it's design which make it a superior API. First of all they emulated the OpenGL API which is one of the best ever designed. The API style is flexible, so different coding methods and hardware implementations will take advantage of this. People who have had a lot of experience with OpenGL will be able to pick up OpenAL quite fast. OpenAL also has the advantage of creating 3D surround sound which a lot of other API's cannot boast. On top of all of that it also has the ability to extend itself into EAX and AC3 flawlessly. To my knowledge no other audio library has that capability.</p> <p>If you still haven't found a reason here to use OpenAL then here's another. It's just cool. It's a nice looking API and will integrate well into your code. You will be able to do many cool sound effects with it. But before we do that we have to learn the basics.</p> <p>So let's get coding!</p> <pre class="code"> <span class="codeKeyword">import</span> java.io.IOException; <span class="codeKeyword">import</span> java.nio.FloatBuffer; <span class="codeKeyword">import</span> java.nio.IntBuffer; <span class="codeKeyword">import</span> org.lwjgl.BufferUtils; <span class="codeKeyword">import</span> org.lwjgl.LWJGLException; <span class="codeKeyword">import</span> org.lwjgl.openal.AL; <span class="codeKeyword">import</span> org.lwjgl.openal.AL10; <span class="codeKeyword">import</span> org.lwjgl.test.openal.WaveData; <span class="codeKeyword">public class</span> Lesson1 { <span class="codeComment"> /** Buffers hold sound data. */</span> IntBuffer buffer = BufferUtils.createIntBuffer(1); <span class="codeComment"> /** Sources are points emitting sound. */</span> IntBuffer source = BufferUtils.createIntBuffer(1); </pre> <p>Those familiar with OpenGL know that it uses "texture objects" (or "texture names") to handle textures used by a program. OpenAL does a similar thing with audio samples. There are essentially 3 kinds of objects in OpenAL. A buffer which stores all the information about how a sound should be played and the sound data itself, and a source which is a point in space that emits a sound. It's important to understand that a source is not itself an audio sample. A source only plays back sound data from a buffer bound to it. The source is also given special properties like position and velocity. </p> <p>The third object which I have not mentioned yet is the listener. There is only one listener which represents where 'you' are, the user. The listener properties along with the source properties determine how the audio sample will be heard. For example their relative positions will determine the intensity of the sound.</p> <pre class="code"><span class="codeComment"> /** Position of the source sound. */</span> FloatBuffer sourcePos = BufferUtils.createFloatBuffer(3).put(<span class="codeKeyword">new</span> float[] { 0.0f, 0.0f, 0.0f }); <span class="codeComment"> /** Velocity of the source sound. */</span> FloatBuffer sourceVel = BufferUtils.createFloatBuffer(3).put(<span class="codeKeyword">new</span> float[] { 0.0f, 0.0f, 0.0f }); <span class="codeComment"> /** Position of the listener. */</span> FloatBuffer listenerPos = BufferUtils.createFloatBuffer(3).put(<span class="codeKeyword">new</span> float[] { 0.0f, 0.0f, 0.0f }); <span class="codeComment"> /** Velocity of the listener. */</span> FloatBuffer listenerVel = BufferUtils.createFloatBuffer(3).put(<span class="codeKeyword">new</span> float[] { 0.0f, 0.0f, 0.0f }); <span class="codeComment"> /** Orientation of the listener. (first 3 elements are "at", second 3 are "up") */</span> FloatBuffer listenerOri = BufferUtils.createFloatBuffer(6).put(<span class="codeKeyword">new</span> float[] { 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f }); </pre> <p>In the above code we specify the position and velocity of the source and listener objects. These NIO Buffers are vector based Cartesian coordinates. <br><i><b>Note:</b> LWJGL uses the position, limit and capacity properties of NIO buffers to determine where to index the data and how many elements to get/set. It is therefore crucial that these limits are set correctly. In the above case we would have to flip each of the buffers so their position and limit is set correctly. A newly created buffer will have its position set to 0 and its limit to its capacity.</i></p> <pre class="code"><span class="codeComment"> /** * 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. */</span> <span class="codeKeyword">int</span> loadALData() { </pre> <p>Here we will create a function that loads all of our sound data from a file. <br><i><b>Note:</b> The original tutorial uses ALUT to load wave data. ALUT is not available in the LWJGL binding, due to license issues. You may use the <code>WaveData</code> class to load sound files instead.</i></p></p> <pre class="code"><span class="codeComment"> // Load wav data into a buffer.</span> AL10.alGenBuffers(buffer); <span class="codeKeyword">if</span>(AL10.alGetError() != AL10.AL_NO_ERROR) <span class="codeKeyword">return</span> AL10.AL_FALSE; WaveData waveFile = WaveData.create("FancyPants.wav"); AL10.alBufferData(buffer.get(0), waveFile.format, waveFile.data, waveFile.samplerate); waveFile.dispose(); </pre> <p>The function 'alGenBuffers' will create the buffer objects and store them in the variable we passed it. It's important to do an error check to make sure everything went smoothly. There may be a case in which OpenAL could not generate a buffer object due to a lack of memory. In this case it would set the error bit.</p> <p>The WaveData class is very helpful here. It opens up the file for us and gives us all the information we need to create the buffer. And after we have attached all this data to the buffer it will help use dispose of the data. It all works in a clean and efficient manner.</p> <pre class="code"> <span class="codeComment">// Bind the buffer with the source.</span> AL10.alGenSources(source); <span class="codeKeyword">if</span> (AL10.alGetError() != AL10.AL_NO_ERROR) <span class="codeKeyword">return</span> AL10.AL_FALSE; AL10.alSourcei(source.get(0), AL10.AL_BUFFER, buffer.get(0) ); AL10.alSourcef(source.get(0), AL10.AL_PITCH, 1.0f ); AL10.alSourcef(source.get(0), AL10.AL_GAIN, 1.0f ); AL10.alSource (source.get(0), AL10.AL_POSITION, sourcePos ); AL10.alSource (source.get(0), AL10.AL_VELOCITY, sourceVel ); </pre> <p>We generate a source object in the same manner we generated the buffer object. Then we define the source properties that it will use when it's in playback. The most important of these properties is the buffer it should use. This tells the source which audio sample to playback. In this case we only have one so we bind it. We also tell the source it's position and velocity which we defined earlier.</p> <p>One more thing on 'alGenBuffers' and 'alGenSources'. In some example code I have seen these functions will return an integer value for the number of buffers/sources created. I suppose this was meant as an error checking feature that was left out in a later version. If you see this done in other code don't use it yourself. If you want to do this check, use 'alGetError' instead (like we have done above).</p> <pre class="code"> <span class="codeComment">// Do another error check and return.</span> <span class="codeKeyword">if</span> (AL10.alGetError() == AL10.AL_NO_ERROR) <span class="codeKeyword">return</span> AL10.AL_TRUE; <span class="codeKeyword">return</span> AL10.AL_FALSE; </pre> <p>To end the function we just do one more check to make sure all is well, then we return success.</p> <pre class="code"><span class="codeComment"> /** * void setListenerValues() * * We already defined certain values for the Listener, but we need * to tell OpenAL to use that data. This function does just that. */</span> <span class="codeKeyword">void</span> setListenerValues() { AL10.alListener(AL10.AL_POSITION, listenerPos); AL10.alListener(AL10.AL_VELOCITY, listenerVel); AL10.alListener(AL10.AL_ORIENTATION, listenerOri); } </pre> <p>We created this function to update the listener properties.</p> <pre class="code"><span class="codeComment"> /** * void killALData() * * We have allocated memory for our buffers and sources which needs * to be returned to the system. This function frees that memory. */</span> <span class="codeKeyword">void</span> killALData() { AL10.alDeleteSources(source); AL10.alDeleteBuffers(buffer); } </pre> <p>This will be our shutdown procedure. It is necessary to call this to release all the memory and audio devices that our program may be using.</p> <pre class="code"> <span class="codeKeyword">public static void</span> main(String[] args) { <span class="codeKeyword">new</span> Lesson1().execute(); } </pre> <p>This is our main entry point. We just create our <code>Lesson1</code> instance and call the execute method</p> <pre class="code"> <span class="codeKeyword">public void</span> execute() { <span class="codeComment">// Initialize OpenAL and clear the error bit.</span> <span class="codeKeyword">try</span>{ AL.create(); } <span class="codeKeyword">catch</span> (LWJGLException le) { le.printStackTrace(); <span class="codeKeyword">return</span>; } AL10.alGetError(); </pre> <p>The function 'AL.create()' will setup everything that OpenAL needs to do for us. Basically 'AL.create()' creates a single OpenAL context through Alc and sets it to current. On the Windows platform it initializes DirectSound. We also do an initial call to the error function to clear it. Every time we call 'alGetError' it will reset itself to 'AL_NO_ERROR'.</p> <pre class="code"><span class="codeComment"> // Load the wav data.</span> <span class="codeKeyword">if</span>(loadALData() == AL10.AL_FALSE) { System.out.println("Error loading data."); <span class="codeKeyword">return</span>; } setListenerValues(); </pre> <p>We will check to see if the wav files loaded correctly. If not we must exit the program. Then we update the listener values.</p> <pre class="code"> <span class="codeComment">// Loop.</span> <span class="codeKeyword">char</span> c = ' '; <span class="codeKeyword">while</span>(c != 'q') { <span class="codeKeyword">try</span> { c = (<span class="codeKeyword">char</span>) System.in.read(); } <span class="codeKeyword">catch</span> (IOException ioe) { c = 'q'; } <span class="codeKeyword">switch</span>(c) { // Pressing 'p' will begin playing the sample. <span class="codeKeyword">case</span> 'p': AL10.alSourcePlay(source.get(0)); <span class="codeKeyword">break</span>; // Pressing 's' will stop the sample from playing. <span class="codeKeyword">case</span>'s': AL10.alSourceStop(source.get(0)); <span class="codeKeyword">break</span>; // Pressing 'h' will pause the sample. <span class="codeKeyword">case</span> 'h': AL10.alSourcePause(source.get(0)); <span class="codeKeyword">break</span>; }; } killALData(); } } </pre> <p>This is the interesting part of the tutorial. It's a very basic loop that lets us control the playback of the audio sample. Pressing 'p' will replay the sample, pressing 's' will stop the sample, and pressing 'h' will pause the sample. Pressing 'q' will exit the program. When done, we kill all data loaded into buffers and delete any source objects created.</p> <p>Well there it is. Your first delve into OpenAL. I hope it was made simple enough for you. It may have been a little too simple for the 1337 h4X0r, but we all got to start somewhere. Things will get more advanced as we go along.</p> <p> Download source code and resources for this lesson <a href="tutorials/openal/devmaster/lesson1.zip">here</a> </p> </div> --- NEW FILE: lesson4.php --- <div class="paragraph_item"> <h2>A Closer Look at ALC: Lesson 4</h2> <p class="title" align="center"> Author: <a href="mailto:lig...@ho...">Jesse Maurais</a> | From: <a href="http://www.devmaster.net/articles.php?catID=6" target="_blank">devmaster.net</a><br> Modified for LWJGL by: <a href="mailto:br...@ma...">Brian Matzon</a> </p> <div style="width: 20em; float: right; padding: 10;"><table border="0" bgcolor="#000000" cellpadding="5" cellspacing="1"> <tr> <td width="100%" class="BlockHeader"><p align="center" class="tableHeader"><b>Other Articles in the Series</b></p></td> </tr> <tr> <td width="100%" bgcolor="#ffffff"> <table cellpadding="0" cellspacing="0"> <tr> <td width="10%" align="center">*</td> <td width="90%" class="ArticleDesc"> <a href="documentation.php?page=tutorials/openal/devmaster/lesson1">Lesson 1: Single Static Source</a></td> </tr> <tr> <td width="10%" align="center">*</td> <td width="90%" class="ArticleDesc"> <a href="documentation.php?page=tutorials/openal/devmaster/lesson2">Lesson 2: Looping and Fade-away</a></td> </tr> <tr> <td width="10%" align="center">*</td> <td width="90%" class="ArticleDesc"> <a href="documentation.php?page=tutorials/openal/devmaster/lesson3">Lesson 3: Multiple Sources</a></td> </tr> <tr> <td width="10%" align="center">*</td> <td width="90%" class="ArticleDesc"> <a href="documentation.php?page=tutorials/openal/devmaster/lesson4">Lesson 4: A Closer Look at ALC</a></td> </tr> <tr> <td width="10%" align="center">*</td> <td width="90%" class="ArticleDesc"> <a href="documentation.php?page=tutorials/openal/devmaster/lesson5">Lesson 5: Sources Sharing Buffers</a></td> </tr> <tr> <td width="10%" align="center">*</td> <td width="90%" class="ArticleDesc"> <a href="documentation.php?page=tutorials/openal/devmaster/lesson6">Lesson 6: Advanced Loading and Error Handles</a></td> </tr> <tr> <td width="10%" align="center">*</td> <td width="90%" class="ArticleDesc"> <a href="documentation.php?page=tutorials/openal/devmaster/lesson7">Lesson 7: The Doppler Effect</a></td> </tr> </table> </td> </tr> </table> </div> <p> <i><b>Note:</b>This article originally deals with ALC Context management, and especially using multiple contexts. The LWJGL binding does <b>not</b> expose multiple contexts, nor do we have any plans to do so. It is an advanced feature, not used in games. This article has also been edited heavily to make sense in an LWJGL environment which doesn't expose ALC directly.</i></p> <p> Up until now we have been letting <code>AL.create()</code> do all the real tricky stuff for us. For example handling the audio devices. It's really nice that the <code>AL.create()</code> method is there to provide this functionality, but any smart coder will want to know exactly what their doing. We may want to, at some point, use the verbose <code>AL.create(String deviceArguments, <span class="codeKeyword">int</span> contextFrequency, <span class="codeKeyword">int</span> contextRefresh, <span class="codeKeyword">boolean</span> contextSynchronized)</code> method. In this tutorial we will expose the Alc layer and take a look at how to handle the devices on our own.</p> <p> When using either of the two create methods of <code>AL</code>, a default context and device is created for you. You cannot create multiple contexts nor can you access multiple devices. Thus a lot of the ALC methods are not available, since they simply don't make any sense. Methods that are available also no longer take a device or context parameter since they are managed internally. </p> <pre class="code"> <span class="codeComment">// Initialize OpenAL and clear the error bit.</span> <span class="codeKeyword">try</span>{ AL.create(); } <span class="codeKeyword">catch</span> (LWJGLException le) { le.printStackTrace(); <span class="codeKeyword">return</span>; } AL10.alGetError(); </pre> <p>Using the default create method is the simplest way to create a context and access a device. Default values are used, and once called OpenAL is initialized and ready to go.</p> <pre class="code"> <span class="codeComment">// Initialize OpenAL and clear the error bit.</span> <span class="codeKeyword">try</span>{ AL.create("DirectSound3D", 44100, 15, <span class="codeKeyword">false</span>); } <span class="codeKeyword">catch</span> (LWJGLException le) { le.printStackTrace(); <span class="codeKeyword">return</span>; } AL10.alGetError(); </pre> <p>Here we initialize OpenAL using arguments for ALC. The first argument tells ALC which device to access. Passing null is a perfectly valid argument. It forces the ALC to use a default device.<br> The second argument tells ALC the frequency for mixing output buffer, in units of Hz. Third argument tells ALC how often it should update it's internal state and buffers. The final argument determines whether the ALC should run in synchronous mode. </p> <pre class="code"> <span class="codeKeyword">public static int</span> alcGetError(); <span class="codeKeyword">public static boolean</span> alcIsExtensionPresent(String extName); <span class="codeKeyword">public static int</span> alcGetEnumValue(String enumName); <span class="codeKeyword">public static </span>String alcGetString(<span class="codeKeyword">int</span> pname); <span class="codeKeyword">public static void</span> alcGetInteger(<span class="codeKeyword">int</span> pname, IntBuffer integerdata); </pre> <p>It may be pretty obvious to you what these do, but lets humour ourselves and have a closer look. First we have 'alcGetError' which is just like 'alGetError' but will return Alc errors. The next two functions are for querying Alc extensions. This was just the creators planning ahead, as there are no Alc extensions either. The last function, 'alcGetInteger', will return the Alc version when passed 'ALC_MAJOR_VERSION' or 'ALC_MINOR_VERSION'.<br> The function 'alcGetString' is pretty cool. It can take any of the following three parameters to 'token': <br><br> * ALC_DEFAULT_DEVICE_SPECIFIER<br> * ALC_DEVICE_SPECIFIER<br> * ALC_EXTENSIONS<br> <br> The first will return the device string which your OpenAL implementation will prefer you to use. In current OpenAL this should be "DirectSound3D", like we used above. The second token will return a list of specifiers, but in current OpenAL will only return "DirectSound" (without the "3D" for some reason). The last will return a list of Alc extensions, of which none exist yet. <br> Well that's most of Alc for you. I hope it gave you a better understanding of how OpenAL interacts with the operation system. </p> </div> --- NEW FILE: lesson6.php --- <div class="paragraph_item"> <h2>Advanced Loading and Error Handles: Lesson 6</h2> <p class="title" align="center"> Author: <a href="mailto:lig...@ho...">Jesse Maurais</a> | From: <a href="http://www.devmaster.net/articles.php?catID=6" target="_blank">devmaster.net</a><br> Modified for LWJGL by: <a href="mailto:br...@ma...">Brian Matzon</a> </p> <div style="width: 20em; float: right; padding: 10;"><table border="0" bgcolor="#000000" cellpadding="5" cellspacing="1"> <tr> <td width="100%" class="BlockHeader"><p align="center" class="tableHeader"><b>Other Articles in the Series</b></p></td> </tr> <tr> <td width="100%" bgcolor="#ffffff"> <table cellpadding="0" cellspacing="0"> <tr> <td width="10%" align="center">*</td> <td width="90%" class="ArticleDesc"> <a href="documentation.php?page=tutorials/openal/devmaster/lesson1">Lesson 1: Single Static Source</a></td> </tr> <tr> <td width="10%" align="center">*</td> <td width="90%" class="ArticleDesc"> <a href="documentation.php?page=tutorials/openal/devmaster/lesson2">Lesson 2: Looping and Fade-away</a></td> </tr> <tr> <td width="10%" align="center">*</td> <td width="90%" class="ArticleDesc"> <a href="documentation.php?page=tutorials/openal/devmaster/lesson3">Lesson 3: Multiple Sources</a></td> </tr> <tr> <td width="10%" align="center">*</td> <td width="90%" class="ArticleDesc"> <a href="documentation.php?page=tutorials/openal/devmaster/lesson4">Lesson 4: A Closer Look at ALC</a></td> </tr> <tr> <td width="10%" align="center">*</td> <td width="90%" class="ArticleDesc"> <a href="documentation.php?page=tutorials/openal/devmaster/lesson5">Lesson 5: Sources Sharing Buffers</a></td> </tr> <tr> <td width="10%" align="center">*</td> <td width="90%" class="ArticleDesc"> <a href="documentation.php?page=tutorials/openal/devmaster/lesson6">Lesson 6: Advanced Loading and Error Handles</a></td> </tr> <tr> <td width="10%" align="center">*</td> <td width="90%" class="ArticleDesc"> <a href="documentation.php?page=tutorials/openal/devmaster/lesson7">Lesson 7: The Doppler Effect</a></td> </tr> </table> </td> </tr> </table> </div> <p>We've been doing some pretty simple stuff up until now that didn't require us to be very precise in the way we've handled things. The reason for this is that we have been writing code for simplicity in order to learn easier, rather that for robustness. Since we are going to move into some advanced stuff soon we will take some time to learn the proper ways. Most importantly we will learn a more advanced way of handling errors. We will also reorganize the way we have been loading audio data. There wasn't anything wrong with our methods in particular, but we will need a more organized and flexible approach to the process. </p> <p> We will first consider a few functions that will help us out a lot by the time we have finished.</p> <pre class="code"><span class="codeComment"> /** * 1) Identify the error code. * 2) Return the error as a string. */ </span> <span class="codeKeyword">public static</span> String getALErrorString(<span class="codeKeyword">int</span> err); <span class="codeComment"> /** * 1) Identify the error code. * 2) Return the error as a string. */ </span> <span class="codeKeyword">public static</span> String getALCErrorString(<span class="codeKeyword">int</span> err); <span class="codeComment"> /** * 1) Creates a buffer. * 2) Loads a wav file into the buffer. * 3) Returns the buffer id. */ </span> <span class="codeKeyword">public static int</span> loadALBuffer(String path); <span class="codeComment"> /** * 1) Checks if file has already been loaded. * 2) If it has been loaded already, return the buffer id. * 3) If it has not been loaded, load it and return buffer id. */ </span> <span class="codeKeyword">public static int</span> getLoadedALBuffer(String path); <span class="codeComment"> /** * 1) Checks if file has already been loaded. * 2) If it has been loaded already, return the buffer id. * 3) If it has not been loaded, load it and return buffer id. */ </span> <span class="codeKeyword">public static int</span> loadALSample(String path, <span class="codeKeyword">boolean</span> loop); <span class="codeComment"> /** * 1) Releases temporary loading phase data. */ </span> <span class="codeKeyword">public static void</span> killALLoadedData(); <span class="codeComment"> /** * 1) Loads all buffers and sources for the application. */ </span> <span class="codeKeyword">public static void</span> loadALData(); <span class="codeComment"> /** * 1) Releases all buffers. * 2) Releases all sources. */ </span> <span class="codeKeyword">public static void</span> killALData(); <span class="codeComment">/** List of loaded files */</span> <span class="codeKeyword">private static</span> ArrayList loadedFiles = <span class="codeKeyword">new</span> ArrayList(); <span class="codeComment">/** List of buffers */</span> <span class="codeKeyword">private static</span> ArrayList buffers = <span class="codeKeyword">new</span> ArrayList(); <span class="codeComment">/** List of sources */</span> <span class="codeKeyword">private static</span> ArrayList sources = <span class="codeKeyword">new</span> ArrayList(); </pre> <p>Take a close look at the functions and try to understand what we are going to be doing. Basically what we are trying to create is a system in which we no longer have to worry about the relationship between buffers and sources. We can call for the creation of a source from a file and this system will handle the buffer's creation on it's own so we don't duplicate a buffer (have two buffers with the same data). This system will handle the buffers as a limited resource, and will handle that resource efficiently.</p> <pre class="code"><span class="codeComment"> /** * 1) Identify the error code. * 2) Return the error as a string. */</span> <span class="codeKeyword">public static</span> String getALErrorString(<span class="codeKeyword">int</span> err) { <span class="codeKeyword">switch</span> (err) { <span class="codeKeyword">case</span> AL10.AL_NO_ERROR: <span class="codeKeyword">return</span> "AL_NO_ERROR"; <span class="codeKeyword">case</span> AL10.AL_INVALID_NAME: <span class="codeKeyword">return</span> "AL_INVALID_NAME"; <span class="codeKeyword">case</span> AL10.AL_INVALID_ENUM: <span class="codeKeyword">return</span> "AL_INVALID_ENUM"; <span class="codeKeyword">case</span> AL10.AL_INVALID_VALUE: <span class="codeKeyword">return</span> "AL_INVALID_VALUE"; <span class="codeKeyword">case</span> AL10.AL_INVALID_OPERATION: <span class="codeKeyword">return</span> "AL_INVALID_OPERATION"; <span class="codeKeyword">case</span> AL10.AL_OUT_OF_MEMORY: <span class="codeKeyword">return</span> "AL_OUT_OF_MEMORY"; <span class="codeKeyword">default</span>: <span class="codeKeyword">return</span> "No such error code"; } } </pre> <p>This function will convert an OpenAL error code to a string so it can be read on the console (or some other device). The OpenAL sdk says that the only exception that needs be looked for in the current version is the 'AL_OUT_OF_MEMORY' error. However, we will account for all the errors so that our code will be up to date with later versions.</p> <pre class="code"><span class="codeComment"> /** * 1) Identify the error code. * 2) Return the error as a string. */ </span> <span class="codeKeyword">public static</span> String getALCErrorString(<span class="codeKeyword">int</span> err) { <span class="codeKeyword">switch</span> (err) { <span class="codeKeyword">case</span> ALC.ALC_NO_ERROR: <span class="codeKeyword">return</span> "AL_NO_ERROR"; <span class="codeKeyword">case</span> ALC.ALC_INVALID_DEVICE: <span class="codeKeyword">return</span> "ALC_INVALID_DEVICE"; <span class="codeKeyword">case</span> ALC.ALC_INVALID_CONTEXT: <span class="codeKeyword">return</span> "ALC_INVALID_CONTEXT"; <span class="codeKeyword">case</span> ALC.ALC_INVALID_ENUM: <span class="codeKeyword">return</span> "ALC_INVALID_ENUM"; <span class="codeKeyword">case</span> ALC.ALC_INVALID_VALUE: <span class="codeKeyword">return</span> "ALC_INVALID_VALUE"; <span class="codeKeyword">case</span> ALC.ALC_OUT_OF_MEMORY: <span class="codeKeyword">return</span> "ALC_OUT_OF_MEMORY"; <span class="codeKeyword">default</span>: <span class="codeKeyword">return</span> "no such error code"; } } </pre> <p>This function will perform a similar task as the previous one accept this one will interpret Alc errors. OpenAL and Alc share common id's, but not common enough and not dissimilar enough to use the same function for both.</p> <p>One more note about the function 'alGetError': The OpenAL sdk defines that it only holds a single error at a time (i.e. there is no stacking). When the function is invoked it will return the first error that it has received, and then clear the error bit to 'AL_NO_ERROR'. In other words an error will only be stored in the error bit if no previous error is already stored there.</p> <pre class="code"> <span class="codeComment">/** * 1) Creates a buffer. * 2) Loads a wav file into the buffer. * 3) Returns the buffer id. */</span> <span class="codeKeyword">public static int</span> loadALBuffer(String path) { int result; IntBuffer buffer = BufferUtils.createIntBuffer(1); <span class="codeComment">// Load wav data into a buffers.</span> AL10.alGenBuffers(buffer); <span class="codeKeyword">if</span> ((result = AL10.alGetError()) != AL10.AL_NO_ERROR) { throw <span class="codeKeyword">new</span> OpenALException(getALErrorString(result)); } WaveData waveFile = WaveData.create(path); <span class="codeKeyword">if</span> (waveFile != <span class="codeKeyword">null</span>) { AL10.alBufferData(buffer.get(0), waveFile.format, waveFile.data, waveFile.samplerate); waveFile.dispose(); } <span class="codeKeyword">else</span> { throw <span class="codeKeyword">new</span> RuntimeException("No such file: " + path); } <span class="codeComment">// Do another error check and return.</span> <span class="codeKeyword">if</span> ((result = AL10.alGetError()) != AL10.AL_NO_ERROR) { throw <span class="codeKeyword">new</span> OpenALException(getALErrorString(result)); } <span class="codeKeyword">return</span> buffer.get(0); } </pre> <p>As you can see we do an error check at every possible phase of the load. Any number of things can happen at this point which will cause an error to be thrown. There could be no more system memory either for the buffer creation or the data to be loaded, the wav file itself may not even exist, or an invalid value can be passed to any one of the OpenAL functions which will generate an error.</p> <pre class="code"> <span class="codeComment">/** * 1) Checks if file has already been loaded. * 2) If it has been loaded already, return the buffer id. * 3) If it has not been loaded, load it and return buffer id. */</span> <span class="codeKeyword">public static int</span> getLoadedALBuffer(String path) { <span class="codeKeyword">int</span> count = 0; <span class="codeKeyword">for</span> (Iterator i = loadedFiles.iterator(); i.hasNext(); count++) { <span class="codeKeyword">if</span> (i.equals(path)) { <span class="codeKeyword">return</span> ((Integer) buffers.get(count)).intValue(); } } int</span> buffer = loadALBuffer(path); buffers.add(new Integer(buffer)); loadedFiles.add(path); <span class="codeKeyword">return</span> buffer; } </pre> <p>This will probably be the piece of code most people have trouble with, but it's really not that complex. We are doing a search through a list which contains the file paths of all the wav's we have loaded so far. If one of the paths matches the one we want to load, we will simply return the id to the buffer we loaded it into the first time. This way as long as we consistently load our files through this function, we will never have buffers wasted due to duplication. Every file loaded this way must also be kept track of with it's own list. The 'buffers' list is parallel to the 'loadedFiles' list. What I mean by this is that every buffer in the index of 'buffers', is the same path of the index in 'loadedFiles' from which that buffer was created.</p> <pre class="code"> <span class="codeComment"> /** * 1) Creates a source. * 2) Calls 'GetLoadedALBuffer' with 'path' and uses the * returned buffer id as it's sources buffer. * 3) Returns the source id. */</span> <span class="codeKeyword">public static int</span> loadALSample(String path, <span class="codeKeyword">boolean</span> loop) { IntBuffer source = BufferUtils.createIntBuffer(1); <span class="codeKeyword">int</span> buffer; <span class="codeKeyword">int</span> result; <span class="codeComment">// Get the files buffer id (load it if necessary).</span> buffer = getLoadedALBuffer(path); <span class="codeComment">// Generate a source.</span> AL10.alGenSources(source); <span class="codeKeyword">if</span> ((result = AL10.alGetError()) != AL10.AL_NO_ERROR) throw <span class="codeKeyword">new</span> OpenALException(getALErrorString(result)); <span class="codeComment">// Setup the source properties.</span> AL10.alSourcei(source.get(0), AL10.AL_BUFFER, buffer ); AL10.alSourcef(source.get(0), AL10.AL_PITCH, 1.0f ); AL10.alSourcef(source.get(0), AL10.AL_GAIN, 1.0f ); AL10.alSource (source.get(0), AL10.AL_POSITION, sourcePos ); AL10.alSource (source.get(0), AL10.AL_VELOCITY, sourceVel ); AL10.alSourcei(source.get(0), AL10.AL_LOOPING, (loop ? AL10.AL_TRUE : AL10.AL_FALSE)); <span class="codeComment">// Save the source id.</span> sources.add(<span class="codeKeyword">new</span> Integer(source.get(0))); <span class="codeComment">// Return the source id.</span> <span class="codeKeyword">return</span> source.get(0); } </pre> <p>Now that we have created a system which will handle the buffers for us, we just need an extension to it that will get the sources. In this code we obtain the result of a search for the file, which is the buffer id that the file was loaded into. This buffer is bound to the new source. We save the source id internally and also return it.</p> <pre class="code"> <span class="codeComment">/** * 1) Releases temporary loading phase data. */</span> <span class="codeKeyword">public static void</span> killALLoadedData() { loadedFiles.clear(); } </pre> <p>The global arraylist 'loadedFiles' stored the file path of every wav file that was loaded into a buffer. It doesn't make sense to keep this data lying around after we have loaded all of our data, so we will dispose of it.</p> <pre class="code"> <span class="codeComment">//Source id's.</span> <span class="codeKeyword">static int</span> phaser1; <span class="codeKeyword">static int</span> phaser2; <span class="codeKeyword">public static void</span> loadALData() { <span class="codeComment">// Anything for your application here. No worrying about buffers.</span> phaser1 = loadALSample("phaser.wav", <span class="codeKeyword">false</span>); phaser2 = loadALSample("phaser.wav", <span class="codeKeyword">true</span>); killALLoadedData(); } </pre> <p>We have seen the function in previous tutorials. It will represent the part of a program which loads all wav's used by the program. In it we can see why our system is useful. Even though we have made a call to load the same wav file into two distinct sources, the buffer for the file 'phaser.wav' will only be created once, and the sources 'gPhaser1' and 'gPhaser2' will both use that buffer for playback. There is no more concern for handling buffers because the system will handle them automatically.</p> <pre class="code"> <span class="codeComment"> /** * 1) Releases all buffers. * 2) Releases all sources. */</span> <span class="codeKeyword">public static void</span> killALData() { IntBuffer scratch = BufferUtils.createIntBuffer(1); <span class="codeComment">// Release all buffer data.</span> <span class="codeKeyword">for</span> (Iterator iter = buffers.iterator(); iter.hasNext();) { scratch.put(0, ((Integer) iter.next()).intValue()); AL10.alDeleteBuffers(scratch); } <span class="codeComment">// Release all source data.</span> <span class="codeKeyword">for</span> (Iterator iter = sources.iterator(); iter.hasNext();) { scratch.put(0, ((Integer) iter.next()).intValue()); AL10.alDeleteSources(scratch); } <span class="codeComment">// Destroy the lists.</span> buffers.clear(); sources.clear(); } </pre> <p>All along we have been storing the buffer and source id's into array lists. We free all the buffers and sources by going through them and releasing them individually. After which we destroy the lists themselves. All we need to do now is catch the OpenAL errors that we have thrown.</p> <pre class="code"> <span class="codeKeyword">try</span> { InitOpenAL(); loadALData(); } <span class="codeKeyword">catch</span> (Exception e) { e.printStackTrace(); } </pre> <p>If something has gone wrong during the course of the load we will be notified of it right away. When we catch the error it will be reported on the console. We can use this for debugging or general error reporting.</p> <p> That's it. A more advanced way of reporting errors, and a more robust way of loading your wav files. We may find we need to do some modifications in the future to allow for more flexibility, but for now we will be using this source for basic file loading in future tutorials. Expect future tutorials to expand on this code.</p> </div> --- NEW FILE: lesson7.php --- <div class="paragraph_item"> <h2>OpenAL Lesson 7: The Doppler Effect</h2> <p class="title" align="center"> Author: <a href="mailto:lig...@ho...">Jesse Maurais</a> | From: <a href="http://www.devmaster.net/articles.php?catID=6" target="_blank">devmaster.net</a><br> Modified for LWJGL by: <a href="mailto:br...@ma...">Brian Matzon</a> </p> <div style="width: 20em; float: right; padding: 10;"><table border="0" bgcolor="#000000" cellpadding="5" cellspacing="1"> <tr> <td width="100%" class="BlockHeader"><p align="center" class="tableHeader"><b>Other Articles in the Series</b></p></td> </tr> <tr> <td width="100%" bgcolor="#ffffff"> <table cellpadding="0" cellspacing="0"> <tr> <td width="10%" align="center">*</td> <td width="90%" class="ArticleDesc"> <a href="documentation.php?page=tutorials/openal/devmaster/lesson1">Lesson 1: Single Static Source</a></td> </tr> <tr> <td width="10%" align="center">*</td> <td width="90%" class="ArticleDesc"> <a href="documentation.php?page=tutorials/openal/devmaster/lesson2">Lesson 2: Looping and Fade-away</a></td> </tr> <tr> <td width="10%" align="center">*</td> <td width="90%" class="ArticleDesc"> <a href="documentation.php?page=tutorials/openal/devmaster/lesson3">Lesson 3: Multiple Sources</a></td> </tr> <tr> <td width="10%" align="center">*</td> <td width="90%" class="ArticleDesc"> <a href="documentation.php?page=tutorials/openal/devmaster/lesson4">Lesson 4: A Closer Look at ALC</a></td> </tr> <tr> <td width="10%" align="center">*</td> <td width="90%" class="ArticleDesc"> <a href="documentation.php?page=tutorials/openal/devmaster/lesson5">Lesson 5: Sources Sharing Buffers</a></td> </tr> <tr> <td width="10%" align="center">*</td> <td width="90%" class="ArticleDesc"> <a href="documentation.php?page=tutorials/openal/devmaster/lesson6">Lesson 6: Advanced Loading and Error Handles</a></td> </tr> <tr> <td width="10%" align="center">*</td> <td width="90%" class="ArticleDesc"> <a href="documentation.php?page=tutorials/openal/devmaster/lesson7">Lesson 7: The Doppler Effect</a></td> </tr> </table> </td> </tr> </table> </div> <p>I know this will be boring review for anyone with a course in high school physics, but lets humour ourselves. The Doppler effect can be a very tricky concept for some people, but it is a logical process, and kind of interesting when you get right down to it. To begin understanding the Doppler effect we first must start to understand what a "sound" really is. Basically a sound is your minds interpretation of a compression wave that is traveling through the air. Whenever the air becomes disturbed it starts a wave which compresses the air particles around it. This wave travels outward from it's point of origin. Consider the following diagram.</p> <p><img border="0" src="images/tutorials/openal/devmaster/sound_waves.jpg" align="left" width="150" height="132">In this diagram (on the left) the big red "S" stands for the sources position, and the big red "L" stands for (you guessed it), the Listener's position. Both source and Listener are not moving. The source is emitting compression waves outward, which are represented in this diagram by the blue circles. The Listener is experiencing the sound exactly as it is being made in this diagram. The Doppler effect is not actually present in this example since there is no motion; the Doppler effect only describes the warping of sound due to motion.</p> <p>What you should try to do is picture this diagram animated. When the source emits a wave (the circles) it will look as though it is growing away from it's point of origin, which is the sources position. A good example of a similar effect is the ripples in a pond. When you throw a pebble into a calm body of water it will emit waves which constantly move away from the point of impact. Believe it or not this occurs from the exact same physical properties. But what does this have to do with the Doppler effect? Check out the next diagram (on the right).</p> <p> <img border="0" src="images/tutorials/openal/devmaster/doppler_effect.jpg" align="right" width="150" height="132">Wow, what's going on here? The source is now in motion, indicated by the little red arrow. In fact the source is now moving towards the Listener with an implied velocity. Notice particularly that the waves (circles) are being displaced inside each other. The displacement follows the approximate path of the source which emits them. This is the key to the Doppler effect. Essentially what has happened is that the source has emitted a wave at different points in it's path of travel. The waves it emits do not move with it, but continue on their own path of travel from the point they were emitted.</p> <p>So how does this effect the perceived sound by the Listener? Well, notice too in the last diagram that the waves (circles) that are between the source and the Listener are kind of compressed together. This will cause the sound waves to run together, which in turn causes the perceived sound seem like it's faster. What we are talking about here is frequency. The distances between the waves effects the frequency of the sound. When the source that emits the sound is in motion, it causes a change in frequency. You may notice too that distance between the waves varies at different points in space. For example, on the opposite side of the moving source (anywhere along the previous path of travel) the distances are actually wider, so the frequency will be lower (the distance and frequency have an inverse relationship). What this implies is that the frequency perceived by the Listener is relative to where the Listener is standing. </p> <p>The motion of the Listener can also affect the frequency. This one is a little harder to picture though. If the source is still, and the Listener is moving toward the source, then the perceived frequency by the Listener will be warped in the same exact manner that we described for the moving source. </p> <p>If you still have trouble picturing this, consider the following two diagrams:</p> <p align="center"><img border="0" src="images/tutorials/openal/devmaster/sin_wave.jpg" width="255" height="135"> <img border="0" src="images/tutorials/openal/devmaster/compress_sin_wave.jpg" width="255" height="135"></p> <p>These two diagrams will represent the sound in the form of a sine wave. Look at the first one. Think of the peaks as the instance of the wave. The very top point of the wave will be the same as the instance of the blue circle in the previous set of diagrams. The valleys will be like the spaces in between the blue circles. The second diagram represents a compressed wave. When you compare the two you will notice an obvious difference. The second diagram simply has more wave occurrences in the same amount of space. Other ways of saying this are that they occur more often, with a greater regularity, or with a greater frequency. </p> <p>For anyone who is interested in some added information: The velocity of the waves is the speed of sound. If the velocity of the source is greater than that of the wave, then the source is breaking the sound barrier.</p> <h1>The Physics of OpenAL</h1> <p>Ok, either you have understood my ramblings on the Doppler effect from above, or you have skipped it because you already have full knowledge of the Doppler effect and just want to know how it effects the OpenAL rendering pipeline. I think the best start to his section will be to quote the OpenAL spec directly:</p> <blockquote> <p><i>"The Doppler Effect depends on the velocities of Source and Listener relative to the medium, and the propagation speed of sound in that medium." - chapter 3, subsection 7"</i></p> </blockquote> <p>We can take this to mean that there are 3 factors which are going to affect the final frequency of the sound heard by the Listener. These factors are the velocity of the source, the velocity of the Listener, and a predefined speed of sound. </p> <p>When we refer to a "medium", what we mean is the kind of material that both the source and Listener are "in". For example, sounds that are heard from underwater are much different than sounds that are heard in the open air. Air and water are examples of different mediums. The reason that sound is so different between these mediums has to do with the particle density. As we said before, sound is nothing but the motion of particles in the air. In a medium with a much greater particle density the sound will be much different because the particles are in closer contact. When they are in closer contact it allows for the wave to travel much better. As an example of the opposite, think of outer space. In outer space there is an extremely low particle density. In fact there is only a few very light particles (mostly hydrogen) scattered about. This is why no sound can be heard from space. </p> <p>Ok, lets get back on topic. OpenAL calculates the Doppler effect internally for us, so we need only define a few parameters that will effect the calculation. We would do this in case we don't want a realistic rendering. Rather if want to exaggerate or deemphasize the effect. The calculation goes like this.</p> <p><span class="codeNormal"> shift = DOPPLER_FACTOR * freq * (DOPPLER_VELOCITY - l.velocity) / (DOPPLER_VELOCITY + s.velocity)</span></p> <p>Constants are written in all caps to differentiate. The "l" and "s" variables are the Listener and source respectively. "freq" is the initial unaltered frequency of the emitting wave, and "shift" is the altered frequency of the wave. The term "shift" is the proper way to address the altered frequency and will be used from now on. This final shifted frequency will be sampled by OpenAL for all audio streaming that is affected. </p> <p>We already know that we can define the velocity of both source and Listener by using the 'AL_VELOCITY' field to 'alListenerfv' and 'alSourcefv'. The 'freq' parameter comes straight from the buffer properties when it was loaded from file. To set the constant values the following functions are provided for us.</p> <pre class="code"><span class="codeKeyword">public static native void</span> alDopplerFactor(<span class="codeKeyword">float</span> value); <span class="codeKeyword">public static native void</span> alDopplerVelocity(<span class="codeKeyword">float</span> value); </pre> <p>For 'alDopplerFactor' any non-negative value will suffice. Passing a negative value will raise an error of 'AL_INVALID_VALUE', and the whole command will be ignored. Passing zero is a perfectly valid argument. Doing this will disable the Doppler effect and may in fact help overall performance (but won't be as realistic). The effect of the Doppler factor will directly change the magnitude of the equation. A value of 1.0 will not change the effect at all. Passing anything between 0.0 and 1.0 will minimize the Doppler effect, and anything greater than 1.0 will maximize the effect. </p> <p>For 'alDopplerVelocity' any non-negative non-zero value will suffice. Passing either a negative or a zero will raise an error of 'AL_INVALID_VALUE', and the whole command will be ignored. The Doppler velocity is essentially the speed of sound. Setting this will be like setting how fast sound can move through the medium. OpenAL has no sense of medium, but setting the velocity will give the effect of a medium. OpenAL also has no sense of units (kilometer, miles, parsecs), so keep that in mind when you set this value so it is consistent with all other notions of units that you have defined.</p> </div> --- NEW FILE: lesson2.zip --- (This appears to be a binary file; contents omitted.) --- NEW FILE: lesson2.php --- <div class="paragraph_item"> <h2>Looping and Fade-away: Lesson 2</h2> <p class="title" align="center"> Author: <a href="mailto:lig...@ho...">Jesse Maurais</a> | From: <a href="http://www.devmaster.net/articles.php?catID=6" target="_blank">devmaster.net</a><br> Modified for LWJGL by: <a href="mailto:br...@ma...">Brian Matzon</a> </p> <div style="width: 20em; float: right; padding: 10;"><table border="0" bgcolor="#000000" cellpadding="5" cellspacing="1"> <tr> <td width="100%" class="BlockHeader"><p align="center" class="tableHeader"><b>Other Articles in the Series</b></p></td> </tr> <tr> <td width="100%" bgcolor="#ffffff"> <table cellpadding="0" cellspacing="0"> <tr> <td width="10%" align="center">*</td> <td width="90%" class="ArticleDesc"> <a href="documentation.php?page=tutorials/openal/devmaster/lesson1">Lesson 1: Single Static Source</a></td> </tr> <tr> <td width="10%" align="center">*</td> <td width="90%" class="ArticleDesc"> <a href="documentation.php?page=tutorials/openal/devmaster/lesson2">Lesson 2: Looping and Fade-away</a></td> </tr> <tr> <td width="10%" align="center">*</td> <td width="90%" class="ArticleDesc"> <a href="documentation.php?page=tutorials/openal/devmaster/lesson3">Lesson 3: Multiple Sources</a></td> </tr> <tr> <td width="10%" align="center">*</td> <td width="90%" class="ArticleDesc"> <a href="documentation.php?page=tutorials/openal/devmaster/lesson4">Lesson 4: A Closer Look at ALC</a></td> </tr> <tr> <td width="10%" align="center">*</td> <td width="90%" class="ArticleDesc"> <a href="documentation.php?page=tutorials/openal/devmaster/lesson5">Lesson 5: Sources Sharing Buffers</a></td> </tr> <tr> <td width="10%" align="center">*</td> <td width="90%" class="ArticleDesc"> <a href="documentation.php?page=tutorials/openal/devmaster/lesson6">Lesson 6: Advanced Loading and Error Handles</a></td> </tr> <tr> <td width="10%" align="center">*</td> <td width="90%" class="ArticleDesc"> <a href="documentation.php?page=tutorials/openal/devmaster/lesson7">Lesson 7: The Doppler Effect</a></td> </tr> </table> </td> </tr> </table> </div> <p>Hope you found the last tutorial of some use. I know I did. This will be a real quick and easy tutorial. It won't get too much more complicated at this point.</p> <pre class="code"> <span class="codeKeyword">import</span> java.io.IOException; <span class="codeKeyword">import</span> java.nio.FloatBuffer; <span class="codeKeyword">import</span> java.nio.IntBuffer; <span class="codeKeyword">import</span> org.lwjgl.BufferUtils; <span class="codeKeyword">import</span> org.lwjgl.LWJGLException; <span class="codeKeyword">import</span> org.lwjgl.openal.AL; <span class="codeKeyword">import</span> org.lwjgl.openal.AL10; <span class="codeKeyword">import</span> org.lwjgl.test.openal.WaveData; <span class="codeKeyword">public class</span> Lesson2 { <span class="codeComment"> /** Buffers hold sound data. */</span> IntBuffer buffer = BufferUtils.createIntBuffer(1); <span class="codeComment"> /** Sources are points emitting sound. */</span> IntBuffer source = BufferUtils.createIntBuffer(1); <span class="codeComment"> /** Position of the source sound. */</span> FloatBuffer sourcePos = BufferUtils.createFloatBuffer(3).put(<span class="codeKeyword">new</span> float[] { 0.0f, 0.0f, 0.0f }); <span class="codeComment"> /** Velocity of the source sound. */</span> FloatBuffer sourceVel = BufferUtils.createFloatBuffer(3).put(<span class="codeKeyword">new</span> float[] { 0.0f, 0.0f, 0.1f }); <span class="codeComment"> /** Position of the listener. */</span> FloatBuffer listenerPos = BufferUtils.createFloatBuffer(3).put(<span class="codeKeyword">new</span> float[] { 0.0f, 0.0f, 0.0f }); <span class="codeComment"> /** Velocity of the listener. */</span> FloatBuffer listenerVel = BufferUtils.createFloatBuffer(3).put(<span class="codeKeyword">new</span> float[] { 0.0f, 0.0f, 0.0f }); <span class="codeComment"> /** Orientation of the listener. (first 3 elements are "at", second 3 are "up") */</span> FloatBuffer listenerOri = BufferUtils.createFloatBuffer(6).put(<span class="codeKeyword">new</span> float[] { 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f }); </pre> <p>There is only one change in the code since the last tutorial in this fist section. It is that we altered the sources velocity. It's 'z' field is now 0.1.</p> <pre class="code"><span class="codeComment"> /** * 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. */</span> <span class="codeKeyword">int</span> loadALData() { <span class="codeComment"> // Load wav data into a buffer.</span> AL10.alGenBuffers(buffer); <span class="codeKeyword">if</span>(AL10.alGetError() != AL10.AL_NO_ERROR) <span class="codeKeyword">return</span> AL10.AL_FALSE; WaveData waveFile = WaveData.create("Footsteps.wav"); AL10.alBufferData(buffer.get(0), waveFile.format, waveFile.data, waveFile.samplerate); waveFile.dispose(); <span class="codeComment">// Bind the buffer with the source.</span> AL10.alGenSources(source); <span class="codeKeyword">if</span> (AL10.alGetError() != AL10.AL_NO_ERROR) ... [truncated message content] |