1 /*
2  * Copyright (c) 2002-2008 LWJGL Project
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * * Redistributions of source code must retain the above copyright
10  *   notice, this list of conditions and the following disclaimer.
11  *
12  * * Redistributions in binary form must reproduce the above copyright
13  *   notice, this list of conditions and the following disclaimer in the
14  *   documentation and/or other materials provided with the distribution.
15  *
16  * * Neither the name of 'LWJGL' nor the names of
17  *   its contributors may be used to endorse or promote products derived
18  *   from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 package org.lwjgl.test.openal;
33 
34 import java.nio.FloatBuffer;
35 import java.nio.IntBuffer;
36 
37 import org.lwjgl.BufferUtils;
38 import org.lwjgl.LWJGLUtil;
39 import org.lwjgl.input.Keyboard;
40 import org.lwjgl.input.Mouse;
41 import org.lwjgl.openal.AL;
42 import org.lwjgl.opengl.Display;
43 import org.lwjgl.util.WaveData;
44 
45 import static org.lwjgl.openal.AL10.*;
46 import static org.lwjgl.opengl.GL11.*;
47 import static org.lwjgl.util.glu.GLU.*;
48 
49 /**
50  * <br>
51  * This test demonstrates OpenAL positioning Based on the example by Chad Armstrong
52  * (http://www.edenwaith.com/products/pige/tutorials/openal.php)
53  *
54  * @author Brian Matzon <brian@matzon.dk>
55  * @version $Revision$
56  * $Id$
57  */
58 public class PositionTest extends BasicTest {
59 
60   /** *Small* glut implementation :) */
61   private GLUT glut;
62 
63   /** Width of window */
64   public static final int WINDOW_WIDTH = 640;
65 
66   /** Height of window */
67   public static final int WINDOW_HEIGHT = 480;
68 
69   /** LEFT enumeration */
70   public static final int LEFT = 0;
71 
72   /** CENTER enumeration */
73   public static final int CENTER = 1;
74 
75   /** RIGHT enumeration */
76   public static final int RIGHT = 2;
77 
78   /** Whether the demo is done */
79   private boolean finished;
80 
81   /** Whether in pause mode */
82   private boolean pauseMode;
83 
84   // OpenAL stuff
85   // ===================================================
86 
87   /** OpenAL buffers */
88   private IntBuffer soundBuffers = BufferUtils.createIntBuffer(3);
89 
90   /** OpenAL sources */
91   private IntBuffer soundSources = BufferUtils.createIntBuffer(3);
92 
93   /** Position of listener */
94   private FloatBuffer listenerPosition = createFloatBuffer(new float[] { 0.0f, 0.0f, 0.0f });
95 
96   /** Velocity of listener */
97   private FloatBuffer listenerVelocity = createFloatBuffer(new float[] { 0.0f, 0.0f, 0.0f });
98 
99   /** Orientation of listener */
100   private FloatBuffer listenerOrientation =
101     createFloatBuffer(new float[] { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f });
102 
103   /** Position of left sound */
104   private FloatBuffer leftPosition = createFloatBuffer(new float[] { -2.0f, 0.0f, 0.0f });
105 
106   /** Velocity of left sound */
107   private FloatBuffer leftVelocity = createFloatBuffer(new float[] { 0.0f, 0.0f, 0.0f });
108 
109   /** Position of center sound */
110   private FloatBuffer centerPosition = createFloatBuffer(new float[] { 0.0f, 0.0f, -4.0f });
111 
112   /** Velocity of center sound */
113   private FloatBuffer centerVelocity = createFloatBuffer(new float[] { 0.0f, 0.0f, 0.0f });
114 
115   /** Position of right sound */
116   private FloatBuffer rightPosition = createFloatBuffer(new float[] { 2.0f, 0.0f, 0.0f });
117 
118   /** Velocity of right sound */
119   private FloatBuffer rightVelocity = createFloatBuffer(new float[] { 0.0f, 0.0f, 0.0f });
120   // ---------------------------------------------------
121 
122   /**
123    * Runs the actual test, using supplied arguments
124    */
execute(String[] args)125   protected void execute(String[] args) {
126     // Setup needed stuff
127     try {
128       setup();
129     } catch (Exception e) {
130       System.out.println("Error setting up demonstration: ");
131       e.printStackTrace();
132       System.exit(-1);
133     }
134 
135     // run the actual demonstration
136     run();
137 
138     // shutdown
139     shutdown();
140   }
141 
142   /**
143    * Performs setup of demonstration
144    */
setup()145   private void setup() throws Exception {
146 
147     // Setup Window
148     // =====================================================
149     LWJGLUtil.log("Setting up window");
150 
151     // calc center
152     int centerX = (Display.getDisplayMode().getWidth() - WINDOW_WIDTH) / 2;
153     int centerY = (Display.getDisplayMode().getHeight() - WINDOW_HEIGHT) / 2;
154 
155     // setup window
156     setDisplayMode();
157     Display.create();
158     // -----------------------------------------------------
159 
160     // Setup OpenGL
161     // =====================================================
162     LWJGLUtil.log("Setting up OpenGL");
163 
164     glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
165     glMatrixMode(GL_PROJECTION);
166     glLoadIdentity();
167     gluPerspective(50.0f, (float) WINDOW_WIDTH / WINDOW_HEIGHT, 0.0f, 50.0f);
168     glMatrixMode(GL_MODELVIEW);
169     glLoadIdentity();
170     glTranslatef(0.0f, 0.0f, -6.6f);
171     glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
172     glut = this.new GLUT();
173 
174     Display.setVSyncEnabled(true);
175     // -----------------------------------------------------
176 
177     // Setup OpenAL
178     // =====================================================
179     LWJGLUtil.log("Setting up OpenAL");
180 
181     alListener(AL_POSITION, listenerPosition);
182     alListener(AL_VELOCITY, listenerVelocity);
183     alListener(AL_ORIENTATION, listenerOrientation);
184 
185     // creating buffers
186     LWJGLUtil.log("Creating buffers");
187     alGenBuffers(soundBuffers);
188     soundBuffers.rewind();
189 
190     // creating sources
191     alGenSources(soundSources);
192     soundSources.rewind();
193 
194     // load sound files (left, center, right).wav
195     LWJGLUtil.log("Loading soundfiles...");
196 
197     LWJGLUtil.log("Loading left.wav");
198     WaveData left = WaveData.create("left.wav");
199     alBufferData(soundBuffers.get(LEFT), left.format, left.data, left.samplerate);
200     alSourcef(soundSources.get(LEFT), AL_PITCH, 1.0f);
201     alSourcef(soundSources.get(LEFT), AL_GAIN, 1.0f);
202     alSource(soundSources.get(LEFT), AL_POSITION, leftPosition);
203     alSource(soundSources.get(LEFT), AL_VELOCITY, leftVelocity);
204     alSourcei(soundSources.get(LEFT), AL_BUFFER, soundBuffers.get(LEFT));
205     alSourcei(soundSources.get(LEFT), AL_LOOPING, AL_TRUE);
206 
207     LWJGLUtil.log("Loading center.wav");
208     WaveData center = WaveData.create("center.wav");
209     alBufferData(soundBuffers.get(CENTER), center.format, center.data, center.samplerate);
210     alSourcef(soundSources.get(CENTER), AL_PITCH, 1.0f);
211     alSourcef(soundSources.get(CENTER), AL_GAIN, 1.0f);
212     alSource(soundSources.get(CENTER), AL_POSITION, centerPosition);
213     alSource(soundSources.get(CENTER), AL_VELOCITY, centerVelocity);
214     alSourcei(soundSources.get(CENTER), AL_BUFFER, soundBuffers.get(CENTER));
215     alSourcei(soundSources.get(CENTER), AL_LOOPING, AL_TRUE);
216 
217     LWJGLUtil.log("Loading right.wav");
218     WaveData right = WaveData.create("right.wav");
219     alBufferData(soundBuffers.get(RIGHT), right.format, right.data, right.samplerate);
220     alSourcef(soundSources.get(RIGHT), AL_PITCH, 1.0f);
221     alSourcef(soundSources.get(RIGHT), AL_GAIN, 1.0f);
222     alSource(soundSources.get(RIGHT), AL_POSITION, rightPosition);
223     alSource(soundSources.get(RIGHT), AL_VELOCITY, rightVelocity);
224     alSourcei(soundSources.get(RIGHT), AL_BUFFER, soundBuffers.get(RIGHT));
225     alSourcei(soundSources.get(RIGHT), AL_LOOPING, AL_TRUE);
226 
227     LWJGLUtil.log("Soundfiles loaded successfully");
228     // -----------------------------------------------------
229 
230     Mouse.setGrabbed(true);
231   }
232 
233   /**
234    * Runs the actual demonstration
235    */
run()236   private void run() {
237     boolean firstRun = true;
238 
239     System.out.println("Press 1/4 (left), 2/5 (center) or 3/6 (right) to toggle sound");
240     System.out.println("Press LEFT/RIGHT to move along x axis");
241     System.out.println("Press SHIFT and either UP/DOWN to move along y axis");
242     System.out.println("Press UP/DOWN to move along z axis");
243     System.out.println("Move along the x and y axis with the mouse");
244     System.out.println("Press LEFT or RIGHT mouse button to move along z axis");
245     System.out.println("Press ESC to exit demo");
246 
247     LWJGLUtil.log(
248       "Listener position: "
249         + listenerPosition.get(0)
250         + ", "
251         + listenerPosition.get(1)
252         + ", "
253         + listenerPosition.get(2));
254     LWJGLUtil.log("Left position: " + leftPosition.get(0) + ", " + leftPosition.get(1) + ", " + leftPosition.get(2));
255     LWJGLUtil.log("Center position: " + centerPosition.get(0) + ", " + centerPosition.get(1) + ", " + centerPosition.get(2));
256     LWJGLUtil.log("Right position: " + rightPosition.get(0) + ", " + rightPosition.get(1) + ", " + rightPosition.get(2));
257 
258     while (!finished) {
259       // handle any input
260       handleInput();
261 
262       // allow window to process internal messages
263       Display.update();
264 
265       // render and paint if !minimized and not dirty
266       if(Display.isVisible()) {
267         render();
268       } else {
269         // sleeeeeep
270         pause(100);
271       }
272 
273       // act on pause mode
274       paused(!(Display.isVisible() || Display.isActive()));
275 
276       // start sound after first paint, since we don't want
277       // the delay before something is painted on the screen
278       if (firstRun && !pauseMode) {
279         firstRun = false;
280 
281         // start sounds with delays
282         startSounds();
283       }
284     }
285   }
286 
287   /**
288    * Starts playing the sounds at different times
289    */
startSounds()290   private void startSounds() {
291     alSourcePlay(soundSources.get(LEFT));
292     pause(300);
293     alSourcePlay(soundSources.get(CENTER));
294     pause(500);
295     alSourcePlay(soundSources.get(RIGHT));
296   }
297 
298   /**
299    * Handles any changes in pause mode
300    *
301    * @param paused Which pause mode to enter
302    */
paused(boolean paused)303   private void paused(boolean paused) {
304     // if requesting pause, and not paused - pause and stop sound
305     if(paused && !pauseMode) {
306       pauseMode = true;
307       alSourcePause(soundSources);
308       System.out.println("pauseMode = true");
309     }
310 
311     // else go out of pause mode and start sounds
312     else if(!paused && pauseMode) {
313       pauseMode = false;
314       startSounds();
315       System.out.println("pauseMode = false");
316     }
317   }
318 
319   /**
320    * Handles any input
321    */
handleInput()322   private void handleInput() {
323     // User wants to exit?
324     finished = Display.isCloseRequested() || Keyboard.isKeyDown(Keyboard.KEY_ESCAPE);
325     if (finished) {
326       return;
327     }
328 
329     boolean shift = Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) || Keyboard.isKeyDown(Keyboard.KEY_RSHIFT);
330 
331     // Test for play
332     // ============================================
333     if (Keyboard.isKeyDown(Keyboard.KEY_1)) {
334       alSourcePlay(soundSources.get(LEFT));
335       LWJGLUtil.log("Playing left.wav");
336     }
337 
338     if (Keyboard.isKeyDown(Keyboard.KEY_2)) {
339       alSourcePlay(soundSources.get(CENTER));
340       LWJGLUtil.log("Playing center.wav");
341     }
342 
343     if (Keyboard.isKeyDown(Keyboard.KEY_3)) {
344       alSourcePlay(soundSources.get(RIGHT));
345       LWJGLUtil.log("Playing right.wav");
346     }
347     // --------------------------------------------
348 
349     // Test for stop
350     // ============================================
351     if (Keyboard.isKeyDown(Keyboard.KEY_4)) {
352       alSourceStop(soundSources.get(LEFT));
353       LWJGLUtil.log("Stopped left.wav");
354     }
355 
356     if (Keyboard.isKeyDown(Keyboard.KEY_5)) {
357       alSourceStop(soundSources.get(CENTER));
358       LWJGLUtil.log("Stopped center.wav");
359     }
360 
361     if (Keyboard.isKeyDown(Keyboard.KEY_6)) {
362       alSourceStop(soundSources.get(RIGHT));
363       LWJGLUtil.log("Stopped right.wav");
364     }
365     // --------------------------------------------
366 
367     // Test for movement with keyboard
368     // ============================================
369     if (Keyboard.isKeyDown(Keyboard.KEY_LEFT)) {
370       listenerPosition.put(0, listenerPosition.get(0) - 0.1f);
371       alListener(AL_POSITION, listenerPosition);
372     }
373 
374     if (Keyboard.isKeyDown(Keyboard.KEY_RIGHT)) {
375       listenerPosition.put(0, listenerPosition.get(0) + 0.1f);
376       alListener(AL_POSITION, listenerPosition);
377     }
378 
379     if (Keyboard.isKeyDown(Keyboard.KEY_UP)) {
380       if (shift) {
381         listenerPosition.put(1, listenerPosition.get(1) + 0.1f);
382       } else {
383         listenerPosition.put(2, listenerPosition.get(2) - 0.1f);
384       }
385       alListener(AL_POSITION, listenerPosition);
386     }
387 
388     if (Keyboard.isKeyDown(Keyboard.KEY_DOWN)) {
389       if (shift) {
390         listenerPosition.put(1, listenerPosition.get(1) - 0.1f);
391       } else {
392         listenerPosition.put(2, listenerPosition.get(2) + 0.1f);
393       }
394       alListener(AL_POSITION, listenerPosition);
395     }
396     // --------------------------------------------
397 
398     // Test for movement with Mouse
399     // ============================================
400     listenerPosition.put(0, listenerPosition.get(0) + (0.01f * Mouse.getDX()));
401     listenerPosition.put(1, listenerPosition.get(1) + (0.01f * Mouse.getDY()));
402     if (Mouse.isButtonDown(0)) {
403       listenerPosition.put(2, listenerPosition.get(2) - 0.1f);
404     }
405     if (Mouse.isButtonDown(1)) {
406       listenerPosition.put(2, listenerPosition.get(2) + 0.1f);
407     }
408 
409     alListener(AL_POSITION, listenerPosition);
410 
411     // empty mouse buffer
412     while(Mouse.next());
413   }
414 
415   /**
416    * Render the scene
417    */
render()418   private void render() {
419     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
420     glPushMatrix();
421     {
422       glRotatef(20.0f, 1.0f, 1.0f, 0.0f);
423 
424       // left
425       glPushMatrix();
426       {
427         glTranslatef(leftPosition.get(0), leftPosition.get(1), leftPosition.get(2));
428         glColor3f(1.0f, 0.0f, 0.0f);
429         glut.glutWireCube(0.5f);
430       }
431       glPopMatrix();
432 
433       // center
434       glPushMatrix();
435       {
436         glTranslatef(centerPosition.get(0), centerPosition.get(1), centerPosition.get(2));
437         glColor3f(0.0f, 0.0f, 1.0f);
438         glut.glutWireCube(0.5f);
439       }
440       glPopMatrix();
441 
442       // right
443       glPushMatrix();
444       {
445         glTranslatef(rightPosition.get(0), rightPosition.get(1), rightPosition.get(2));
446         glColor3f(0.0f, 1.0f, 0.0f);
447         glut.glutWireCube(0.5f);
448       }
449       glPopMatrix();
450 
451       // listener
452       glPushMatrix();
453       {
454         glTranslatef(listenerPosition.get(0), listenerPosition.get(1), listenerPosition.get(2));
455         glColor3f(1.0f, 1.0f, 1.0f);
456         glut.glutSolidCube(0.5f);
457       }
458       glPopMatrix();
459     }
460     glPopMatrix();
461   }
462 
463   /**
464    * Shutdown of demonstration
465    */
shutdown()466   private void shutdown() {
467     LWJGLUtil.log("Shutting down OpenAL");
468     alSourceStop(soundSources);
469     alDeleteSources(soundSources);
470     alDeleteBuffers(soundBuffers);
471     AL.destroy();
472 
473     LWJGLUtil.log("Shutting down Window");
474     Display.destroy();
475   }
476 
477   /**
478    * main entry point
479    *
480    * @param args
481    *          String array containing arguments
482    */
main(String[] args)483   public static void main(String[] args) {
484     PositionTest positionTest = new PositionTest();
485     positionTest.execute(args);
486     System.exit(0);
487   }
488 
489   /**
490    * Minute implementation of GLUT: <br>COPYRIGHT:
491    *
492    * The OpenGL Utility Toolkit distribution for Win32 (Windows NT & Windows
493    * 95) contains source code modified from the original source code for GLUT
494    * version 3.3 which was developed by Mark J. Kilgard. The original source
495    * code for GLUT is Copyright 1997 by Mark J. Kilgard. GLUT for Win32 is
496    * Copyright 1997 by Nate Robins and is not in the public domain, but it is
497    * freely distributable without licensing fees. It is provided without
498    * guarantee or warrantee expressed or implied. It was ported with the
499    * permission of Mark J. Kilgard by Nate Robins.
500    *
501    * THIS SOURCE CODE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
502    * EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
503    * OR MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
504    */
505   class GLUT {
506 
507     float n[][] = new float[][] { { -1.0f, 0.0f, 0.0f }, {
508         0.0f, 1.0f, 0.0f }, {
509         1.0f, 0.0f, 0.0f }, {
510         0.0f, -1.0f, 0.0f }, {
511         0.0f, 0.0f, 1.0f }, {
512         0.0f, 0.0f, -1.0f }
513     };
514 
515     int faces[][] = new int[][] { { 0, 1, 2, 3 }, {
516         3, 2, 6, 7 }, {
517         7, 6, 5, 4 }, {
518         4, 5, 1, 0 }, {
519         5, 6, 2, 1 }, {
520         7, 4, 0, 3 }
521     };
522     float v[][] = new float[8][3];
523 
glutWireCube(float size)524     public void glutWireCube(float size) {
525       drawBox(size, GL_LINE_LOOP);
526     }
527 
glutSolidCube(float size)528     public void glutSolidCube(float size) {
529       drawBox(size, GL_QUADS);
530     }
531 
drawBox(float size, int type)532     private void drawBox(float size, int type) {
533 
534       v[0][0] = v[1][0] = v[2][0] = v[3][0] = -size / 2;
535       v[4][0] = v[5][0] = v[6][0] = v[7][0] = size / 2;
536       v[0][1] = v[1][1] = v[4][1] = v[5][1] = -size / 2;
537       v[2][1] = v[3][1] = v[6][1] = v[7][1] = size / 2;
538       v[0][2] = v[3][2] = v[4][2] = v[7][2] = -size / 2;
539       v[1][2] = v[2][2] = v[5][2] = v[6][2] = size / 2;
540 
541       for (int i = 5; i >= 0; i--) {
542         glBegin(type);
543         glNormal3f(n[i][0], n[i][1], n[i][2]);
544         glVertex3f(v[faces[i][0]][0], v[faces[i][0]][1], v[faces[i][0]][2]);
545         glVertex3f(v[faces[i][1]][0], v[faces[i][1]][1], v[faces[i][1]][2]);
546         glVertex3f(v[faces[i][2]][0], v[faces[i][2]][1], v[faces[i][2]][2]);
547         glVertex3f(v[faces[i][3]][0], v[faces[i][3]][1], v[faces[i][3]][2]);
548         glEnd();
549       }
550 
551     }
552   }
553 }
554