Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
lDEVinux
GitHub Repository: lDEVinux/eaglercraft
Path: blob/main/src/lwjgl/java/paulscode/sound/libraries/ChannelLWJGLOpenAL.java
8650 views
1
package paulscode.sound.libraries;
2
3
import java.nio.ByteBuffer;
4
import java.nio.IntBuffer;
5
import java.util.LinkedList;
6
import javax.sound.sampled.AudioFormat;
7
8
// From the lwjgl library, http://www.lwjgl.org
9
import org.lwjgl.BufferUtils;
10
import org.lwjgl.openal.AL10;
11
import org.lwjgl.openal.AL11;
12
13
import paulscode.sound.Channel;
14
import paulscode.sound.SoundSystemConfig;
15
16
/**
17
* The ChannelLWJGLOpenAL class is used to reserve a sound-card voice using the
18
* lwjgl binding of OpenAL. Channels can be either normal or streaming
19
* channels.
20
*<b><br><br>
21
* This software is based on or using the LWJGL Lightweight Java Gaming
22
* Library available from
23
* http://www.lwjgl.org/.
24
*</b><br><br>
25
* LWJGL License:
26
*<br><i>
27
* Copyright (c) 2002-2008 Lightweight Java Game Library Project
28
* All rights reserved.
29
*<br>
30
* Redistribution and use in source and binary forms, with or without
31
* modification, are permitted provided that the following conditions are
32
* met:
33
* <br>
34
* * Redistributions of source code must retain the above copyright
35
* notice, this list of conditions and the following disclaimer.
36
*<br>
37
* * Redistributions in binary form must reproduce the above copyright
38
* notice, this list of conditions and the following disclaimer in the
39
* documentation and/or other materials provided with the distribution.
40
*<br>
41
* * Neither the name of 'Light Weight Java Game Library' nor the names of
42
* its contributors may be used to endorse or promote products derived
43
* from this software without specific prior written permission.
44
* <br>
45
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
46
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
47
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
48
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
49
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
50
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
51
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
52
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
53
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
54
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
55
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
56
* <br><br><br></i>
57
*<b><i> SoundSystem LibraryLWJGLOpenAL License:</b></i><br><b><br>
58
*<b>
59
* You are free to use this library for any purpose, commercial or otherwise.
60
* You may modify this library or source code, and distribute it any way you
61
* like, provided the following conditions are met:
62
*<br>
63
* 1) You must abide by the conditions of the aforementioned LWJGL License.
64
*<br>
65
* 2) You may not falsely claim to be the author of this library or any
66
* unmodified portion of it.
67
*<br>
68
* 3) You may not copyright this library or a modified version of it and then
69
* sue me for copyright infringement.
70
*<br>
71
* 4) If you modify the source code, you must clearly document the changes
72
* made before redistributing the modified source code, so other users know
73
* it is not the original code.
74
*<br>
75
* 5) You are not required to give me credit for this library in any derived
76
* work, but if you do, you must also mention my website:
77
* http://www.paulscode.com
78
*<br>
79
* 6) I the author will not be responsible for any damages (physical,
80
* financial, or otherwise) caused by the use if this library or any part
81
* of it.
82
*<br>
83
* 7) I the author do not guarantee, warrant, or make any representations,
84
* either expressed or implied, regarding the use of this library or any
85
* part of it.
86
* <br><br>
87
* Author: Paul Lamb
88
* <br>
89
* http://www.paulscode.com
90
* </b>
91
*/
92
public class ChannelLWJGLOpenAL extends Channel
93
{
94
/**
95
* OpenAL's IntBuffer identifier for this channel.
96
*/
97
public IntBuffer ALSource;
98
99
/**
100
* OpenAL data format to use when playing back the assigned source.
101
*/
102
public int ALformat; // OpenAL data format
103
104
/**
105
* Sample rate (speed) to use for play-back.
106
*/
107
public int sampleRate; // sample rate
108
109
/**
110
* Miliseconds of buffers previously played (streaming sources).
111
*/
112
public float millisPreviouslyPlayed = 0;
113
114
/**
115
* Constructor: takes channelType identifier and a handle to the OpenAL
116
* IntBuffer identifier to use for this channel. Possible values for channel
117
* type can be found in the
118
* {@link paulscode.sound.SoundSystemConfig SoundSystemConfig} class.
119
* @param type Type of channel (normal or streaming).
120
* @param src Handle to the OpenAL source identifier.
121
*/
122
public ChannelLWJGLOpenAL( int type, IntBuffer src )
123
{
124
super( type );
125
libraryType = LibraryLWJGLOpenAL.class;
126
ALSource = src;
127
}
128
129
/**
130
* Empties the streamBuffers list, stops and deletes the ALSource, shuts the
131
* channel down, and removes references to all instantiated objects.
132
*/
133
@Override
134
public void cleanup()
135
{
136
if( ALSource != null )
137
{
138
try
139
{
140
// Stop playing the source:
141
AL10.alSourceStop( ALSource );
142
AL10.alGetError();
143
}
144
catch( Exception e )
145
{}
146
try
147
{
148
// Delete the source:
149
AL10.alDeleteSources( ALSource );
150
AL10.alGetError();
151
}
152
catch( Exception e )
153
{}
154
ALSource.clear();
155
}
156
ALSource = null;
157
158
super.cleanup();
159
}
160
161
/**
162
* Attaches an OpenAL sound-buffer identifier for the sound data to be played
163
* back for a normal source.
164
* @param buf Intbuffer identifier for the sound data to play.
165
* @return False if an error occurred.
166
*/
167
public boolean attachBuffer( IntBuffer buf )
168
{
169
// A sound buffer can only be attached to a normal source:
170
if( errorCheck( channelType != SoundSystemConfig.TYPE_NORMAL,
171
"Sound buffers may only be attached to normal " +
172
"sources." ) )
173
return false;
174
175
// send the sound buffer to the channel:
176
AL10.alSourcei( ALSource.get( 0 ), AL10.AL_BUFFER,
177
buf.get(0) );
178
179
180
// save the format for later, for determining milliseconds played
181
if( attachedSource != null && attachedSource.soundBuffer != null &&
182
attachedSource.soundBuffer.audioFormat != null )
183
setAudioFormat( attachedSource.soundBuffer.audioFormat );
184
185
// Check for errors and return:
186
return checkALError();
187
}
188
/**
189
* Sets the channel up to receive the specified audio format.
190
* @param audioFormat Format to use when playing the stream data.
191
*/
192
@Override
193
public void setAudioFormat( AudioFormat audioFormat )
194
{
195
int soundFormat = 0;
196
if( audioFormat.getChannels() == 1 )
197
{
198
if( audioFormat.getSampleSizeInBits() == 8 )
199
{
200
soundFormat = AL10.AL_FORMAT_MONO8;
201
}
202
else if( audioFormat.getSampleSizeInBits() == 16 )
203
{
204
soundFormat = AL10.AL_FORMAT_MONO16;
205
}
206
else
207
{
208
errorMessage( "Illegal sample size in method " +
209
"'setAudioFormat'" );
210
return;
211
}
212
}
213
else if( audioFormat.getChannels() == 2 )
214
{
215
if( audioFormat.getSampleSizeInBits() == 8 )
216
{
217
soundFormat = AL10.AL_FORMAT_STEREO8;
218
}
219
else if( audioFormat.getSampleSizeInBits() == 16 )
220
{
221
soundFormat = AL10.AL_FORMAT_STEREO16;
222
}
223
else
224
{
225
errorMessage( "Illegal sample size in method " +
226
"'setAudioFormat'" );
227
return;
228
}
229
}
230
else
231
{
232
errorMessage( "Audio data neither mono nor stereo in " +
233
"method 'setAudioFormat'" );
234
return;
235
}
236
ALformat = soundFormat;
237
sampleRate = (int) audioFormat.getSampleRate();
238
}
239
/**
240
* Sets the channel up to receive the specified OpenAL audio format and sample
241
* rate.
242
* @param format Format to use.
243
* @param rate Sample rate (speed) to use.
244
*/
245
public void setFormat( int format, int rate )
246
{
247
ALformat = format;
248
sampleRate = rate;
249
}
250
251
/**
252
* Queues up the initial byte[] buffers of data to be streamed.
253
* @param bufferList List of the first buffers to be played for a streaming source.
254
* @return False if problem occurred or if end of stream was reached.
255
*/
256
@Override
257
public boolean preLoadBuffers( LinkedList<byte[]> bufferList )
258
{
259
// Stream buffers can only be queued for streaming sources:
260
if( errorCheck( channelType != SoundSystemConfig.TYPE_STREAMING,
261
"Buffers may only be queued for streaming sources." ) )
262
return false;
263
264
if( errorCheck( bufferList == null,
265
"Buffer List null in method 'preLoadBuffers'" ) )
266
return false;
267
268
IntBuffer streamBuffers;
269
270
// Remember if the channel was playing:
271
boolean playing = playing();
272
// stop the channel if it is playing:
273
if( playing )
274
{
275
AL10.alSourceStop( ALSource.get( 0 ) );
276
checkALError();
277
}
278
// Clear out any previously queued buffers:
279
int processed = AL10.alGetSourcei( ALSource.get( 0 ),
280
AL10.AL_BUFFERS_PROCESSED );
281
if( processed > 0 )
282
{
283
streamBuffers = BufferUtils.createIntBuffer( processed );
284
AL10.alGenBuffers( streamBuffers );
285
if( errorCheck( checkALError(),
286
"Error clearing stream buffers in method 'preLoadBuffers'" ) )
287
return false;
288
AL10.alSourceUnqueueBuffers( ALSource.get( 0 ), streamBuffers );
289
if( errorCheck( checkALError(),
290
"Error unqueuing stream buffers in method 'preLoadBuffers'" ) )
291
return false;
292
}
293
294
// restart the channel if it was previously playing:
295
if( playing )
296
{
297
AL10.alSourcePlay( ALSource.get( 0 ) );
298
checkALError();
299
}
300
301
streamBuffers = BufferUtils.createIntBuffer( bufferList.size() );
302
AL10.alGenBuffers( streamBuffers );
303
if( errorCheck( checkALError(),
304
"Error generating stream buffers in method 'preLoadBuffers'" ) )
305
return false;
306
307
ByteBuffer byteBuffer = null;
308
for( int i = 0; i < bufferList.size(); i++ )
309
{
310
//byteBuffer = ByteBuffer.wrap( bufferList.get(i), 0,
311
// bufferList.get(i).length );
312
byteBuffer = (ByteBuffer) BufferUtils.createByteBuffer(
313
bufferList.get(i).length ).put( bufferList.get( i ) ).flip();
314
315
try
316
{
317
AL10.alBufferData( streamBuffers.get(i), ALformat, byteBuffer,
318
sampleRate );
319
}
320
catch( Exception e )
321
{
322
errorMessage( "Error creating buffers in method " +
323
"'preLoadBuffers'" );
324
printStackTrace( e );
325
return false;
326
}
327
if( errorCheck( checkALError(),
328
"Error creating buffers in method 'preLoadBuffers'" ) )
329
return false;
330
331
}
332
333
try
334
{
335
AL10.alSourceQueueBuffers( ALSource.get( 0 ), streamBuffers );
336
}
337
catch( Exception e )
338
{
339
errorMessage( "Error queuing buffers in method 'preLoadBuffers'" );
340
printStackTrace( e );
341
return false;
342
}
343
if( errorCheck( checkALError(),
344
"Error queuing buffers in method 'preLoadBuffers'" ) )
345
return false;
346
347
AL10.alSourcePlay( ALSource.get( 0 ) );
348
if( errorCheck( checkALError(),
349
"Error playing source in method 'preLoadBuffers'" ) )
350
return false;
351
352
// Success:
353
return true;
354
}
355
356
/**
357
* Queues up a byte[] buffer of data to be streamed.
358
* @param buffer The next buffer to be played for a streaming source.
359
* @return False if an error occurred or if the channel is shutting down.
360
*/
361
@Override
362
public boolean queueBuffer( byte[] buffer )
363
{
364
// Stream buffers can only be queued for streaming sources:
365
if( errorCheck( channelType != SoundSystemConfig.TYPE_STREAMING,
366
"Buffers may only be queued for streaming sources." ) )
367
return false;
368
369
//ByteBuffer byteBuffer = ByteBuffer.wrap( buffer, 0, buffer.length );
370
ByteBuffer byteBuffer = (ByteBuffer) BufferUtils.createByteBuffer(
371
buffer.length ).put( buffer ).flip();
372
373
IntBuffer intBuffer = BufferUtils.createIntBuffer( 1 );
374
375
AL10.alSourceUnqueueBuffers( ALSource.get( 0 ), intBuffer );
376
if( checkALError() )
377
return false;
378
379
if( AL10.alIsBuffer( intBuffer.get( 0 ) ) )
380
millisPreviouslyPlayed += millisInBuffer( intBuffer.get( 0 ) );
381
checkALError();
382
383
AL10.alBufferData( intBuffer.get(0), ALformat, byteBuffer, sampleRate );
384
if( checkALError() )
385
return false;
386
387
AL10.alSourceQueueBuffers( ALSource.get( 0 ), intBuffer );
388
if( checkALError() )
389
return false;
390
391
return true;
392
}
393
394
/**
395
* Feeds raw data to the stream.
396
* @param buffer Buffer containing raw audio data to stream.
397
* @return Number of prior buffers that have been processed., or -1 if error.
398
*/
399
@Override
400
public int feedRawAudioData( byte[] buffer )
401
{
402
// Stream buffers can only be queued for streaming sources:
403
if( errorCheck( channelType != SoundSystemConfig.TYPE_STREAMING,
404
"Raw audio data can only be fed to streaming sources." ) )
405
return -1;
406
407
//ByteBuffer byteBuffer = ByteBuffer.wrap( buffer, 0, buffer.length );
408
ByteBuffer byteBuffer = (ByteBuffer) BufferUtils.createByteBuffer(
409
buffer.length ).put( buffer ).flip();
410
411
IntBuffer intBuffer;
412
413
// Clear out any previously queued buffers:
414
int processed = AL10.alGetSourcei( ALSource.get( 0 ),
415
AL10.AL_BUFFERS_PROCESSED );
416
if( processed > 0 )
417
{
418
intBuffer = BufferUtils.createIntBuffer( processed );
419
AL10.alGenBuffers( intBuffer );
420
if( errorCheck( checkALError(),
421
"Error clearing stream buffers in method 'feedRawAudioData'" ) )
422
return -1;
423
AL10.alSourceUnqueueBuffers( ALSource.get( 0 ), intBuffer );
424
if( errorCheck( checkALError(),
425
"Error unqueuing stream buffers in method 'feedRawAudioData'" ) )
426
return -1;
427
int i;
428
intBuffer.rewind();
429
while( intBuffer.hasRemaining() )
430
{
431
i = intBuffer.get();
432
if( AL10.alIsBuffer( i ) )
433
{
434
millisPreviouslyPlayed += millisInBuffer( i );
435
}
436
checkALError();
437
}
438
AL10.alDeleteBuffers( intBuffer );
439
checkALError();
440
}
441
intBuffer = BufferUtils.createIntBuffer( 1 );
442
AL10.alGenBuffers( intBuffer );
443
if( errorCheck( checkALError(),
444
"Error generating stream buffers in method 'preLoadBuffers'" ) )
445
return -1;
446
447
AL10.alBufferData( intBuffer.get(0), ALformat, byteBuffer, sampleRate );
448
if( checkALError() )
449
return -1;
450
451
AL10.alSourceQueueBuffers( ALSource.get( 0 ), intBuffer );
452
if( checkALError() )
453
return -1;
454
455
if( attachedSource != null && attachedSource.channel == this &&
456
attachedSource.active() )
457
{
458
// restart the channel if it was previously playing:
459
if( !playing() )
460
{
461
AL10.alSourcePlay( ALSource.get( 0 ) );
462
checkALError();
463
}
464
}
465
466
return processed;
467
}
468
469
/**
470
* Returns the number of milliseconds of audio contained in specified buffer.
471
* @return milliseconds, or 0 if unable to calculate.
472
*/
473
public float millisInBuffer( int alBufferi )
474
{
475
return( ( (float) AL10.alGetBufferi( alBufferi, AL10.AL_SIZE ) /
476
(float) AL10.alGetBufferi( alBufferi, AL10.AL_CHANNELS ) /
477
( (float) AL10.alGetBufferi( alBufferi, AL10.AL_BITS ) / 8.0f ) /
478
(float) sampleRate ) * 1000 );
479
}
480
481
/**
482
* Calculates the number of milliseconds since the channel began playing.
483
* @return Milliseconds, or -1 if unable to calculate.
484
*/
485
@Override
486
public float millisecondsPlayed()
487
{
488
// get number of samples played in current buffer
489
float offset = (float)AL10.alGetSourcei( ALSource.get( 0 ),
490
AL11.AL_BYTE_OFFSET );
491
492
float bytesPerFrame = 1f;
493
switch( ALformat )
494
{
495
case AL10.AL_FORMAT_MONO8 :
496
bytesPerFrame = 1f;
497
break;
498
case AL10.AL_FORMAT_MONO16 :
499
bytesPerFrame = 2f;
500
break;
501
case AL10.AL_FORMAT_STEREO8 :
502
bytesPerFrame = 2f;
503
break;
504
case AL10.AL_FORMAT_STEREO16 :
505
bytesPerFrame = 4f;
506
break;
507
default :
508
break;
509
}
510
511
offset = ( ( (float) offset / bytesPerFrame ) / (float) sampleRate )
512
* 1000;
513
514
// add the milliseconds from stream-buffers that played previously
515
if( channelType == SoundSystemConfig.TYPE_STREAMING )
516
offset += millisPreviouslyPlayed;
517
518
// Return millis played:
519
return( offset );
520
}
521
522
/**
523
* Returns the number of queued byte[] buffers that have finished playing.
524
* @return Number of buffers processed.
525
*/
526
@Override
527
public int buffersProcessed()
528
{
529
// Only streaming sources process buffers:
530
if( channelType != SoundSystemConfig.TYPE_STREAMING )
531
return 0;
532
533
// determine how many have been processed:
534
int processed = AL10.alGetSourcei( ALSource.get( 0 ),
535
AL10.AL_BUFFERS_PROCESSED );
536
537
// Check for errors:
538
if( checkALError() )
539
return 0;
540
541
// Return how many were processed:
542
return processed;
543
}
544
545
/**
546
* Dequeues all previously queued data.
547
*/
548
@Override
549
public void flush()
550
{
551
// Only a streaming source can be flushed, because only streaming
552
// sources have queued buffers:
553
if( channelType != SoundSystemConfig.TYPE_STREAMING )
554
return;
555
556
// determine how many buffers have been queued:
557
int queued = AL10.alGetSourcei( ALSource.get( 0 ),
558
AL10.AL_BUFFERS_QUEUED );
559
// Check for errors:
560
if( checkALError() )
561
return;
562
563
IntBuffer intBuffer = BufferUtils.createIntBuffer( 1 );
564
while( queued > 0 )
565
{
566
try
567
{
568
AL10.alSourceUnqueueBuffers( ALSource.get( 0 ), intBuffer );
569
}
570
catch( Exception e )
571
{
572
return;
573
}
574
if( checkALError() )
575
return;
576
queued--;
577
}
578
millisPreviouslyPlayed = 0;
579
}
580
581
/**
582
* Stops the channel, dequeues any queued data, and closes the channel.
583
*/
584
@Override
585
public void close()
586
{
587
try
588
{
589
AL10.alSourceStop( ALSource.get( 0 ) );
590
AL10.alGetError();
591
}
592
catch( Exception e )
593
{}
594
595
if( channelType == SoundSystemConfig.TYPE_STREAMING )
596
flush();
597
}
598
599
/**
600
* Plays the currently attached normal source, opens this channel up for
601
* streaming, or resumes playback if this channel was paused.
602
*/
603
@Override
604
public void play()
605
{
606
AL10.alSourcePlay( ALSource.get( 0 ) );
607
checkALError();
608
}
609
610
/**
611
* Temporarily stops playback for this channel.
612
*/
613
@Override
614
public void pause()
615
{
616
AL10.alSourcePause( ALSource.get( 0 ) );
617
checkALError();
618
}
619
620
/**
621
* Stops playback for this channel and rewinds the attached source to the
622
* beginning.
623
*/
624
@Override
625
public void stop()
626
{
627
AL10.alSourceStop( ALSource.get( 0 ) );
628
if( !checkALError() )
629
millisPreviouslyPlayed = 0;
630
}
631
632
/**
633
* Rewinds the attached source to the beginning. Stops the source if it was
634
* paused.
635
*/
636
@Override
637
public void rewind()
638
{
639
// rewinding for streaming sources is handled elsewhere
640
if( channelType == SoundSystemConfig.TYPE_STREAMING )
641
return;
642
643
AL10.alSourceRewind( ALSource.get( 0 ) );
644
if( !checkALError() )
645
millisPreviouslyPlayed = 0;
646
}
647
648
649
/**
650
* Used to determine if a channel is actively playing a source. This method
651
* will return false if the channel is paused or stopped and when no data is
652
* queued to be streamed.
653
* @return True if this channel is playing a source.
654
*/
655
@Override
656
public boolean playing()
657
{
658
int state = AL10.alGetSourcei( ALSource.get( 0 ),
659
AL10.AL_SOURCE_STATE );
660
if( checkALError() )
661
return false;
662
663
return( state == AL10.AL_PLAYING );
664
}
665
666
/**
667
* Checks for OpenAL errors, and prints a message if there is an error.
668
* @return True if there was an error, False if not.
669
*/
670
private boolean checkALError()
671
{
672
switch( AL10.alGetError() )
673
{
674
case AL10.AL_NO_ERROR:
675
return false;
676
case AL10.AL_INVALID_NAME:
677
errorMessage( "Invalid name parameter." );
678
return true;
679
case AL10.AL_INVALID_ENUM:
680
errorMessage( "Invalid parameter." );
681
return true;
682
case AL10.AL_INVALID_VALUE:
683
errorMessage( "Invalid enumerated parameter value." );
684
return true;
685
case AL10.AL_INVALID_OPERATION:
686
errorMessage( "Illegal call." );
687
return true;
688
case AL10.AL_OUT_OF_MEMORY:
689
errorMessage( "Unable to allocate memory." );
690
return true;
691
default:
692
errorMessage( "An unrecognized error occurred." );
693
return true;
694
}
695
}
696
}
697
698