diff options
-rw-r--r-- | www/devmaster/lesson6.html | 344 | ||||
-rw-r--r-- | www/index.html | 3 |
2 files changed, 347 insertions, 0 deletions
diff --git a/www/devmaster/lesson6.html b/www/devmaster/lesson6.html new file mode 100644 index 0000000..3a0c5ee --- /dev/null +++ b/www/devmaster/lesson6.html @@ -0,0 +1,344 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html> +<head> +<title>Untitled Document</title> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> +<link rel="stylesheet" type="text/css" href="general.css"> +</head> +<body> + +<div align="center"> + +<IMG SRC="http://games.dev.java.net/images/navbar.gif" WIDTH=454 HEIGHT=43 BORDER=0 ALT="" USEMAP="#navbar_Map"></div> + <MAP NAME="navbar_Map"> + <AREA SHAPE="rect" ALT="Wiki" COORDS="340,0,386,43" HREF="http://wiki.java.net/bin/view/Games"> + <AREA SHAPE="rect" ALT="Weblogs" COORDS="286,0,339,43" HREF="http://weblogs.java.net/weblogs/project/games"> + <AREA SHAPE="rect" ALT="Forums" COORDS="216,0,285,43" HREF="http://games.dev.java.net/forums"> + <AREA SHAPE="rect" ALT="JavaGames Home" COORDS="91,0,215,43" HREF="http://community.java.net/games"> + <AREA SHAPE="rect" ALT="www.java.net" COORDS="1,1,91,44" HREF="http://www.java.net" TARGET="_self"> + </MAP><br> +<br> +OpenAL Tutorials from DevMaster.net. Reprinted with Permission.<br> +<br> +<table border="0" cellspacing="0" style="border-collapse: collapse" width="100%" cellpadding="0" id="AutoNumber1" height="12" bgcolor="#666699"> + <tr> + <td width="47%" height="12" valign="middle"><p><b><font color="#FFFFFF">OpenAL + Tutorials</font></b></p></td> + <td width="53%" height="12" align="right" valign="middle"><p align="right"><a href="http://devmaster.net/"><font color="#66FF99">DevMaster.net</font></a></p></td> + </tr> +</table> +<p class="ArticleTitle"><font size="5">Advanced Loading and Error Handles<br> + </font><font color="#000000" size="4"><strong>Lesson 6</strong></font></p> +<p align="right" class="ArticleAuthor">Author: <a href="mailto:[email protected]">Jesse + Maurais</a><br> + Adapted For Java By: <a href="mailto:[email protected]">Athomas Goldberg</a></p> +<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><font color="#0000FF"><span class=codeComment><font color="#006600">/** + * 1) Identify the error code. + * 2) Return the error as a string. + */</font></span> +public</font> <font color="#0000FF">static</font> String getALErrorString(<font color="#0000FF">int</font> err); + +<span class=codeComment><font color="#006600">/** + * 1) Identify the error code. + * 2) Return the error as a string. + */</font></span> +<font color="#0000FF">public</font> <font color="#0000FF">static</font> String getALCErrorString(<font color="#0000FF">int</font> err); + +<font color="#0000FF"><span class=codeComment><font color="#006600">/** + * 1) Creates a buffer. + * 2) Loads a wav file into the buffer. + * 3) Returns the buffer id. + */</font></span> +public static int </font>loadALBuffer(String path) <font color="#0000FF">throws</font> IOException; + +<span class=codeComment><font color="#006600">/** + * 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. + */</font></span> +<font color="#0000FF">public static int </font>getLoadedALBuffer(String path) <font color="#0000FF">throws</font> IOException; + + +<font color="#0000FF"><span class=codeComment><font color="#006600">/** + * 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. + */ +</font></span>public static int </font>loadALSample(String path, <span class=codeKeyword><font color="#0000FF">boolean</font></span> loop) <font color="#0000FF">throws</font> IOException; +<span class=codeKeyword> +</span><span class=codeComment><font color="#006600">/** + * 1) Releases temporary loading phase data. + */</font></span> +<span class=codeKeyword><font color="#0000FF">public static void</font></span><font color="#0000FF"> </font>killALLoadedData(); +<span class=codeKeyword> +</span><span class=codeComment><font color="#006600">/** + * 1) Loads all buffers and sources for the application. + */</font></span> +<span class=codeKeyword><font color="#0000FF">public static boolean</font></span><font color="#0000FF"> </font>loadALData(); +<span class=codeKeyword> +</span><span class=codeComment><font color="#006600">/** + * 1) Releases all buffers. + * 2) Releases all sources. + */</font></span> +<font color="#0000FF"><span class=codeKeyword>public </span>static <span class=codeKeyword>void</span> </font>killALData(); + + +<font color="#0000FF">private static</font> Vector loadedFiles = <font color="#0000FF">new</font> Vector(); <span class=codeComment><font color="#006600">// Holds loaded file paths temporarily.</font></span> + +<font color="#0000FF">private static</font> Vector buffers = <font color="#0000FF">new</font> Vector(); <span class=codeComment><font color="#006600">// Holds all loaded buffers.</font></span><font color="#006600"> +</font><font color="#0000FF">private static</font> Vector sources<font color="#006600"> = <font color="#0000FF">new</font> </font>Vector();<font color="#006600"> <span class=codeComment>// Holds all validated sources.</span></font> +</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><font color="#0000FF">public</font> String getALErrorString(<font color="#0000FF">int</font> err) { + <span class=codeKeyword><font color="#0000FF">switch</font></span>(err) { + <span class=codeKeyword><font color="#0000FF">case</font></span> AL.AL_NO_ERROR: <span class=codeKeyword><font color="#0000FF">return</font></span> "AL_NO_ERROR"; + <span class=codeKeyword><font color="#0000FF">case</font></span> AL.AL_INVALID_NAME: <span class=codeKeyword><font color="#0000FF">return</font></span> "AL_INVALID_NAME"; + <span class=codeKeyword><font color="#0000FF">case</font></span> AL.AL_INVALID_ENUM: <span class=codeKeyword><font color="#0000FF">return</font></span> "AL_INVALID_ENUM"; + <span class=codeKeyword><font color="#0000FF">case</font></span> AL.AL_INVALID_VALUE: <span class=codeKeyword><font color="#0000FF">return</font></span> "AL_INVALID_VALUE"; + <span class=codeKeyword><font color="#0000FF">case</font></span> AL.AL_INVALID_OPERATION: <span class=codeKeyword><font color="#0000FF">return</font></span> "AL_INVALID_OPERATION"; + <span class=codeKeyword><font color="#0000FF">case</font></span> AL.AL_OUT_OF_MEMORY: <span class=codeKeyword><font color="#0000FF">return</font></span> "AL_OUT_OF_MEMORY"; + <font color="#0000FF">default</font>: <font color="#0000FF">return</font> <font color="#0000FF">null</font>; + } +} + +</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><font color="#0000FF">public</font> String getALCErrorString(<font color="#0000FF">int</font> err) { + <span class=codeKeyword><font color="#0000FF">switch</font></span>(err) { + <span class=codeKeyword><font color="#0000FF">case</font></span> ALC_NO_ERROR: <span class=codeKeyword><font color="#0000FF">return</font></span> "ALC_NO_ERROR"; +<span class=codeKeyword> <font color="#0000FF">case</font></span> ALC_INVALID_DEVICE: <span class=codeKeyword><font color="#0000FF">return</font></span> "ALC_INVALID_DEVICE"; +<span class=codeKeyword> <font color="#0000FF">case</font></span> ALC_INVALID_CONTEXT: <span class=codeKeyword><font color="#0000FF">return</font></span> "ALC_INVALID_CONTEXT"; +<span class=codeKeyword> <font color="#0000FF">case</font></span> ALC_INVALID_ENUM: <span class=codeKeyword><font color="#0000FF">return</font></span> "ALC_INVALID_ENUM"; +<span class=codeKeyword> <font color="#0000FF">case</font></span> ALC_INVALID_VALUE: <span class=codeKeyword><font color="#0000FF">return</font></span> "ALC_INVALID_VALUE"; +<span class=codeKeyword> <font color="#0000FF">case</font></span> ALC_OUT_OF_MEMORY: <span class=codeKeyword><font color="#0000FF">return</font></span> "ALC_OUT_OF_MEMORY"; + <font color="#0000FF">default</font>: <font color="#0000FF">return</font> <font color="#0000FF">null</font>; + } +} + +</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><font color="#0000FF">public int</font> loadALBuffer(String path) <font color="#0000FF">throws</font> IOException { + <span class=codeComment><font color="#006600">// Variables to store data which defines the buffer.</font></span> + <font color="#0000FF">int</font>[] format = <font color="#0000FF">new int</font>[1]; + <font color="#0000FF">int</font>[] size = <font color="#0000FF">new int</font>[1]; + ByteBuffer[] data = <font color="#0000FF">new</font> ByteBuffer[1]; + <font color="#0000FF">int</font>[] freq = <font color="#0000FF">new int</font>[1]; + <font color="#0000FF">int</font>[] loop = <font color="#0000FF">new int</font>[1]; + + <font color="#006600"><span class=codeComment>// Buffer id and error checking variable.</span></font> + <font color="#0000FF">int</font>[] buffer = <font color="#0000FF">new int</font>[1]; + int result; + + <span class=codeComment><font color="#006600">// Generate a buffer. Check that it was created successfully.</font></span> + al.alGenBuffers(1, buffer); + + <span class=codeKeyword><font color="#0000FF">if</font> </span>((result = al.alGetError()) != AL.AL_NO_ERROR) + <span class=codeKeyword><font color="#0000FF">throw</font></span><font color="#0000FF"> new</font> IOException(getALErrorString(result)); + + <span class=codeComment><font color="#006600">// Read in the wav data from file. Check that it loaded correctly.</font></span><font color="#006600"> +</font> + ALut.alutLoadWAVFile(path, format, data, size, freq, loop); + + <span class=codeKeyword><font color="#0000FF">if</font> </span>((result = al.alGetError()) != AL.AL_NO_ERROR) + <span class=codeKeyword><font color="#0000FF">throw</font></span><font color="#0000FF"> new </font>IOException(getALErrorString(result)); + + <span class=codeComment><font color="#006600">// Send the wav data into the buffer. Check that it was received properly.</font></span> + al.alBufferData(buffer[1], format[0], data[0], size[0], freq[0]); + + <span class=codeKeyword><font color="#0000FF">if</font> </span>((result = al.alGetError()) != AL.AL_NO_ERROR) + <span class=codeKeyword><font color="#0000FF">throw</font></span> new IOException(getALErrorString(result)); + +<font color="#006600"> <span class=codeComment>// Get rid of the temporary data.</span> +</font> + ALut.alutUnloadWAV(format[0], data[0], size[0], freq[0]); + + <span class=codeKeyword><font color="#0000FF">if</font> </span>((result = al.alGetError()) != AL.AL_NO_ERROR) + <span class=codeKeyword><font color="#0000FF">throw</font></span> new IOException(getALErrorString(result)); + +<font color="#006600"> <span class=codeComment>// Return the buffer id.</span> +</font> <span class=codeKeyword><font color="#0000FF">return</font></span> buffer[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><font color="#0000FF">public int </font>getLoadedALBuffer(String path) <font color="0000ff">throws</font> IOException { + <span class=codeKeyword><font color="#0000FF">int</font></span> count = 0; <span class=codeComment><font color="#006600">// 'count' will be an index to the buffer list.</font></span> + + <font color="#0000FF">int</font> buffer; <span class=codeComment><font color="#006600">// Buffer id for the loaded buffer.</font></span> + + + <span class=codeComment>// Iterate through each file path in the list.</span> + Iterator iter = loadedFiles.iterator(); + <font color="#0000FF">int i = 0; + while</font>(iter.hasNext()) { + String str = (String)iter.next(); + if(str.equals(path)) { + <font color="#0000FF">return</font> ((Integer)buffers.get(i)).intValue(); + } + i++; + } + <span class=codeComment><font color="#006600">// If we have made it this far then this file is new and we will create a buffer for it.</font></span> + buffer = loadALBuffer(path); + + <span class=codeComment><font color="#006600">// Add this new buffer to the list, and register that this file has been loaded already.</font></span> + buffers.add(new Integer(buffer)); + + loadedFiles.add(path); + + <span class=codeKeyword><font color="#0000FF">return</font></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><font color="#0000FF">public static int </font>loadALSample(String path, <span class=codeKeyword><font color="#0000FF">boolean</font></span> loop) <font color="0000ff">throws</font> IOException { + <font color="#0000FF">int</font>[] source = <font color="0000ff">new int</font>[1]; + <font color="0000ff">int</font> buffer; + <font color="0000ff">int</font> result; + + <span class=codeComment><font color="006600">// Get the files buffer id (load it if necessary).</font></span> + buffer = getLoadedALBuffer(path); + + <span class=codeComment><font color="006600">// Generate a source.</font></span> + al.alGenSources(1, source); + + <span class=codeKeyword><font color="000066">if</font> </span>((result = al.alGetError()) != AL.AL_NO_ERROR) + <span class=codeKeyword><font color="0000ff">throw</font></span> new IOException(getALErrorString(result)); + + <font color="#006600"><span class=codeComment>// Setup the source properties.</span></font> + + al.alSourcei (source[0], AL.AL_BUFFER, buffer ); + al.alSourcef (source[0], AL.AL_PITCH, 1.0f ); + al.alSourcef (source[0], AL.AL_GAIN, 1.0f ); + al.alSourcefv(source[0], AL.AL_POSITION, sourcePos); + al.alSourcefv(source[0], AL.AL_VELOCITY, sourceVel); + al.alSourcei (source[0], AL.AL_LOOPING, loop ? AL.AL_TRUE : AL.AL_FALSE ); + + <span class=codeComment>// Save the source id.</span> + sources.add(new Integer(source[0])); + + <span class=codeComment>// Return the source id.</span> + <span class=codeKeyword><font color="0000ff">return</font></span> source[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=codeKeyword><font color="#0000FF">public static void</font></span><font color="#0000FF"> </font>killALLoadedData() { + loadedFiles.clear(); +} + +</pre> +<p>The global vector 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><font color="#006600">// Source id's.</font></span> + +<font color="#0000FF">int</font> phaser1; +<font color="#0000FF">int</font> phaser2; + +<span class=codeKeyword><font color="#0000FF">public static void</font></span><font color="#0000FF"> </font>loadALData() <font color="#0000FF">throws</font> IOException { + <font color="#006600"><span class=codeComment>// Anything for your application here. No worrying about buffers.</span></font> + phaser1 = loadALSample("wavdata/phaser.wav", <span class=codeKeyword><font color="#0000FF">false</font></span>); + phaser2 = loadALSample("wavdata/phaser.wav", <span class=codeKeyword><font color="#0000FF">true</font></span>); + + killLoadedALData(); +} + +</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=codeKeyword><font color="#0000FF">public static void</font></span><font color="#0000FF"> </font>killALData() +{ +<font color="006600"> <span class=codeComment>// Release all buffer data.</span> +</font> Iterator iter = buffers.iterator(); + <span class=codeKeyword><font color="#0000FF">while</font>(iter.hasNext()) {</span> + al.alDeleteBuffers(1, new int[] { ((Integer)iter.next()).intValue()); + } +<font color="006600"> <span class=codeComment>// Release all source data.</span> +</font> iter = sources.iterator();<font color="006600"> +</font> <span class=codeKeyword><font color="#0000FF">while</font>(iter.hasNext()) {</span> + al.alDeleteSources(1, new int[] { ((Integer)iter.next()).intValue()); + }<font color="006600"> + <span class=codeComment>// Destroy the lists.</span></font> + buffers.clear(); + sources.clear(); +} +</pre> +<p>All along we have been storing the buffer and source id's into stl vectors. + 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><font color="#0000FF">try</font></span> { + initOpenAL(); + loadALData(); + } <span class=codeKeyword><font color="#0000FF">catch</font></span>(IOException err){ + err.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> +<table border="0" cellspacing="1" style="border-collapse: collapse" width="100%" id="AutoNumber2" bgcolor="#666699"> + <tr> + <td width="40%"> <p dir="ltr"><font color="#FFFFFF" size="2">� 2003 DevMaster.net. + All rights reserved.</font></td> + <td width="60%"> <p align="right" dir="ltr"><font size="2"><a href="mailto:[email protected]"> + <font color="#FFFFFF">Contact us</font></a><font color="#FFFFFF"> if you + want to write for us or for any comments, suggestions, or feedback.</font></font></td> + </tr> +</table> +</body> +</html> diff --git a/www/index.html b/www/index.html index 8e09685..a34e552 100644 --- a/www/index.html +++ b/www/index.html @@ -104,6 +104,9 @@ Look at the ALC</li> <li><a href="devmaster/lesson5.html">lesson 5:</a> Sources Sharing Buffers</li> + <li><a href="devmaster/lesson6.html">lesson 6:</a> Advanced + Loading and Error Handles.</li> + <li>lesson 7: The Doppler Effect</li> </ul> </li> </ul></td> |