summaryrefslogtreecommitdiffstats
path: root/www/devmaster/lesson5.html
blob: 66c35243b3a2df0e2be8d73fc4fc51e6529f234d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
<!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 ID="NavBar" WIDTH=800 HEIGHT=64 SRC="http://games.dev.java.net/images/navbar2p.gif" VSPACE=0 HSPACE=0 ALIGN="TOP" BORDER=0 USEMAP="#NavBar_MAP" NOFINSIDE="~!   ~!" > </div>
  
<MAP NAME="NavBar_Map">
  <AREA SHAPE="rect" ALT="Projects" COORDS="356,14,440,46" HREF="http://games.dev.java.net" TARGET="_self">
  <AREA SHAPE="rect" ALT="Wiki" COORDS="643,14,695,46" HREF="http://wiki.java.net/bin/view/Games">
  <AREA SHAPE="rect" ALT="Weblogs" COORDS="562,15,624,46" HREF="http://weblogs.java.net/weblogs/project/games">
  <AREA SHAPE="rect" COORDS="463,16,541,45" HREF="http://www.javagaming.org/cgi-bin/JGNetForums/YaBB.cgi" target="_top" ALT="Forums">
  <AREA SHAPE="rect" ALT="JavaGames Home" COORDS="147,16,334,48" HREF="http://community.java.net/games">
  <AREA SHAPE="rect" ALT="Java.net" COORDS="21,15,128,46" 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">Sources Sharing Buffers<br>
  </font><strong><font size="4">Lesson 5</font></strong></p>
<p align="right" class="ArticleAuthor">Author: <a href="mailto:lightonthewater@hotmail.com">Jesse 
  Maurais<br>
  </a>Adapted For Java By: <a href="athomas@dev.java.net">Athomas 
  Goldberg </a></p>

<p><a href="http://download.java.net/media/joal/webstart/joal-lesson5.jnlp">Launch the Demo via Java Web Start</a></p>

<p align="justify">At this point in the OpenAL series I will show one method of 
  having your buffers be shared among many sources. This is a very logical and 
  natural step, and it is so easy that some of you may have already done this 
  yourself. If you have you may just skip this tutorial in total and move on. 
  But for those keeners who want to read all of the info I've got to give, you 
  may find this interesting. Plus, we will be implementing the Alc layer directly 
  so that we can use some of that knowledge gained in lesson 4. On top of that 
  we will create a program you might even use!</p>
<p align="justify">Well, here we go. I've decided to only go over bits of the 
  code that are significant, since most of the code has been repeated so far in 
  the series. Check out the full source code in the download.</p>
<pre class=code><span class=codeComment><font color="#0000FF">static</font> ALC alc;
<font color="#0000FF">static</font> AL al;<font color="#006600">

// These index the buffers.</font></span>
<span class=codeKeyword><font color="#0000FF">public static final int</font></span> THUNDER 	 = 0;
<span class=codeKeyword><font color="#0000FF">public static final int</font></span> WATERDROP   = 1;
<span class=codeKeyword><font color="#0000FF">public static final int</font></span> STREAM      = 2;
<span class=codeKeyword><font color="#0000FF">public static final int</font></span> RAIN        = 3;

<span class=codeKeyword><font color="#0000FF">public static final int</font></span> CHIMES      = 4;
<span class=codeKeyword><font color="#0000FF">public static final int</font></span> OCEAN       = 5;
<span class=codeKeyword><font color="#0000FF">public static final int</font></span> NUM_BUFFERS = 6;

<font color="#006600"><span class=codeComment>// Buffers hold sound data.</span>
</font><font color="#0000FF">static int</font>[] buffers = <font color="#0000FF">new int</font>[NUM_BUFFERS];

<span class=codeComment><font color="#006600">// A vector list of sources for multiple emissions.</font></span>
Vector sources = <font color="#0000FF">new </font>Vector();

</pre>
<p align="justify">First I've written out a few macros that we can use to index 
  the buffer array. We will be using several wav files so we need quite a few 
  buffers here. Instead of using an array for storing the sources we will use 
  a Vector. We chose to do this because it allows us to have a dynamic number 
  of sources. We can just keep adding sources to the scene until OpenAL runs out 
  of them. This is also the first tutorial where we will deal with sources as 
  being a resource that will run out. And yes, they will run out; they are finite.</p>
<pre class=code><font color="#0000FF">static int </font>initOpenAL() {
 
    ALC.Device device;
    ALC.Context context;
    String deviceSpecifier;
    String deviceName = "DirectSound3D";

    <span class=codeComment><font color="#006600">// Get handle to device.</font></span>
    device = alc.alcOpenDevice(deviceName);

    <font color="#006600"><span class=codeComment>// Get the device specifier.</span></font>
    deviceSpecifier = alc.alcGetString(device, ALC.ALC_DEVICE_SPECIFIER);

    System.out.println("Using device &quot; + deviceSpecifier);

    <span class=codeComment><font color="#006600">// Create audio context.</font></span>
    context = alc.alcCreateContext(device, <font color="#0000FF">null</font>);

    <span class=codeComment><font color="#006600">// Set active context.</font></span>
    alc.alcMakeContextCurrent(context);

    <span class=codeComment><font color="#006600">// Check for an error.</font></span>
    <span class=codeKeyword><font color="#0000FF">if </font></span>(alc.alcGetError() != ALC.ALC_NO_ERROR)
        <span class=codeKeyword><font color="#0000FF">return</font></span> AL.AL_FALSE;

    <span class=codeKeyword><font color="#0000FF">return</font></span> AL.AL_TRUE;
}
</pre>
<p align="justify">This is some sample code from what we learned in the last tutorial. 
  We get a handle to the device &quot;DirectSound3D&quot;, and then obtain a rendering 
  context for our application. This context is set to current and the function 
  will check if everything went smoothly before we return success.</p>
<pre class=code><span class=codeKeyword><font color="#0000FF">static void</font></span> exitOpenAL() {
    ALC.Context curContext;
    ALC.Device curDevice;

    <font color="#006600"><span class=codeComment>// Get the current context.</span></font>
    curContext = alc.alcGetCurrentContext();

    <span class=codeComment><font color="#006600">// Get the device used by that context.</font></span>
    curDevice = alc.alcGetContextsDevice(curContext);

<font color="#006600">    <span class=codeComment>// Reset the current context to NULL.</span>
</font>    alc.alcMakeContextCurrent(<font color="#0000FF">null</font>);

<font color="#006600">    <span class=codeComment>// Release the context and the device.</span>
</font>    alc.alcDestroyContext(curContext);
    alc.alcCloseDevice(curDevice);
}
</pre>
<p align="justify">This will do the opposite we did in the previous code. It retrieves 
  the context and device that our application was using and releases them. It 
  also sets the current context to null (the default) which will suspend the processing 
  of any data sent to OpenAL. It is important to reset the current context to 
  null or else you will have an invalid context trying to process data. The results 
  of doing this can be unpredictable.</p>
<p align="justify">If you are using a multi-context application you may need to 
  have a more advanced way of dealing with initialization and shutdown. I would 
  recommend making all devices and contexts global and closing them individually, 
  rather than retrieving the current context.</p>
<pre class=code><font color="#0000FF">static int </font>loadALData() {
    <font color="#006600"><span class=codeComment>// Variables to load into.</span></font>
    <font color="#0000FF">int</font>[] format = new <font color="#0000FF">int</font>[1];
    <font color="#0000FF">int</font>[] size = new <font color="#0000FF">int</font>[1];
    ByteBuffer[] data = new ByteBuffer[1];
    <font color="#0000FF">int</font>[] freq = new <font color="#0000FF">int</font>[1];
    <font color="#0000FF">int</font>[] loop = new <font color="#0000FF">int</font>[1];

    <span class=codeComment><font color="#006600">// Load wav data into buffers.</font></span>
    al.alGenBuffers(NUM_BUFFERS, buffers);

    <span class=codeKeyword><font color="#0000FF">if</font></span>(al.alGetError() != AL.AL_NO_ERROR)
        <span class=codeKeyword><font color="#0000FF">return</font></span> AL.AL_FALSE;

    ALut.alutLoadWAVFile("wavdata/thunder.wav", format, data, size, freq, loop);
    al.alBufferData(buffers[THUNDER], format[0], data[0], size[0], freq[0]);
    ALut.alutUnloadWAV(format[0], data[0], size[0], freq[0]);

    ALut.alutLoadWAVFile("wavdata/waterdrop.wav", format, data, size, freq, loop);
    al.alBufferData(buffers[WATERDROP], format[0], data[0], size[0], freq[0]);
    ALut.alutUnloadWAV(format[0], data[0], size[0], freq[0]);

    ALut.alutLoadWAVFile("wavdata/stream.wav", format, data, size, freq, loop);
    al.alBufferData(buffers[STREAM], format[0], data[0], size[0], freq[0]);
    ALut.alutUnloadWAV(format[0], data[0], size[0], freq[0]);

    ALut.alutLoadWAVFile("wavdata/rain.wav", format, data, size, freq, loop);
    al.alBufferData(buffers[RAIN], format[0], data[0], size[0], freq[0]);
    ALut.alutUnloadWAV(format[0], data[0], size[0], freq[0]);

    ALut.alutLoadWAVFile("wavdata/ocean.wav", format, data, size, freq, loop);
    al.alBufferData(buffers[OCEAN], format[0], data[0], size[0], freq[0]);
    ALut.alutUnloadWAV(format[0], data[0], size[0], freq[0]);

    ALut.alutLoadWAVFile("wavdata/chimes.wav", format, data, size, freq, loop);
    al.alBufferData(buffers[CHIMES], format[0], data[0], size[0], freq[0]);
    ALut.alutUnloadWAV(format[0], data[0], size[0], freq[0]);

<font color="#006600">    <span class=codeComment>// Do another error check and return.</span>
    </font><span class=codeKeyword><font color="#0000FF">if</font> </span>(al.alGetError() != AL.AL_NO_ERROR)
        <span class=codeKeyword><font color="#0000FF">return</font></span> AL.AL_FALSE;

    <span class=codeKeyword><font color="#0000FF">return</font></span> AL.AL_TRUE;
}
</pre>
<p align="justify">We've totally removed the source generation from this function. 
  That's because from now on we will be initializing the sources separately.</p>
<pre class=code><span class=codeKeyword><font color="#0000FF">static void</font></span><font color="#0000FF"> </font>addSource(int type) {
    <font color="#0000FF">int[]</font> source = <font color="#0000FF">new int</font>[1];

    al.alGenSources(1, source);

    <span class=codeKeyword><font color="#0000FF">if</font> </span>(al.alGetError() != AL.AL_NO_ERROR) {
        System.err.println("Error generating audio source.");
        System.exit(1);
    }

    al.alSourcei (source[0], AL.AL_BUFFER,   buffers[type]);
    al.alSourcef (source[0], AL.AL_PITCH,    1.0          );
    al.alSourcef (source[0], AL.AL_GAIN,     1.0          );
    al.alSourcefv(source[0], AL.AL_POSITION, sourcePos    );
    al.alSourcefv(source[0], AL.AL_VELOCITY, sourceVel    );
    al.alSourcei (source[0], AL.AL_LOOPING,  AL.AL_TRUE      );

    al.alSourcePlay(source);

    sources.put(new Integer(source[0]));
}

</pre>
<p align="justify">Here's the function that will generate the sources for us. 
  This function will generate a single source for any one of the loaded buffers 
  we generated in the previous source. Given the buffer index 'type', which is 
  one of the macros we created right from the start of this tutorial. We do an 
  error check to make sure we have a source to play (like I said, they are finite). 
  If a source cannot be allocated then the program will exit.</p>
<pre class=code><span class=codeKeyword><font color="#0000FF">static void</font></span><font color="#0000FF"> </font>killALData() {

    Iterator iter = sources.iterator();
    <span class=codeKeyword><font color="#000000">while</font></span>(iter.hasNext()) {
        al.alDeleteSources(1, <font color="#0000FF">new int</font>[] { ((Integer)iter.next()).intValue() });
    }
    sources.clear();
    al.alDeleteBuffers(NUM_BUFFERS, buffers);
    exitOpenAL();
}
</pre>
<p align="justify">This function has been modified a bit to accommodate the Vector. 
  We have to delete each source in the list individually and then clear the list 
  which will effectively destroy it.</p>
<pre class=code><font color="#0000FF">    char</font>[] c = <font color="#0000FF">new char</font>[1];

<font color="#0000FF">    </font><font color="#0000FF">while</font>(c[0] != 'q') {	
<font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">try</font> {
<font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">    </font>BufferedReader buf =
<font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">new</font> BufferedReader(<font color="#0000FF">new</font> InputStreamReader(System.in));
<font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">    </font>System.out.println(&quot;Press a key and hit ENTER: \n&quot; +
<font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">   </font>&quot;\t'w' for Water Drop\n&quot; +
<font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">   </font>&quot;\t't' for Thunder\n&quot; +
<font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">   </font>&quot;\t's' for Stream\n&quot; +
<font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">   </font>&quot;\t'r' for Rain\n&quot; +
<font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">   </font>&quot;\t'o' for Ocean\n&quot; +
<font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">   </font>&quot;\t'c' for Chimes\n&quot; +
<font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">   </font>&quot;\n'q' to Quit\n&quot;);
<font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">    </font>buf.read(c);
<font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">    </font><font color="#0000FF">switch</font>(c[0]) {
<font color="#0000FF">                case</font> 'w': addSource(WATERDROP); <font color="#0000FF">break</font>;
<font color="#0000FF"></font><font color="#0000FF">                case</font> 't': addSource(THUNDER); <font color="#0000FF">break</font>;
<font color="#0000FF"></font><font color="#0000FF">                case</font> 's': addSource(STREAM); <font color="#0000FF">break</font>;
<font color="#0000FF"></font><font color="#0000FF">                case</font> 'r': addSource(RAIN); <font color="#0000FF">break</font>;
<font color="#0000FF"></font><font color="#0000FF">                case</font> 'o': addSource(OCEAN); <font color="#0000FF">break</font>;
<font color="#0000FF"></font><font color="#0000FF">                case</font> 'c': addSource(CHIMES); <font color="#0000FF">break</font>;
<font color="#0000FF"></font>}
<font color="#0000FF">    </font><font color="#0000FF">    </font>} <font color="#0000FF">catch</font> (IOException e) {
			System.exit(1);
<font color="#0000FF">    </font><font color="#0000FF">    </font>}
<font color="#0000FF">    </font>}</pre>
<p align="justify">Here is the programs inner loop taken straight out of our main. 
  Basically it waits for some keyboard input and on certain key hits it will create 
  a new source of a certain type and add it to the audio scene. Essentially what 
  we have created here is something like one of those nature tapes that people 
  listen to for relaxation. Ours is a little better since it allows the user to 
  customize which sounds that they want in the background. Pretty neat eh? I've 
  been listening to mine while I code. It's a Zen experience (I'm listening to 
  it right now).</p>
<p align="justify">The program can be expanded for using more wav files, and have 
  the added feature of placing the sources around the scene in arbitrary positions. 
  You could even allow for sources to play with a given frequency rather than 
  have them loop. However this would require GUI routines that go beyond the scope 
  of the tutorial. A full featured &quot;Weathering Engine&quot; would be a nifty 
  program to make though. ;)</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:webmaster@devmaster.net"> 
        <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>