Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
lDEVinux
GitHub Repository: lDEVinux/eaglercraft
Path: blob/main/src/lwjgl/java/paulscode/sound/libraries/LibraryLWJGLOpenAL.java
8650 views
1
package paulscode.sound.libraries;
2
3
import java.nio.ByteBuffer;
4
import java.nio.IntBuffer;
5
import java.nio.FloatBuffer;
6
import java.net.URL;
7
import java.util.HashMap;
8
import java.util.Iterator;
9
import java.util.Set;
10
import javax.sound.sampled.AudioFormat;
11
12
// From the lwjgl library, http://www.lwjgl.org
13
import org.lwjgl.BufferUtils;
14
import org.lwjgl.LWJGLException;
15
import org.lwjgl.openal.AL;
16
import org.lwjgl.openal.AL10;
17
18
import paulscode.sound.Channel;
19
import paulscode.sound.FilenameURL;
20
import paulscode.sound.ICodec;
21
import paulscode.sound.Library;
22
import paulscode.sound.ListenerData;
23
import paulscode.sound.SoundBuffer;
24
import paulscode.sound.SoundSystemConfig;
25
import paulscode.sound.SoundSystemException;
26
import paulscode.sound.Source;
27
28
/**
29
* The LibraryLWJGLOpenAL class interfaces the lwjgl binding of OpenAL.
30
*<b><br><br>
31
* This software is based on or using the LWJGL Lightweight Java Gaming
32
* Library available from
33
* http://www.lwjgl.org/.
34
*</b><br><br>
35
* LWJGL License:
36
*<br><i>
37
* Copyright (c) 2002-2008 Lightweight Java Game Library Project
38
* All rights reserved.
39
*<br>
40
* Redistribution and use in source and binary forms, with or without
41
* modification, are permitted provided that the following conditions are
42
* met:
43
* <br>
44
* * Redistributions of source code must retain the above copyright
45
* notice, this list of conditions and the following disclaimer.
46
*<br>
47
* * Redistributions in binary form must reproduce the above copyright
48
* notice, this list of conditions and the following disclaimer in the
49
* documentation and/or other materials provided with the distribution.
50
*<br>
51
* * Neither the name of 'Light Weight Java Game Library' nor the names of
52
* its contributors may be used to endorse or promote products derived
53
* from this software without specific prior written permission.
54
* <br>
55
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
56
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
57
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
58
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
59
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
60
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
61
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
62
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
63
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
64
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
65
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
66
* <br><br><br></i>
67
*<b><i> SoundSystem LibraryLWJGLOpenAL License:</b></i><br><b><br>
68
*<b>
69
* You are free to use this library for any purpose, commercial or otherwise.
70
* You may modify this library or source code, and distribute it any way you
71
* like, provided the following conditions are met:
72
*<br>
73
* 1) You must abide by the conditions of the aforementioned LWJGL License.
74
*<br>
75
* 2) You may not falsely claim to be the author of this library or any
76
* unmodified portion of it.
77
*<br>
78
* 3) You may not copyright this library or a modified version of it and then
79
* sue me for copyright infringement.
80
*<br>
81
* 4) If you modify the source code, you must clearly document the changes
82
* made before redistributing the modified source code, so other users know
83
* it is not the original code.
84
*<br>
85
* 5) You are not required to give me credit for this library in any derived
86
* work, but if you do, you must also mention my website:
87
* http://www.paulscode.com
88
*<br>
89
* 6) I the author will not be responsible for any damages (physical,
90
* financial, or otherwise) caused by the use if this library or any part
91
* of it.
92
*<br>
93
* 7) I the author do not guarantee, warrant, or make any representations,
94
* either expressed or implied, regarding the use of this library or any
95
* part of it.
96
* <br><br>
97
* Author: Paul Lamb
98
* <br>
99
* http://www.paulscode.com
100
* </b>
101
*/
102
public class LibraryLWJGLOpenAL extends Library
103
{
104
/**
105
* Used to return a current value from one of the synchronized
106
* boolean-interface methods.
107
*/
108
private static final boolean GET = false;
109
/**
110
* Used to set the value in one of the synchronized boolean-interface methods.
111
*/
112
private static final boolean SET = true;
113
/**
114
* Used when a parameter for one of the synchronized boolean-interface methods
115
* is not aplicable.
116
*/
117
private static final boolean XXX = false;
118
119
/**
120
* Position of the listener in 3D space.
121
*/
122
private FloatBuffer listenerPositionAL = null;
123
/**
124
* Information about the listener's orientation.
125
*/
126
private FloatBuffer listenerOrientation = null;
127
/**
128
* Velocity of the listener.
129
*/
130
private FloatBuffer listenerVelocity = null;
131
/**
132
* Map containing OpenAL identifiers for sound buffers.
133
*/
134
private HashMap<String, IntBuffer> ALBufferMap = null;
135
136
/**
137
* Whether or not the AL_PITCH control is supported.
138
*/
139
private static boolean alPitchSupported = true;
140
141
/**
142
* Constructor: Instantiates the source map, buffer map and listener
143
* information. Also sets the library type to
144
* SoundSystemConfig.LIBRARY_OPENAL
145
*/
146
public LibraryLWJGLOpenAL() throws SoundSystemException
147
{
148
super();
149
ALBufferMap = new HashMap<String, IntBuffer>();
150
reverseByteOrder = true;
151
}
152
153
/**
154
* Initializes OpenAL, creates the listener, and grabs up audio channels.
155
*/
156
@Override
157
public void init() throws SoundSystemException
158
{
159
boolean errors = false; // set to 'true' if error(s) occur:
160
161
try
162
{
163
// Try and create the sound system:
164
AL.create();
165
errors = checkALError();
166
}
167
catch( LWJGLException e )
168
{
169
// There was an exception
170
errorMessage( "Unable to initialize OpenAL. Probable cause: " +
171
"OpenAL not supported." );
172
printStackTrace( e );
173
throw new LibraryLWJGLOpenAL.Exception( e.getMessage(),
174
LibraryLWJGLOpenAL.Exception.CREATE );
175
}
176
177
// Let user know if the library loaded properly
178
if( errors )
179
importantMessage( "OpenAL did not initialize properly!" );
180
else
181
message( "OpenAL initialized." );
182
183
// Listener is at the origin, facing along the z axis, no velocity:
184
listenerPositionAL = BufferUtils.createFloatBuffer( 3 ).put(
185
new float[] { listener.position.x,
186
listener.position.y,
187
listener.position.z } );
188
listenerOrientation = BufferUtils.createFloatBuffer( 6 ).put (
189
new float[] { listener.lookAt.x, listener.lookAt.y,
190
listener.lookAt.z, listener.up.x, listener.up.y,
191
listener.up.z } );
192
listenerVelocity = BufferUtils.createFloatBuffer( 3 ).put (
193
new float[] { 0.0f, 0.0f, 0.0f } );
194
195
// Flip the buffers, so they can be used:
196
listenerPositionAL.flip();
197
listenerOrientation.flip();
198
listenerVelocity.flip();
199
200
// Pass the buffers to the sound system, and check for potential errors:
201
AL10.alListener( AL10.AL_POSITION, listenerPositionAL );
202
errors = checkALError() || errors;
203
AL10.alListener( AL10.AL_ORIENTATION, listenerOrientation );
204
errors = checkALError() || errors;
205
AL10.alListener( AL10.AL_VELOCITY, listenerVelocity );
206
errors = checkALError() || errors;
207
208
AL10.alDopplerFactor( SoundSystemConfig.getDopplerFactor() );
209
errors = checkALError() || errors;
210
211
AL10.alDopplerVelocity( SoundSystemConfig.getDopplerVelocity() );
212
errors = checkALError() || errors;
213
214
// Let user know what caused the above error messages:
215
if( errors )
216
{
217
importantMessage( "OpenAL did not initialize properly!" );
218
throw new LibraryLWJGLOpenAL.Exception( "Problem encountered " +
219
"while loading OpenAL or " +
220
"creating the listener. " +
221
"Probable cause: OpenAL not " +
222
"supported",
223
LibraryLWJGLOpenAL.Exception.CREATE );
224
}
225
226
super.init();
227
228
// Check if we can use the AL_PITCH control:
229
ChannelLWJGLOpenAL channel = (ChannelLWJGLOpenAL)
230
normalChannels.get( 1 );
231
try
232
{
233
AL10.alSourcef( channel.ALSource.get( 0 ),
234
AL10.AL_PITCH, 1.0f );
235
if( checkALError() )
236
{
237
alPitchSupported( SET, false );
238
throw new LibraryLWJGLOpenAL.Exception( "OpenAL: AL_PITCH not " +
239
"supported.", LibraryLWJGLOpenAL.Exception.NO_AL_PITCH );
240
}
241
else
242
{
243
alPitchSupported( SET, true );
244
}
245
}
246
catch( java.lang.Exception e )
247
{
248
alPitchSupported( SET, false );
249
throw new LibraryLWJGLOpenAL.Exception( "OpenAL: AL_PITCH not " +
250
"supported.", LibraryLWJGLOpenAL.Exception.NO_AL_PITCH );
251
}
252
}
253
254
/**
255
* Checks if the OpenAL library type is compatible.
256
* @return True or false.
257
*/
258
public static boolean libraryCompatible()
259
{
260
if( AL.isCreated() )
261
return true;
262
263
try
264
{
265
AL.create();
266
}
267
catch( java.lang.Exception e )
268
{
269
return false;
270
}
271
272
try
273
{
274
AL.destroy();
275
}
276
catch( java.lang.Exception e )
277
{}
278
279
return true;
280
}
281
282
/**
283
* Creates a new channel of the specified type (normal or streaming). Possible
284
* values for channel type can be found in the
285
* {@link paulscode.sound.SoundSystemConfig SoundSystemConfig} class.
286
* @param type Type of channel.
287
*/
288
@Override
289
protected Channel createChannel( int type )
290
{
291
ChannelLWJGLOpenAL channel;
292
IntBuffer ALSource;
293
294
ALSource = BufferUtils.createIntBuffer( 1 );
295
try
296
{
297
AL10.alGenSources( ALSource );
298
}
299
catch( java.lang.Exception e )
300
{
301
AL10.alGetError();
302
return null; // no more voices left
303
}
304
305
if( AL10.alGetError() != AL10.AL_NO_ERROR )
306
return null;
307
308
channel = new ChannelLWJGLOpenAL( type, ALSource );
309
return channel;
310
}
311
312
/**
313
* Stops all sources, shuts down OpenAL, and removes references to all
314
* instantiated objects.
315
*/
316
@Override
317
public void cleanup()
318
{
319
super.cleanup();
320
321
Set<String> keys = bufferMap.keySet();
322
Iterator<String> iter = keys.iterator();
323
String filename;
324
IntBuffer buffer;
325
326
// loop through and clear all sound buffers:
327
while( iter.hasNext() )
328
{
329
filename = iter.next();
330
buffer = ALBufferMap.get( filename );
331
if( buffer != null )
332
{
333
AL10.alDeleteBuffers( buffer );
334
checkALError();
335
buffer.clear();
336
}
337
}
338
339
bufferMap.clear();
340
AL.destroy();
341
342
bufferMap = null;
343
listenerPositionAL = null;
344
listenerOrientation = null;
345
listenerVelocity = null;
346
}
347
348
/**
349
* Pre-loads a sound into memory.
350
* @param filenameURL Filename/URL of the sound file to load.
351
* @return True if the sound loaded properly.
352
*/
353
@Override
354
public boolean loadSound( FilenameURL filenameURL )
355
{
356
// Make sure the buffer map exists:
357
if( bufferMap == null )
358
{
359
bufferMap = new HashMap<String, SoundBuffer>();
360
importantMessage( "Buffer Map was null in method 'loadSound'" );
361
}
362
// Make sure the OpenAL buffer map exists:
363
if( ALBufferMap == null )
364
{
365
ALBufferMap = new HashMap<String, IntBuffer>();
366
importantMessage( "Open AL Buffer Map was null in method" +
367
"'loadSound'" );
368
}
369
370
// make sure they gave us a filename:
371
if( errorCheck( filenameURL == null,
372
"Filename/URL not specified in method 'loadSound'" ) )
373
return false;
374
375
// check if it is already loaded:
376
if( bufferMap.get( filenameURL.getFilename() ) != null )
377
return true;
378
379
ICodec codec = SoundSystemConfig.getCodec( filenameURL.getFilename() );
380
if( errorCheck( codec == null, "No codec found for file '" +
381
filenameURL.getFilename() +
382
"' in method 'loadSound'" ) )
383
return false;
384
codec.reverseByteOrder( true );
385
386
URL url = filenameURL.getURL();
387
if( errorCheck( url == null, "Unable to open file '" +
388
filenameURL.getFilename() +
389
"' in method 'loadSound'" ) )
390
return false;
391
392
codec.initialize( url );
393
SoundBuffer buffer = codec.readAll();
394
codec.cleanup();
395
codec = null;
396
if( errorCheck( buffer == null,
397
"Sound buffer null in method 'loadSound'" ) )
398
return false;
399
400
bufferMap.put( filenameURL.getFilename(), buffer );
401
402
AudioFormat audioFormat = buffer.audioFormat;
403
int soundFormat = 0;
404
if( audioFormat.getChannels() == 1 )
405
{
406
if( audioFormat.getSampleSizeInBits() == 8 )
407
{
408
soundFormat = AL10.AL_FORMAT_MONO8;
409
}
410
else if( audioFormat.getSampleSizeInBits() == 16 )
411
{
412
soundFormat = AL10.AL_FORMAT_MONO16;
413
}
414
else
415
{
416
errorMessage( "Illegal sample size in method 'loadSound'" );
417
return false;
418
}
419
}
420
else if( audioFormat.getChannels() == 2 )
421
{
422
if( audioFormat.getSampleSizeInBits() == 8 )
423
{
424
soundFormat = AL10.AL_FORMAT_STEREO8;
425
}
426
else if( audioFormat.getSampleSizeInBits() == 16 )
427
{
428
soundFormat = AL10.AL_FORMAT_STEREO16;
429
}
430
else
431
{
432
errorMessage( "Illegal sample size in method 'loadSound'" );
433
return false;
434
}
435
}
436
else
437
{
438
errorMessage( "File neither mono nor stereo in method " +
439
"'loadSound'" );
440
return false;
441
}
442
443
IntBuffer intBuffer = BufferUtils.createIntBuffer( 1 );
444
AL10.alGenBuffers( intBuffer );
445
if( errorCheck( AL10.alGetError() != AL10.AL_NO_ERROR,
446
"alGenBuffers error when loading " +
447
filenameURL.getFilename() ) )
448
return false;
449
450
// AL10.alBufferData( intBuffer.get( 0 ), soundFormat,
451
// ByteBuffer.wrap( buffer.audioData ),
452
// (int) audioFormat.getSampleRate() );
453
AL10.alBufferData( intBuffer.get( 0 ), soundFormat,
454
(ByteBuffer) BufferUtils.createByteBuffer(
455
buffer.audioData.length ).put(
456
buffer.audioData ).flip(),
457
(int) audioFormat.getSampleRate() );
458
459
if( errorCheck( AL10.alGetError() != AL10.AL_NO_ERROR,
460
"alBufferData error when loading " +
461
filenameURL.getFilename() ) )
462
463
464
if( errorCheck( intBuffer == null,
465
"Sound buffer was not created for " +
466
filenameURL.getFilename() ) )
467
return false;
468
469
ALBufferMap.put( filenameURL.getFilename(), intBuffer );
470
471
return true;
472
}
473
474
/**
475
* Saves the specified sample data, under the specified identifier. This
476
* identifier can be later used in place of 'filename' parameters to reference
477
* the sample data.
478
* @param buffer the sample data and audio format to save.
479
* @param identifier What to call the sample.
480
* @return True if there weren't any problems.
481
*/
482
@Override
483
public boolean loadSound( SoundBuffer buffer, String identifier )
484
{
485
// Make sure the buffer map exists:
486
if( bufferMap == null )
487
{
488
bufferMap = new HashMap<String, SoundBuffer>();
489
importantMessage( "Buffer Map was null in method 'loadSound'" );
490
}
491
// Make sure the OpenAL buffer map exists:
492
if( ALBufferMap == null )
493
{
494
ALBufferMap = new HashMap<String, IntBuffer>();
495
importantMessage( "Open AL Buffer Map was null in method" +
496
"'loadSound'" );
497
}
498
499
// make sure they gave us an identifier:
500
if( errorCheck( identifier == null,
501
"Identifier not specified in method 'loadSound'" ) )
502
return false;
503
504
// check if it is already loaded:
505
if( bufferMap.get( identifier ) != null )
506
return true;
507
508
if( errorCheck( buffer == null,
509
"Sound buffer null in method 'loadSound'" ) )
510
return false;
511
512
bufferMap.put( identifier, buffer );
513
514
AudioFormat audioFormat = buffer.audioFormat;
515
int soundFormat = 0;
516
if( audioFormat.getChannels() == 1 )
517
{
518
if( audioFormat.getSampleSizeInBits() == 8 )
519
{
520
soundFormat = AL10.AL_FORMAT_MONO8;
521
}
522
else if( audioFormat.getSampleSizeInBits() == 16 )
523
{
524
soundFormat = AL10.AL_FORMAT_MONO16;
525
}
526
else
527
{
528
errorMessage( "Illegal sample size in method 'loadSound'" );
529
return false;
530
}
531
}
532
else if( audioFormat.getChannels() == 2 )
533
{
534
if( audioFormat.getSampleSizeInBits() == 8 )
535
{
536
soundFormat = AL10.AL_FORMAT_STEREO8;
537
}
538
else if( audioFormat.getSampleSizeInBits() == 16 )
539
{
540
soundFormat = AL10.AL_FORMAT_STEREO16;
541
}
542
else
543
{
544
errorMessage( "Illegal sample size in method 'loadSound'" );
545
return false;
546
}
547
}
548
else
549
{
550
errorMessage( "File neither mono nor stereo in method " +
551
"'loadSound'" );
552
return false;
553
}
554
555
IntBuffer intBuffer = BufferUtils.createIntBuffer( 1 );
556
AL10.alGenBuffers( intBuffer );
557
if( errorCheck( AL10.alGetError() != AL10.AL_NO_ERROR,
558
"alGenBuffers error when saving " +
559
identifier ) )
560
return false;
561
562
// AL10.alBufferData( intBuffer.get( 0 ), soundFormat,
563
// ByteBuffer.wrap( buffer.audioData ),
564
// (int) audioFormat.getSampleRate() );
565
AL10.alBufferData( intBuffer.get( 0 ), soundFormat,
566
(ByteBuffer) BufferUtils.createByteBuffer(
567
buffer.audioData.length ).put(
568
buffer.audioData ).flip(),
569
(int) audioFormat.getSampleRate() );
570
571
if( errorCheck( AL10.alGetError() != AL10.AL_NO_ERROR,
572
"alBufferData error when saving " +
573
identifier ) )
574
575
576
if( errorCheck( intBuffer == null,
577
"Sound buffer was not created for " +
578
identifier ) )
579
return false;
580
581
ALBufferMap.put( identifier, intBuffer );
582
583
return true;
584
}
585
586
/**
587
* Removes a pre-loaded sound from memory. This is a good method to use for
588
* freeing up memory after a large sound file is no longer needed. NOTE: the
589
* source will remain in memory after this method has been called, for as long
590
* as the sound is attached to an existing source.
591
* @param filename Filename/identifier of the sound file to unload.
592
*/
593
@Override
594
public void unloadSound( String filename )
595
{
596
ALBufferMap.remove( filename );
597
super.unloadSound( filename );
598
}
599
600
/**
601
* Sets the overall volume to the specified value, affecting all sources.
602
* @param value New volume, float value ( 0.0f - 1.0f ).
603
*/
604
@Override
605
public void setMasterVolume( float value )
606
{
607
super.setMasterVolume( value );
608
609
AL10.alListenerf( AL10.AL_GAIN, value );
610
checkALError();
611
}
612
613
/**
614
* Creates a new source and places it into the source map.
615
* @param priority Setting this to true will prevent other sounds from overriding this one.
616
* @param toStream Setting this to true will load the sound in pieces rather than all at once.
617
* @param toLoop Should this source loop, or play only once.
618
* @param sourcename A unique identifier for this source. Two sources may not use the same sourcename.
619
* @param filenameURL Filename/URL of the sound file to play at this source.
620
* @param x X position for this source.
621
* @param y Y position for this source.
622
* @param z Z position for this source.
623
* @param attModel Attenuation model to use.
624
* @param distOrRoll Either the fading distance or rolloff factor, depending on the value of "attmodel".
625
*/
626
@Override
627
public void newSource( boolean priority, boolean toStream, boolean toLoop,
628
String sourcename, FilenameURL filenameURL, float x,
629
float y, float z, int attModel, float distOrRoll )
630
{
631
IntBuffer myBuffer = null;
632
if( !toStream )
633
{
634
// Grab the sound buffer for this file:
635
myBuffer = ALBufferMap.get( filenameURL.getFilename() );
636
637
// if not found, try loading it:
638
if( myBuffer == null )
639
{
640
if( !loadSound( filenameURL ) )
641
{
642
errorMessage( "Source '" + sourcename + "' was not created "
643
+ "because an error occurred while loading "
644
+ filenameURL.getFilename() );
645
return;
646
}
647
}
648
649
// try and grab the sound buffer again:
650
myBuffer = ALBufferMap.get( filenameURL.getFilename() );
651
// see if it was there this time:
652
if( myBuffer == null )
653
{
654
errorMessage( "Source '" + sourcename + "' was not created "
655
+ "because a sound buffer was not found for "
656
+ filenameURL.getFilename() );
657
return;
658
}
659
}
660
SoundBuffer buffer = null;
661
662
if( !toStream )
663
{
664
// Grab the audio data for this file:
665
buffer = bufferMap.get( filenameURL.getFilename() );
666
// if not found, try loading it:
667
if( buffer == null )
668
{
669
if( !loadSound( filenameURL ) )
670
{
671
errorMessage( "Source '" + sourcename + "' was not created "
672
+ "because an error occurred while loading "
673
+ filenameURL.getFilename() );
674
return;
675
}
676
}
677
// try and grab the sound buffer again:
678
buffer = bufferMap.get( filenameURL.getFilename() );
679
// see if it was there this time:
680
if( buffer == null )
681
{
682
errorMessage( "Source '" + sourcename + "' was not created "
683
+ "because audio data was not found for "
684
+ filenameURL.getFilename() );
685
return;
686
}
687
}
688
689
sourceMap.put( sourcename,
690
new SourceLWJGLOpenAL( listenerPositionAL, myBuffer,
691
priority, toStream, toLoop,
692
sourcename, filenameURL, buffer, x,
693
y, z, attModel, distOrRoll,
694
false ) );
695
}
696
697
/**
698
* Opens a direct line for streaming audio data.
699
* @param audioFormat Format that the data will be in.
700
* @param priority Setting this to true will prevent other sounds from overriding this one.
701
* @param sourcename A unique identifier for this source. Two sources may not use the same sourcename.
702
* @param x X position for this source.
703
* @param y Y position for this source.
704
* @param z Z position for this source.
705
* @param attModel Attenuation model to use.
706
* @param distOrRoll Either the fading distance or rolloff factor, depending on the value of "attmodel".
707
*/
708
@Override
709
public void rawDataStream( AudioFormat audioFormat, boolean priority,
710
String sourcename, float x, float y,
711
float z, int attModel, float distOrRoll )
712
{
713
sourceMap.put( sourcename,
714
new SourceLWJGLOpenAL( listenerPositionAL, audioFormat,
715
priority, sourcename, x, y, z,
716
attModel, distOrRoll ) );
717
}
718
719
/**
720
* Creates and immediately plays a new source.
721
* @param priority Setting this to true will prevent other sounds from overriding this one.
722
* @param toStream Setting this to true will load the sound in pieces rather than all at once.
723
* @param toLoop Should this source loop, or play only once.
724
* @param sourcename A unique identifier for this source. Two sources may not use the same sourcename.
725
* @param filenameURL Filename/URL of the sound file to play at this source.
726
* @param x X position for this source.
727
* @param y Y position for this source.
728
* @param z Z position for this source.
729
* @param attModel Attenuation model to use.
730
* @param distOrRoll Either the fading distance or rolloff factor, depending on the value of "attmodel".
731
* @param temporary Whether or not this source should be removed after it finishes playing.
732
*/
733
@Override
734
public void quickPlay( boolean priority, boolean toStream, boolean toLoop,
735
String sourcename, FilenameURL filenameURL, float x,
736
float y, float z, int attModel, float distOrRoll,
737
boolean temporary )
738
{
739
IntBuffer myBuffer = null;
740
if( !toStream )
741
{
742
// Grab the sound buffer for this file:
743
myBuffer = ALBufferMap.get( filenameURL.getFilename() );
744
// if not found, try loading it:
745
if( myBuffer == null )
746
loadSound( filenameURL );
747
// try and grab the sound buffer again:
748
myBuffer = ALBufferMap.get( filenameURL.getFilename() );
749
// see if it was there this time:
750
if( myBuffer == null )
751
{
752
errorMessage( "Sound buffer was not created for " +
753
filenameURL.getFilename() );
754
return;
755
}
756
}
757
758
SoundBuffer buffer = null;
759
760
if( !toStream )
761
{
762
// Grab the sound buffer for this file:
763
buffer = bufferMap.get( filenameURL.getFilename() );
764
// if not found, try loading it:
765
if( buffer == null )
766
{
767
if( !loadSound( filenameURL ) )
768
{
769
errorMessage( "Source '" + sourcename + "' was not created "
770
+ "because an error occurred while loading "
771
+ filenameURL.getFilename() );
772
return;
773
}
774
}
775
// try and grab the sound buffer again:
776
buffer = bufferMap.get( filenameURL.getFilename() );
777
// see if it was there this time:
778
if( buffer == null )
779
{
780
errorMessage( "Source '" + sourcename + "' was not created "
781
+ "because audio data was not found for "
782
+ filenameURL.getFilename() );
783
return;
784
}
785
}
786
SourceLWJGLOpenAL s = new SourceLWJGLOpenAL( listenerPositionAL,
787
myBuffer, priority,
788
toStream, toLoop,
789
sourcename, filenameURL,
790
buffer, x, y, z,
791
attModel, distOrRoll,
792
false );
793
794
sourceMap.put( sourcename, s );
795
play( s );
796
if( temporary )
797
s.setTemporary( true );
798
}
799
800
/**
801
* Creates sources based on the source map provided.
802
* @param srcMap Sources to copy.
803
*/
804
@Override
805
public void copySources( HashMap<String, Source> srcMap )
806
{
807
if( srcMap == null )
808
return;
809
Set<String> keys = srcMap.keySet();
810
Iterator<String> iter = keys.iterator();
811
String sourcename;
812
Source source;
813
814
// Make sure the buffer map exists:
815
if( bufferMap == null )
816
{
817
bufferMap = new HashMap<String, SoundBuffer>();
818
importantMessage( "Buffer Map was null in method 'copySources'" );
819
}
820
// Make sure the OpenAL buffer map exists:
821
if( ALBufferMap == null )
822
{
823
ALBufferMap = new HashMap<String, IntBuffer>();
824
importantMessage( "Open AL Buffer Map was null in method" +
825
"'copySources'" );
826
}
827
828
// remove any existing sources before starting:
829
sourceMap.clear();
830
831
SoundBuffer buffer;
832
// loop through and copy all the sources:
833
while( iter.hasNext() )
834
{
835
sourcename = iter.next();
836
source = srcMap.get( sourcename );
837
if( source != null )
838
{
839
buffer = null;
840
if( !source.toStream )
841
{
842
loadSound( source.filenameURL );
843
buffer = bufferMap.get( source.filenameURL.getFilename() );
844
}
845
if( source.toStream || buffer != null )
846
sourceMap.put( sourcename, new SourceLWJGLOpenAL(
847
listenerPositionAL,
848
ALBufferMap.get(
849
source.filenameURL.getFilename() ),
850
source, buffer ) );
851
}
852
}
853
}
854
855
/**
856
* Changes the listener's position.
857
* @param x Destination X coordinate.
858
* @param y Destination Y coordinate.
859
* @param z Destination Z coordinate.
860
*/
861
@Override
862
public void setListenerPosition( float x, float y, float z )
863
{
864
super.setListenerPosition( x, y, z );
865
866
listenerPositionAL.put( 0, x );
867
listenerPositionAL.put( 1, y );
868
listenerPositionAL.put( 2, z );
869
870
// Update OpenAL listener position:
871
AL10.alListener( AL10.AL_POSITION, listenerPositionAL );
872
// Check for errors:
873
checkALError();
874
}
875
876
/**
877
* Changes the listeners orientation to the specified 'angle' radians
878
* counterclockwise around the y-Axis.
879
* @param angle Radians.
880
*/
881
@Override
882
public void setListenerAngle( float angle )
883
{
884
super.setListenerAngle( angle );
885
886
listenerOrientation.put( 0, listener.lookAt.x );
887
listenerOrientation.put( 2, listener.lookAt.z );
888
889
// Update OpenAL listener orientation:
890
AL10.alListener( AL10.AL_ORIENTATION, listenerOrientation );
891
// Check for errors:
892
checkALError();
893
}
894
895
/**
896
* Changes the listeners orientation using the specified coordinates.
897
* @param lookX X element of the look-at direction.
898
* @param lookY Y element of the look-at direction.
899
* @param lookZ Z element of the look-at direction.
900
* @param upX X element of the up direction.
901
* @param upY Y element of the up direction.
902
* @param upZ Z element of the up direction.
903
*/
904
@Override
905
public void setListenerOrientation( float lookX, float lookY, float lookZ,
906
float upX, float upY, float upZ )
907
{
908
super.setListenerOrientation( lookX, lookY, lookZ, upX, upY, upZ );
909
listenerOrientation.put( 0, lookX );
910
listenerOrientation.put( 1, lookY );
911
listenerOrientation.put( 2, lookZ );
912
listenerOrientation.put( 3, upX );
913
listenerOrientation.put( 4, upY );
914
listenerOrientation.put( 5, upZ );
915
AL10.alListener( AL10.AL_ORIENTATION, listenerOrientation );
916
checkALError();
917
}
918
919
/**
920
* Changes the listeners position and orientation using the specified listener
921
* data.
922
* @param l Listener data to use.
923
*/
924
@Override
925
public void setListenerData( ListenerData l )
926
{
927
super.setListenerData( l );
928
929
listenerPositionAL.put( 0, l.position.x );
930
listenerPositionAL.put( 1, l.position.y );
931
listenerPositionAL.put( 2, l.position.z );
932
AL10.alListener( AL10.AL_POSITION, listenerPositionAL );
933
checkALError();
934
935
listenerOrientation.put( 0, l.lookAt.x );
936
listenerOrientation.put( 1, l.lookAt.y );
937
listenerOrientation.put( 2, l.lookAt.z );
938
listenerOrientation.put( 3, l.up.x );
939
listenerOrientation.put( 4, l.up.y );
940
listenerOrientation.put( 5, l.up.z );
941
AL10.alListener( AL10.AL_ORIENTATION, listenerOrientation );
942
checkALError();
943
944
listenerVelocity.put( 0, l.velocity.x );
945
listenerVelocity.put( 1, l.velocity.y );
946
listenerVelocity.put( 2, l.velocity.z );
947
AL10.alListener( AL10.AL_VELOCITY, listenerVelocity );
948
checkALError();
949
}
950
951
/**
952
* Sets the listener's velocity, for use in Doppler effect.
953
* @param x Velocity along world x-axis.
954
* @param y Velocity along world y-axis.
955
* @param z Velocity along world z-axis.
956
*/
957
@Override
958
public void setListenerVelocity( float x, float y, float z )
959
{
960
super.setListenerVelocity( x, y, z );
961
962
listenerVelocity.put( 0, listener.velocity.x );
963
listenerVelocity.put( 1, listener.velocity.y );
964
listenerVelocity.put( 2, listener.velocity.z );
965
AL10.alListener( AL10.AL_VELOCITY, listenerVelocity );
966
}
967
968
/**
969
* The Doppler parameters have changed.
970
*/
971
@Override
972
public void dopplerChanged()
973
{
974
super.dopplerChanged();
975
976
AL10.alDopplerFactor( SoundSystemConfig.getDopplerFactor() );
977
checkALError();
978
AL10.alDopplerVelocity( SoundSystemConfig.getDopplerVelocity() );
979
checkALError();
980
}
981
982
/**
983
* Checks for OpenAL errors, and prints a message if there is an error.
984
* @return True if there was an error, False if not.
985
*/
986
private boolean checkALError()
987
{
988
switch( AL10.alGetError() )
989
{
990
case AL10.AL_NO_ERROR:
991
return false;
992
case AL10.AL_INVALID_NAME:
993
errorMessage( "Invalid name parameter." );
994
return true;
995
case AL10.AL_INVALID_ENUM:
996
errorMessage( "Invalid parameter." );
997
return true;
998
case AL10.AL_INVALID_VALUE:
999
errorMessage( "Invalid enumerated parameter value." );
1000
return true;
1001
case AL10.AL_INVALID_OPERATION:
1002
errorMessage( "Illegal call." );
1003
return true;
1004
case AL10.AL_OUT_OF_MEMORY:
1005
errorMessage( "Unable to allocate memory." );
1006
return true;
1007
default:
1008
errorMessage( "An unrecognized error occurred." );
1009
return true;
1010
}
1011
}
1012
1013
/**
1014
* Whether or not the AL_PITCH control is supported.
1015
* @return True if AL_PITCH is supported.
1016
*/
1017
public static boolean alPitchSupported()
1018
{
1019
return alPitchSupported( GET, XXX );
1020
}
1021
/**
1022
* Sets or returns the value of boolean 'alPitchSupported'.
1023
* @param action Action to perform (GET or SET).
1024
* @param value New value if action is SET, otherwise XXX.
1025
* @return value of boolean 'alPitchSupported'.
1026
*/
1027
private static synchronized boolean alPitchSupported( boolean action,
1028
boolean value )
1029
{
1030
if( action == SET )
1031
alPitchSupported = value;
1032
return alPitchSupported;
1033
}
1034
1035
/**
1036
* Returns the short title of this library type.
1037
* @return A short title.
1038
*/
1039
public static String getTitle()
1040
{
1041
return "LWJGL OpenAL";
1042
}
1043
1044
/**
1045
* Returns a longer description of this library type.
1046
* @return A longer description.
1047
*/
1048
public static String getDescription()
1049
{
1050
return "The LWJGL binding of OpenAL. For more information, see " +
1051
"http://www.lwjgl.org";
1052
}
1053
1054
/**
1055
* Returns the name of the class.
1056
* @return "Library" + library title.
1057
*/
1058
@Override
1059
public String getClassName()
1060
{
1061
return "LibraryLWJGLOpenAL";
1062
}
1063
1064
/**
1065
* The LibraryLWJGLOpenAL.Exception class provides library-specific error
1066
* information.
1067
*/
1068
public static class Exception extends SoundSystemException
1069
{
1070
private static final long serialVersionUID = -7502452059037798035L;
1071
/**
1072
* Global identifier for an exception during AL.create(). Probably
1073
* means that OpenAL is not supported.
1074
*/
1075
public static final int CREATE = 101;
1076
/**
1077
* Global identifier for an invalid name parameter in OpenAL.
1078
*/
1079
public static final int INVALID_NAME = 102;
1080
/**
1081
* Global identifier for an invalid parameter in OpenAL.
1082
*/
1083
public static final int INVALID_ENUM = 103;
1084
/**
1085
* Global identifier for an invalid enumerated parameter value in OpenAL.
1086
*/
1087
public static final int INVALID_VALUE = 104;
1088
/**
1089
* Global identifier for an illegal call in OpenAL.
1090
*/
1091
public static final int INVALID_OPERATION = 105;
1092
/**
1093
* Global identifier for OpenAL out of memory.
1094
*/
1095
public static final int OUT_OF_MEMORY = 106;
1096
/**
1097
* Global identifier for an exception while creating the OpenAL Listener.
1098
*/
1099
public static final int LISTENER = 107;
1100
/**
1101
* Global identifier for OpenAL AL_PITCH not supported.
1102
*/
1103
public static final int NO_AL_PITCH = 108;
1104
1105
/**
1106
* Constructor: Generates a standard "unknown error" exception with the
1107
* specified message.
1108
* @param message A brief description of the problem that occurred.
1109
*/
1110
public Exception( String message )
1111
{
1112
super( message );
1113
}
1114
1115
/**
1116
* Constructor: Generates an exception of the specified type, with the
1117
* specified message.
1118
* @param message A brief description of the problem that occurred.
1119
* @param type Identifier indicating they type of error.
1120
*/
1121
public Exception( String message, int type )
1122
{
1123
super( message, type );
1124
}
1125
}
1126
}
1127
1128