summaryrefslogtreecommitdiffstats
path: root/www/devmaster/lesson6.html
diff options
context:
space:
mode:
Diffstat (limited to 'www/devmaster/lesson6.html')
-rw-r--r--www/devmaster/lesson6.html344
1 files changed, 344 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>