Jogamp
www/devmaster/lesson*.html: Fix links to original DevMaster.net articles.
[joal-demos.git] / www / devmaster / lesson8.html
CommitLineData
49b57bac 1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
2<html>
3<head>
aaf9d0a3 4 <title>JOAL OpenAL Tutorials from DevMaster.net Lesson 8: Ogg/Vorbis Streaming</title>
49b57bac 5 <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
6 <link rel="stylesheet" type="text/css" href="general.css">
7</head>
8<body>
aaf9d0a3 9<a href="../index.html"><img src="../../../images/jogamp_symbols/website_final_blue_joal_346x70pel.png" alt="JOAL Symbol"></a><a href="http://www.openal.org"><img src="../openal_c.gif"></a>
49b57bac 10<br>
11
12
13OpenAL Tutorials from DevMaster.net. Reprinted with Permission.<br>
14
15
16<br>
17
18
19
20<table style="border-collapse: collapse;" id="AutoNumber1" bgcolor="#666699" border="0" cellpadding="0" cellspacing="0" height="12" width="100%">
21
22
23 <tbody>
24
25 <tr>
26
27
28 <td height="12" valign="middle" width="47%">
29
30 <p><b><font color="#ffffff">OpenAL
31 Tutorials</font></b></p>
32
33 </td>
34
35
36 <td align="right" height="12" valign="middle" width="53%">
37
38 <p align="right"><a href="http://devmaster.net/"><font color="#66ff99">DevMaster.net</font></a></p>
39
40 </td>
41
42
43 </tr>
44
45
46
47
48 </tbody>
49</table>
50
51
52<p class="title" align="left"><span class="title"><font size="5">Ogg/Vorbis Streaming
53</font></span><font size="4"><br>
54
55
56<b>Lesson 8</b></font></p>
57
58
59
60<p class="title" align="right"> <span class="author">Author: <a href="mailto:lightonthewater@hotmail.com"><font color="#888888">Jesse
61 Maurais<br>
62
63
64 </font></a></span>Adapted for Java by: <a href="mailto:krishna_gadepalli@dev.java.net"><font color="#888888">Krishna
65 Gadepalli</font></a></p>
66
67
68<p></p>
69
70
71
2c621756 72<p><a href="../../../deployment/jogamp-current/joal-demos/jnlp-files/joal-lesson8.jnlp">Launch the Demo via Java Web Start</a></p>
49b57bac 73
74
75
ec096f51 76<p align="justify">This is a translation of <a href="http://devmaster.net/posts/2895/openal-lesson-8-oggvorbis-streaming-using-the-source-queue">
49b57bac 77OpenAL Lesson 8: OggVorbis Streaming Using The Source Queue</a>
78tutorial from <a href="http://devmaster.net/">DevMaster.net</a> to JOAL.
79
80</p>
81
82<p>
83<cite>"This software is based on or using the J-Ogg library available
84 from http://www.j-ogg.de and copyrighted by Tor-Einar Jarnbjo."</cite>
85
86</p>
87
88<h3>An Introduction to OggVorbis</h3>
89
90
91
92<p>Ever heard of Ogg? There's more to it than a funny sounding name. It's the
93biggest thing to happen for audio compression since mp3's (also typically used
94for music). Hopefully, one day, it will replace mp3's as the mainstream standard
95for compressed audio. Is it better than mp3? That is a question that is a little
96more difficult to answer. It's a pretty strong debate in some crowds. There are
97various arguments about compression ratio vs. sound quality which can sometimes
98get really cumbersome to read through. I personally don't have any opinion on
99which is "better". I feel the evidence in either case is arguable and not worth
100mentioning. But for me the fact that Ogg is royalty free (which mp3 is not) wins
101the argument hands down. The mp3 licensing fee is by no means steep for
102developers with deep pockets, but as an independent working on a project in your
103spare time and on minimal resources, shelling out a few grand in fees is not an
104option. Ogg may be the answer to your prayers.</p>
105
106
107
108<h3>Designing Your OggVorbis Streaming API</h3>
109
110
111<p>Without further ado let's get to some code.</p>
112
113
114<p>This tutorial will be written in Java and has two main classes:
115<a href="https://joal-demos.dev.java.net/source/browse/joal-demos/src/java/demos/devmaster/lesson8/OggDecoder.java?view=markup"><var>OggDecoder</var></a> and
116<a href="https://joal-demos.dev.java.net/source/browse/joal-demos/src/java/demos/devmaster/lesson8/OggStreamer.java?view=markup"><var>OggStreamer</var></a>. The
117<var>OggDecoder</var> class is wrapper on the <a href="http://www.j-ogg.de">J-Ogg</a> library to decode an Ogg/Vorbis stream and will not be described in this
118tutorial. <var>OggStreamer</var> which is the main class doing most of the working for streaming using OpenAL is explained below.
119
120</p>
121
c4d389f4 122<pre class="code">
123 // The size of a chunk from the stream that we want to read for each update.
124 private static int BUFFER_SIZE = 4096*8;
125
126 // The number of buffers used in the audio pipeline
127 private static int NUM_BUFFERS = 2;
128</pre>
49b57bac 129
130
131
132<p>'<var>BUFFER_SIZE</var>' defines how big a chunk we want
133to read from the stream on each update. You will find (with a little
134experimentation) that larger buffers usually produce better sound quality since
135they don't update as often, and will generally avoid any abrupt pauses or sound
136distortions. Of course making your buffer too big will also eat up more memory.
137Making a stream redundant. I beleive 4096 is the minimum buffer size one can
138have. I don't recommend using one that small. I tried, and it caused many
139clicks.</p>
140
141
142<p>So why should we even bother with streaming? Why not load the whole file into
143a buffer and then play it? Well, that is a good question. The quick answer is
144that there is too much audio data. Even though the actual Ogg file size is quite
145small (usually around 1-3 MB) you must remember that is compressed audio data.
146You cannot play the compressed form of the data. It must be decompressed and
147formatted into a form OpenAL recognizes before it can be used in a buffer. That
148is why we stream the file.</p>
149
150
151
152<pre class="code"> /**<br> * The main loop to initialize and play the entire stream<br> */<br> public boolean playstream() { ... }<br><br> /**<br> * Open the Ogg/Vorbis stream and initialize OpenAL based<br> * on the stream properties<br> */<br> public boolean open() { ... }<br><br> /**<br> * OpenAL cleanup<br> */<br> public void release() { ... }<br><br> /**<br> * Play the Ogg stream<br> */<br> public boolean playback() { ... }<br><br> /**<br> * Check if the source is playing<br> */<br> public boolean playing() { ... }<br><br> /**<br> * Update the stream if necessary<br> */<br> public boolean update() { ... }<br><br> /**<br> * Reloads a buffer (reads in the next chunk)<br> */<br> public boolean stream(int buffer) { ... }<br><br> /**<br> * Empties the queue<br> */<br> protected void empty() { ... }<br></pre>
153
154
155
156<p>This will be the base of our Ogg streaming api. The public methods are
157everything that one needs to actually get the Ogg to play. Protected methods are
158more internal procedures. I won't go over each function
159just yet. I believe my comments should give you an idea of what they're for.</p>
160
161
162
c4d389f4 163<pre class="code">
164 // Buffers hold sound data. There are two of them by default (front/back)
165 private int[] buffers = new int[NUM_BUFFERS];
166
167 // Sources are points emitting sound.
168 private int[] source = new int[1];
169</pre>
49b57bac 170
171
172<p>First thing that I want to point out is that we have 2 buffers dedicated to
173the stream rather than the 1 we have always used for wav files. This is
174important. To understand this I want you to think about how double buffering
175works in OpenGL/DirectX. There is a front buffer that is "on screen" at any
176given second, while a back buffer is being drawn to. Then they are swapped. The
177back buffer becomes the front and vice versa. Pretty much the same principle is
178applied here. There is a buffer being played and one waiting to be played. When
179the buffer being played has finished the next one starts. While the next buffer
180is being played, the first one is refilled with a new chunk of data from the
181stream and is set to play once the one playing is finished. Confused yet? I'll
182explain this in more detail later on.</p>
183
184
185
186<pre class="code"> public boolean open() {<br> oggDecoder = new OggDecoder(url);<br><br> if (!oggDecoder.initialize()) {<br> System.err.println("Error initializing ogg stream...");<br> return false;<br> }<br> <br> if (oggDecoder.numChannels() == 1)<br> format = AL.AL_FORMAT_MONO16;<br> else<br> format = AL.AL_FORMAT_STEREO16;<br> <br> rate = oggDecoder.sampleRate();<br><br> ...<br> }<br></pre>
187
188
189
190<p>This creates a decoder for Ogg file, initializes it and grabs some information on the file. We extract the OpenAL format
191enumerator based on how many channels are in the Ogg and then make a not of the sample rate.</p>
192
193
194
c4d389f4 195<pre class="code"> public boolean open() {<br> ...<br><br> al.alGenBuffers(NUM_BUFFERS, buffers, 0); check();<br> al.alGenSources(1, source, 0); check();<br><br> al.alSourcefv(source[0], AL.AL_POSITION , sourcePos, 0);<br> al.alSourcefv(source[0], AL.AL_VELOCITY , sourceVel, 0);<br> al.alSourcefv(source[0], AL.AL_DIRECTION, sourceDir, 0);<br> <br> al.alSourcef(source[0], AL.AL_ROLLOFF_FACTOR, 0.0f );<br> al.alSourcei(source[0], AL.AL_SOURCE_RELATIVE, AL.AL_TRUE);<br><br> ...<br> }<br></pre>
49b57bac 196
197
198
199<p>You've seen most of this before. We set a bunch of default values, position,
200velocity, direction... But what is rolloff factor? This has to do with
201attenuation. I will cover attenuation in a later article so I won't go too
202in-depth, but I will explain it basically. Rolloff factor judges the strength of
203attenuation over distance. By setting it to 0 we will have turned it off. This
204means that no matter how far away the Listener is to the source of the Ogg they
205will still hear it. The same idea applies to source relativity.</p>
206
207
208
c4d389f4 209<pre class="code">
210 public void release() {
211 al.alSourceStop(source[0]);
212 empty();
213
214 for (int i = 0; i < NUM_BUFFERS; i++) {
215 al.alDeleteSources(i, source, 0); check();
216 }
217 }
218</pre>
49b57bac 219
220
221<p>We can clean up after ourselves using this. We stop the source, empty out any
222buffers that are still in the queue, and destroy our objects.
223
224</p>
225
c4d389f4 226<pre class="code">
227 public boolean playback() {
228 if (playing())
229 return true;
230
231 for (int i = 0; i < NUM_BUFFERS; i++) {
232 if (!stream(buffers[i]))
233 return false;
234 }
235
236 al.alSourceQueueBuffers(source[0], NUM_BUFFERS, buffers, 0);
237 al.alSourcePlay(source[0]);
238
239 return true;
240 }
241</pre>
49b57bac 242
243
244
245<p>This will start playing the Ogg. If the Ogg is already playing then there is
246no reason to do it again. We must also initialize the buffers with their first
247data set. We then queue them and tell the source to play them. This is the first
248time we have used '<var>alSourceQueueBuffers</var>'. What it does basically is give the
249source multiple buffers. These buffers will be played sequentially. I will
250explain more on this along with the source queue momentarily. One thing to make
251a note of though: if you are using a source for streaming never bind a buffer to
252it using '<var>alSourcei</var>'. Always use '<var>alSourceQueueBuffers</var>' consistently.</p>
253
254
255
256<pre class="code"> public boolean playing() {<br> int[] state = new int[1];<br> <br> al.alGetSourcei(source[0], AL.AL_SOURCE_STATE, state, 0);<br> <br> return (state[0] == AL.AL_PLAYING);<br> }<br></pre>
257
258
259<p>This simplifies the task of checking the state of the source. </p>
260
261
262
263<pre class="code"> public boolean update() {<br> int[] processed = new int[1];<br> boolean active = true;<br><br> al.alGetSourcei(source[0], AL.AL_BUFFERS_PROCESSED, processed, 0);<br><br> while (processed[0] &gt; 0)<br> {<br> int[] buffer = new int[1];<br> <br> al.alSourceUnqueueBuffers(source[0], 1, buffer, 0); check();<br><br> active = stream(buffer[0]);<br><br> al.alSourceQueueBuffers(source[0], 1, buffer, 0); check();<br><br> processed[0]--;<br> }<br><br> return active;<br> }<br></pre>
264
265
266<p>Here is how the queue works in a nutshell: There is a 'list' of buffers. When
267you unqueue a buffer it gets popped off of the front. When you queue a buffer it
268gets pushed to the back. That's it. Simple enough?</p>
269
270
271<p>This is 1 of the 2 most important methods in the class. What we do in this
272bit of code is check if any buffers have already been played. If there is then
273we start popping each of them off the back of the queue, we refill the buffers
274with data from the stream, and then we push them back onto the queue so that
275they can be played. Hopefully the Listener will have no idea that we have done
276this. It should sound like one long continuous chain of music. The '<var>stream</var>'
277function also tells us if the stream is finished playing. This flag is reported
278back when the function returns.</p>
279
280
281
282<pre class="code"> public boolean stream(int buffer) {<br> byte[] pcm = new byte[BUFFER_SIZE];<br> int size = 0;<br><br> try {<br> if ((size = oggDecoder.read(pcm)) &lt;= 0)<br> return false;<br> } catch (Exception e) {<br> e.printStackTrace();<br> return false;<br> }<br><br> ByteBuffer data = ByteBuffer.wrap(pcm, 0, size);<br> al.alBufferData(buffer, format, data, size, rate);<br> check();<br> <br> return true;<br> }<br></pre>
283
284
285<p>This is another important method of the class. This part fills the buffers
286with data from the Ogg bitstream. It's a little harder to get a grip on because
287it's not explainable in a top down manner. '<var>oggDecoder.read</var>' does exactly what you may
288be thinking it does; it reads data from the Ogg bitstream. The <a href="http://www.j-ogg.de/">j-ogg</a> library does all
289the decoding of the bitstream, so we don't have to worry about that. This
290function takes a <var>byte</var> array as an argument and will decode atmost the capacity of
291this array. </p>
292
293
294<p>The return value of '<var>oggDecoder.read</var>' indicates several things. If the value of the
295result is positive then it represents how much data was read. This is important
296because '<var>read</var>' may not be able to read the entire size requested (usually
297because it's at the end of the file and there's nothing left to read). Use the
298result of '<var>read</var>' over '<var>BUFFER_SIZE</var>' in any case. If the result of '<var>read</var>'
299happens to be negative then it indicates that there was an error in the
300bitstream. If the result happens to equal zero then there is nothing left in the file to play.</p>
301
302
303
304<p> The last part of this method is the call to '<var>alBufferData</var>'
305which fills the buffer id with the data that we streamed from the Ogg using '<var>read</var>'.
306We employ the '<var>format</var>' and '<var>rate</var>' values that we set up earlier</p>
307
308
309
310<pre class="code"> protected void empty() {<br> int[] queued = new int[1];<br> <br> al.alGetSourcei(source[0], AL.AL_BUFFERS_QUEUED, queued, 0);<br> <br> while (queued[0] &gt; 0)<br> {<br> int[] buffer = new int[1];<br> <br> al.alSourceUnqueueBuffers(source[0], 1, buffer, 0);<br> check();<br><br> queued[0]--;<br> }<br><br> oggDecoder = null;<br> }<br></pre>
311
312
313<p>This method will will unqueue any buffers that are pending on the source. </p>
314
315
316
317<pre class="code"> protected void check() {<br> if (al.alGetError() != AL.AL_NO_ERROR)<br> throw new ALException("OpenAL error raised...");<br> }<br></pre>
318
319
320<p>This saves us some typing for our error checks.</p>
321
322
323
324<h3>Making Your Own OggVorbis Player</h3>
325
326
327<p>If you're with me so far then you must be pretty serious about getting this
328to work for you. Don't worry! We are almost done. All that we need do now is use
329our newly designed class to play an Ogg file. It should be a relatively simple
330process from here on in. We have done the hardest part. I won't assume that you
331will be using this in a game loop, but I'll keep it in mind when designing the
332loop. </p>
333
334<p>This should be a no-brainer. </p>
335
336
337<pre class="code">
338 public boolean playstream() {
339 if (!open())
340 return false;
341
342 oggDecoder.dump();
343
344 if (!playback())
345 return false;
346
347 while (update()) {
348 if (playing()) continue;
349
350 if (!playback())
351 return false;
352 }
353
354 return true;
355 }
356</pre>
357
358<p>The program opens the stream, dumps some stream information and then
359will continually loop as long as the '<var>update</var>' method continues to
360return true, and it will continue to return true as long as it can successfully
361read and play the audio stream. Within the loop we will make sure that the Ogg
362is playing.</p>
363
364<h3>Answers To Questions You May Be Asking</h3>
365
366
367<h4>Can I use more than one buffer for the stream?</h4>
368
369
370<p>In short, yes. There can be any number of buffers queued on the source at a
371time. Doing this may actually give you better results too. As I said earlier,
372with just 2 buffers in the queue at any time and with the cpu being clocked out
373(or if the system hangs), the source may actually finish playing before the
374stream has decoded another chunk. Having 3 or even 4 buffers in the queue will
375keep you a little further ahead in case you miss the update.</p>
376
377
378<h4>How often should I call ogg.update?</h4>
379
380
381<p>This is going to vary depending on several things. If you want a quick answer
382I'll tell you to update as often as you can, but that is not really necessary.
383As long as you update before the source finishes playing to the end of the
384queue. The biggest factors that are going to affect this are the buffer size and
385the number of buffers dedicated to the queue. Obviously if you have more data
386ready to play to begin with less updates will be necessary.</p>
387
388
389<h4>Is it safe to stream more than one Ogg at a time?</h4>
390
391
392<p>It should be fine. I haven't performed any extreme testing but I don't see
393why not. Generally you will not have that many streams anyway. You may have one
394to play some background music, and the occasional character dialog for a game,
395but most sound effects are too short to bother with streaming. Most of your
396sources will only ever have one buffer attached to them.</p>
397
398
399<h4>So what is with the name?</h4>
400
401
402<p>"Ogg" is the name of Xiph.org's container format for audio, video, and
403metadata. "Vorbis" is the name of a specific audio compression scheme that's
404designed to be contained in Ogg. As for the specific meanings of the words...
405well, that's a little harder to tell. I think they involve some strange
406relationship to Terry Pratchett novels. Here is a little page that goes into the
407details.</p>
408
409<table style="border-collapse: collapse;" id="AutoNumber2" bgcolor="#666699" border="0" cellspacing="1" width="100%">
410
411
412 <tbody>
413
414 <tr>
415
416
417 <td width="40%">
418
419 <p dir="ltr"><font color="#ffffff" size="2">&copy; 2003 DevMaster.net.
420 All rights reserved.</font></p>
421
422 </td>
423
424
425 <td width="60%">
426
427 <p align="right" dir="ltr"><font size="2"><a href="mailto:webmaster@devmaster.net">
428 <font color="#ffffff">Contact us</font></a><font color="#ffffff"> if you
429 want to write for us or for any comments, suggestions, or feedback.</font></font></p>
430
431 </td>
432
433
434 </tr>
435
436
437
438 </tbody>
439</table>
440
441
442<p>&nbsp;</p>
443
444
445</body>
446</html>
http://JogAmp.org git info: FAQ, tutorial and man pages.