Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
lDEVinux
GitHub Repository: lDEVinux/eaglercraft
Path: blob/main/src/lwjgl/java/paulscode/sound/SoundSystemConfig.java
8644 views
1
package paulscode.sound;
2
3
import java.lang.reflect.InvocationTargetException;
4
import java.lang.reflect.Method;
5
import java.util.Locale;
6
import java.util.ListIterator;
7
import java.util.LinkedList;
8
9
/**
10
* The SoundSystemConfig class is used to access global sound system settings,
11
* and to link with external pluggins. All members of this class are static.
12
* SoundSystemConfig is sort of a "catch all" configuration class, so if you
13
* are not sure where to find something in the SoundSystem library, this is
14
* probably a good place to start.
15
*<br><br>
16
*<b><i> SoundSystem License:</b></i><br><b><br>
17
* You are free to use this library for any purpose, commercial or otherwise.
18
* You may modify this library or source code, and distribute it any way you
19
* like, provided the following conditions are met:
20
*<br>
21
* 1) You may not falsely claim to be the author of this library or any
22
* unmodified portion of it.
23
*<br>
24
* 2) You may not copyright this library or a modified version of it and then
25
* sue me for copyright infringement.
26
*<br>
27
* 3) If you modify the source code, you must clearly document the changes
28
* made before redistributing the modified source code, so other users know
29
* it is not the original code.
30
*<br>
31
* 4) You are not required to give me credit for this library in any derived
32
* work, but if you do, you must also mention my website:
33
* http://www.paulscode.com
34
*<br>
35
* 5) I the author will not be responsible for any damages (physical,
36
* financial, or otherwise) caused by the use if this library or any part
37
* of it.
38
*<br>
39
* 6) I the author do not guarantee, warrant, or make any representations,
40
* either expressed or implied, regarding the use of this library or any
41
* part of it.
42
* <br><br>
43
* Author: Paul Lamb
44
* <br>
45
* http://www.paulscode.com
46
* </b>
47
*/
48
public class SoundSystemConfig
49
{
50
// GLOBAL THREAD SYNCHRONIZATION
51
/**
52
* Lock object used to synchronize the three threads used by SoundSystem.
53
* Synchronize on this anytime you manually manipulate a Source's properties.
54
*/
55
public static final Object THREAD_SYNC = new Object();
56
// END GLOBAL THREAD SYNCHRONIZATION
57
58
// GLOBAL IDENTIFIERS
59
60
/**
61
* A normal (non-streaming) source. Also used to define a Channel type as
62
* normal.
63
*/
64
public static final int TYPE_NORMAL = 0;
65
/**
66
* A streaming source. Also used to define a Channel type as streaming.
67
*/
68
public static final int TYPE_STREAMING = 1;
69
70
/**
71
* Global identifier for no attenuation. Attenuation is how a source's volume
72
* fades with distance. When there is no attenuation, a source's volume
73
* remains constaint regardles of distance.
74
*/
75
public static final int ATTENUATION_NONE = 0; // no attenuation
76
/**
77
* Global identifier for rolloff attenuation. Rolloff attenuation is a
78
* realistic attenuation model, which uses a rolloff factor to determine how
79
* quickly a source fades with distance. A smaller rolloff factor will fade at
80
* a further distance, and a rolloff factor of 0 will never fade. NOTE: In
81
* OpenAL, rolloff attenuation only works for monotone sounds.
82
*/
83
public static final int ATTENUATION_ROLLOFF = 1; // logrithmic attenuation
84
/**
85
* Global identifier for linear attenuation. Linear attenuation is less
86
* realistic than rolloff attenuation, but it allows the user to specify a
87
* maximum "fade distance" where a source's volume becomes zero.
88
*/
89
public static final int ATTENUATION_LINEAR = 2; // linear attenuation
90
91
/**
92
* A Regular expression for determining if a file's extension is MIDI.
93
*/
94
public static String EXTENSION_MIDI = ".*[mM][iI][dD][iI]?$";
95
96
/**
97
* A Regular expression for determining if a path is an online URL.
98
*/
99
public static String PREFIX_URL = "^[hH][tT][tT][pP]://.*";
100
101
// END GLOBAL IDENTIFIERS
102
103
104
// PRIVATE STATIC VARIABLES
105
106
/**
107
* Handle to the message logger. The default logger can be changed by
108
* overridding the {@link paulscode.sound.SoundSystemLogger SoundSystemLogger}
109
* class and calling the setLogger() method (must be done BEFORE instantiating
110
* the SoundSystem class!)
111
*/
112
private static SoundSystemLogger logger = null;
113
114
/**
115
* List of library types in their order of priority.
116
*/
117
private static LinkedList<Class> libraries;
118
119
/**
120
* List of codecs and the file formats they are associated with.
121
*/
122
private static LinkedList<Codec> codecs = null;
123
124
/**
125
* List of stream listeners.
126
*/
127
private static LinkedList<IStreamListener> streamListeners = null;
128
/**
129
* For synchronizing access to the streamListeners list.
130
*/
131
private static final Object streamListenersLock = new Object();
132
133
/**
134
* Maximum number of normal (non-streaming) channels that can be created.
135
* NOTE: JavaSound may require the total number of channels (non-streaming +
136
* streaming) to be 32.
137
*/
138
private static int numberNormalChannels = 28;
139
/**
140
* Maximum number of streaming channels that can be created.
141
* NOTE: JavaSound may require the total number of channels (non-streaming +
142
* streaming) to be 32.
143
*/
144
private static int numberStreamingChannels = 4;
145
/**
146
* Overall volume, affecting all sources. Float value (0.0f - 1.0f).
147
*/
148
private static float masterGain = 1.0f;
149
/**
150
* Attenuation model to use if not specified. Attenuation is how a source's
151
* volume fades with distance.
152
*/
153
private static int defaultAttenuationModel = ATTENUATION_ROLLOFF;
154
/**
155
* Default value to use for the rolloff factor if not specified.
156
*/
157
private static float defaultRolloffFactor = 0.03f;
158
/**
159
* Value to use for the doppler factor, for determining Doppler scale.
160
*/
161
private static float dopplerFactor = 0.0f;
162
/**
163
* Value to use for the doppler velocity.
164
*/
165
private static float dopplerVelocity = 1.0f;
166
/**
167
* Default value to use for fade distance if not specified.
168
*/
169
private static float defaultFadeDistance = 1000.0f;
170
/**
171
* Package where the sound files are located (must be followed by '/').
172
*/
173
private static String soundFilesPackage = "Sounds/";
174
175
/**
176
* Number of bytes to load at a time when streaming.
177
*/
178
private static int streamingBufferSize = 131072;
179
/**
180
* Number of buffers used for each streaming sorce. Slow codecs may require
181
* this number to be greater than 2 to prevent audio skipping during playback.
182
*/
183
private static int numberStreamingBuffers = 3;
184
/**
185
* Enables a transition-speed optimization by assuming all sounds in each
186
* streaming source's queue will have exactly the same format once decoded
187
* (including channels, sample rate, and sample size). This is an advanced
188
* setting which should only be changed by experienced developers.
189
*/
190
private static boolean streamQueueFormatsMatch = false;
191
/**
192
* The maximum number of bytes to read in for (non-streaming) files.
193
* Increase this value if non-streaming sounds are getting cut off.
194
* Decrease this value if large sound files are causing lag during load time.
195
*/
196
private static int maxFileSize = 268435456;
197
/**
198
* Size of each chunk to read at a time for loading (non-streaming) files.
199
* Increase if loading sound files is causing significant lag.
200
*/
201
private static int fileChunkSize = 1048576;
202
203
/**
204
* Indicates whether or not there is a codec for reading from MIDI files. If
205
* there is no codec for MIDI, then SoundSystem uses javax.sound.midi.
206
*/
207
private static boolean midiCodec = false;
208
209
/**
210
* MIDI device to try using as the Synthesizer. May be the full name or part
211
* of the name. If this String is empty, the default Synthesizer will be used,
212
* or one of the common alternate synthesizers if the default Synthesizer is
213
* unavailable.
214
*/
215
private static String overrideMIDISynthesizer = "";
216
217
// END PRIVATE STATIC VARIABLES
218
219
// THESE TWO METHODS PROVIDE INFORMATION ABOUT THE INDIVIDUAL SOUND LIBRARIES
220
221
/**
222
* Adds an entry to the list of library types. This method has no effect if
223
* the specified library type is already in the list of libraries.
224
* NOTE: The parameterless constructor of the SoundSystem class will try to
225
* load libraries in the order that they were entered into the list.
226
* @param libraryClass Derivitive of class 'Library'.
227
*/
228
public static void addLibrary( Class libraryClass )
229
throws SoundSystemException
230
{
231
if( libraryClass == null )
232
throw new SoundSystemException(
233
"Parameter null in method 'addLibrary'",
234
SoundSystemException.NULL_PARAMETER );
235
if( !Library.class.isAssignableFrom( libraryClass ) )
236
throw new SoundSystemException( "The specified class does not " +
237
"extend class 'Library' in method 'addLibrary'" );
238
239
if( libraries == null )
240
libraries = new LinkedList<Class>();
241
242
if( !libraries.contains( libraryClass ) )
243
libraries.add( libraryClass );
244
}
245
246
/**
247
* Removes the specified library from the list of library types.
248
* @param libraryClass Derivitive of class 'Library'.
249
*/
250
public static void removeLibrary( Class libraryClass )
251
throws SoundSystemException
252
{
253
if( libraries == null || libraryClass == null )
254
return;
255
256
libraries.remove( libraryClass );
257
}
258
259
/**
260
* Returns the list of library types.
261
* @return LinkedList of classes derived from 'Library', or null if none were specified.
262
*/
263
public static LinkedList<Class> getLibraries()
264
{
265
return libraries;
266
}
267
268
/**
269
* Checks if the specified library class is compatible on the user's machine.
270
* @param libraryClass Library type to check.
271
* @return True or false.
272
*/
273
public static boolean libraryCompatible( Class libraryClass )
274
{
275
if( libraryClass == null )
276
{
277
errorMessage( "Parameter 'libraryClass' null in method" +
278
"'librayCompatible'" );
279
return false;
280
}
281
if( !Library.class.isAssignableFrom( libraryClass ) )
282
{
283
errorMessage( "The specified class does not extend class " +
284
"'Library' in method 'libraryCompatible'" );
285
return false;
286
}
287
288
Object o = runMethod( libraryClass, "libraryCompatible",
289
new Class[0], new Object[0] );
290
291
if( o == null )
292
{
293
errorMessage( "Method 'Library.libraryCompatible' returned " +
294
"'null' in method 'libraryCompatible'" );
295
return false;
296
}
297
298
return( ( (Boolean) o ).booleanValue() );
299
}
300
301
/**
302
* Return the short title of the specified library, or null if error.
303
* @param libraryClass Derivitive of class 'Library'.
304
* @return String containing the library title.
305
*/
306
public static String getLibraryTitle( Class libraryClass )
307
{
308
if( libraryClass == null )
309
{
310
errorMessage( "Parameter 'libraryClass' null in method" +
311
"'getLibrayTitle'" );
312
return null;
313
}
314
if( !Library.class.isAssignableFrom( libraryClass ) )
315
{
316
errorMessage( "The specified class does not extend class " +
317
"'Library' in method 'getLibraryTitle'" );
318
return null;
319
}
320
321
Object o = runMethod( libraryClass, "getTitle", new Class[0],
322
new Object[0] );
323
if( o == null )
324
{
325
errorMessage( "Method 'Library.getTitle' returned " +
326
"'null' in method 'getLibraryTitle'" );
327
return null;
328
}
329
330
return( (String) o );
331
}
332
333
/**
334
* Return the longer description of the specified library, or null if error.
335
* @param libraryClass Derivitive of class 'Library'.
336
* @return String containing the library title.
337
*/
338
public static String getLibraryDescription( Class libraryClass )
339
{
340
if( libraryClass == null )
341
{
342
errorMessage( "Parameter 'libraryClass' null in method" +
343
"'getLibrayDescription'" );
344
return null;
345
}
346
if( !Library.class.isAssignableFrom( libraryClass ) )
347
{
348
errorMessage( "The specified class does not extend class " +
349
"'Library' in method 'getLibraryDescription'" );
350
return null;
351
}
352
353
Object o = runMethod( libraryClass, "getDescription",
354
new Class[0], new Object[0] );
355
if( o == null )
356
{
357
errorMessage( "Method 'Library.getDescription' returned " +
358
"'null' in method 'getLibraryDescription'" );
359
return null;
360
}
361
362
return( (String) o );
363
}
364
365
/**
366
* Return whether or not requires reversal of audio data byte-order.
367
* @param libraryClass Derivitive of class 'Library'.
368
* @return True if byte-order reversal is required.
369
*/
370
public static boolean reverseByteOrder( Class libraryClass )
371
{
372
if( libraryClass == null )
373
{
374
errorMessage( "Parameter 'libraryClass' null in method" +
375
"'reverseByteOrder'" );
376
return false;
377
}
378
if( !Library.class.isAssignableFrom( libraryClass ) )
379
{
380
errorMessage( "The specified class does not extend class " +
381
"'Library' in method 'reverseByteOrder'" );
382
return false;
383
}
384
385
Object o = runMethod( libraryClass, "reversByteOrder",
386
new Class[0], new Object[0] );
387
if( o == null )
388
{
389
errorMessage( "Method 'Library.reverseByteOrder' returned " +
390
"'null' in method 'getLibraryDescription'" );
391
return false;
392
}
393
394
return( ((Boolean) o).booleanValue() );
395
}
396
397
// END LIBRARY INFORMATION
398
399
// Use the following methods to interface the private variables above:
400
401
// STATIC NONSYNCHRONIZED INTERFACE METHODS
402
/**
403
* Changes the message logger to use for handling status messages, warnings,
404
* and error messages. This method should only be called BEFORE instantiating
405
* the SoundSystem class! If this method is called after the SoundSystem has
406
* been created, there will be handles floating around to two different
407
* loggers, and the results will be undesirable. This method can be used to
408
* change how messages are handled. First, the
409
* {@link paulscode.sound.SoundSystemLogger SoundSystemLogger} class should be
410
* extended and methods overriden to change how messages are handled. Then,
411
* the overridden class should be instantiated, and a call made to
412
* SoundSystemConfig.setLogger() before creating the SoundSystem object.
413
* If an alternate logger is not set by the user before the SoundSystem is
414
* instantiated, then an instance of the base SoundSystemLogger class will be
415
* used by default.
416
* @param l Handle to a message logger.
417
*/
418
public static void setLogger( SoundSystemLogger l )
419
{
420
logger = l;
421
}
422
/**
423
* Returns a handle to the message logger.
424
* @return The current message logger.
425
*/
426
public static SoundSystemLogger getLogger()
427
{
428
return logger;
429
}
430
431
// STATIC SYNCHRONIZED INTERFACE METHODS
432
433
/**
434
* Sets the maximum number of normal (non-streaming) channels that can be
435
* created. Streaming channels are created first, so the higher the maximum
436
* number of streaming channels is set, the fewer non-streaming channels will
437
* be available. If unable to create the number of channels specified,
438
* SoundSystem will create as many as possible.
439
* NOTE: Some sound library pluggins may require the total number of channels
440
* (non-streaming + streaming) to be 32.
441
* @param number How many normal audio channels.
442
*/
443
public static synchronized void setNumberNormalChannels( int number )
444
{
445
numberNormalChannels = number;
446
}
447
448
/**
449
* Returns the maximum number of normal (non-streaming) channels that can be
450
* created.
451
* @return Maximum non-streaming channels.
452
*/
453
public static synchronized int getNumberNormalChannels()
454
{
455
return numberNormalChannels;
456
}
457
458
/**
459
* Sets the maximum number of streaming channels that can be created.
460
* Streaming channels are created first, so the higher the maximum number of
461
* streaming channels is set, the fewer non-streaming channels will
462
* be available. If unable to create the number of channels specified,
463
* SoundSystem will create as many as possible.
464
* NOTE: Some sound library pluggins may require the total number of channels
465
* (non-streaming + streaming) to be 32.
466
* @param number How many streaming audio channels.
467
*/
468
public static synchronized void setNumberStreamingChannels( int number )
469
{
470
numberStreamingChannels = number;
471
}
472
473
/**
474
* Returns the maximum number of streaming channels that can be created.
475
* @return Maximum streaming channels.
476
*/
477
public static synchronized int getNumberStreamingChannels()
478
{
479
return numberStreamingChannels;
480
}
481
482
/**
483
* Sets the varriable used for overall volume, affecting all sources.
484
* @param value Float value (0.0f - 1.0f).
485
*/
486
public static synchronized void setMasterGain( float value )
487
{
488
masterGain = value;
489
}
490
491
/**
492
* Returns the value for the overall volume.
493
* @return A float value (0.0f - 1.0f).
494
*/
495
public static synchronized float getMasterGain()
496
{
497
return masterGain;
498
}
499
500
/**
501
* Sets the default attenuation model to use when one is not specified.
502
* Attenuation is how a source's volume fades with distance.
503
* @param model A global attenuation model identifier.
504
*/
505
public static synchronized void setDefaultAttenuation( int model )
506
{
507
defaultAttenuationModel = model;
508
}
509
/**
510
* Returns the default attenuation model used when one is not specified.
511
* @return A global attenuation model identifier
512
*/
513
public static synchronized int getDefaultAttenuation()
514
{
515
return defaultAttenuationModel;
516
}
517
/**
518
* Sets the default rolloff factor to use when one is not specified.
519
* @param rolloff Rolloff factor.
520
*/
521
public static synchronized void setDefaultRolloff( float rolloff )
522
{
523
defaultRolloffFactor = rolloff;
524
}
525
/**
526
* Returns the doppler factor, for determining Doppler Effect scale.
527
* @return Doppler factor
528
*/
529
public static synchronized float getDopplerFactor()
530
{
531
return dopplerFactor;
532
}
533
/**
534
* Sets the doppler factor, for determining Doppler Effect scale. Use this
535
* method BEFORE instantiating the SoundSystem. To change the Doppler factor
536
* after the SoundSystem is instantiated, use the
537
* SoundSystem.changeDopplerFactor method instead.
538
* @param factor Doppler factor.
539
*/
540
public static synchronized void setDopplerFactor( float factor )
541
{
542
dopplerFactor = factor;
543
}
544
/**
545
* Returns the Doppler Velocity, for use in Doppler Effect.
546
* @return Doppler velocity.
547
*/
548
public static synchronized float getDopplerVelocity()
549
{
550
return dopplerVelocity;
551
}
552
/**
553
* Sets the Doppler velocity, for use in Doppler Effect. Use this method
554
* BEFORE instantiating the SoundSystem. To change the Doppler velocity after
555
* the SoundSystem is instantiated, use the SoundSystem.changeDopplerVelocity
556
* method instead.
557
* @param velocity Doppler velocity.
558
*/
559
public static synchronized void setDopplerVelocity( float velocity )
560
{
561
dopplerVelocity = velocity;
562
}
563
/**
564
* Returns the default rolloff factor used when one is not specified.
565
* @return Default rolloff factor
566
*/
567
public static synchronized float getDefaultRolloff()
568
{
569
return defaultRolloffFactor;
570
}
571
/**
572
* Sets the default fade distance to use when one is not specified.
573
* @param distance Fade Distance.
574
*/
575
public static synchronized void setDefaultFadeDistance( float distance )
576
{
577
defaultFadeDistance = distance;
578
}
579
/**
580
* Returns the default fade distance used when one is not specified.
581
* @return Default fade distance
582
*/
583
public static synchronized float getDefaultFadeDistance()
584
{
585
return defaultFadeDistance;
586
}
587
/**
588
* Sets the package where sound files are located.
589
* @param location Path to the sound files location (must be followed by '/').
590
*/
591
public static synchronized void setSoundFilesPackage( String location )
592
{
593
soundFilesPackage = location;
594
}
595
/**
596
* Returns the package where sound files are located.
597
* @return Path to the sound files location
598
*/
599
public static synchronized String getSoundFilesPackage()
600
{
601
return soundFilesPackage;
602
}
603
/**
604
* Sets the number of bytes to load at a time when streaming.
605
* @param size Size in bytes.
606
*/
607
public static synchronized void setStreamingBufferSize( int size )
608
{
609
streamingBufferSize = size;
610
}
611
/**
612
* Returns the number of bytes to load at a time when streaming.
613
* @return Size in bytes.
614
*/
615
public static synchronized int getStreamingBufferSize()
616
{
617
return streamingBufferSize;
618
}
619
/**
620
* Sets the number of buffers used for each streaming sorce.
621
* Slow codecs may require this number to be greater than 2 to prevent audio
622
* skipping during playback.
623
* @param num How many buffers.
624
*/
625
public static synchronized void setNumberStreamingBuffers( int num )
626
{
627
numberStreamingBuffers = num;
628
}
629
/**
630
* Returns the number of buffers used for each streaming sorce.
631
* @return How many buffers.
632
*/
633
public static synchronized int getNumberStreamingBuffers()
634
{
635
return numberStreamingBuffers;
636
}
637
638
/**
639
* Enables a transition-speed optimization by assuming all sounds in each
640
* streaming source's queue will have exactly the same format once decoded
641
* (including channels, sample rate, and sample size). This is an advanced
642
* setting which should only be changed by experienced developers.
643
* @param val False by default.
644
*/
645
public static synchronized void setStreamQueueFormatsMatch( boolean val )
646
{
647
streamQueueFormatsMatch = val;
648
}
649
650
/**
651
* Returns whether or not all sounds in each streaming source's queue will be
652
* handled as if they have exactly the same format once decoded (including
653
* channels, sample rate, and sample size). This is an advanced setting which
654
* should only be changed by experienced developers.
655
* @return Normally false.
656
*/
657
public static synchronized boolean getStreamQueueFormatsMatch()
658
{
659
return streamQueueFormatsMatch;
660
}
661
662
/**
663
* Sets the maximum number of bytes to read in for (non-streaming) files.
664
* Increase this value if non-streaming sounds are getting cut off.
665
* Decrease this value if large sound files are causing lag during load time.
666
* @param size Size in bytes.
667
*/
668
public static synchronized void setMaxFileSize( int size )
669
{
670
maxFileSize = size;
671
}
672
/**
673
* Returns the maximum number of bytes to read in for (non-streaming) files.
674
* @return Size in bytes.
675
*/
676
public static synchronized int getMaxFileSize()
677
{
678
return maxFileSize;
679
}
680
/**
681
* Sets the size of each chunk to read at a time for loading (non-streaming)
682
* files. Increase if loading sound files is causing significant lag.
683
* @param size Size in bytes.
684
*/
685
public static synchronized void setFileChunkSize( int size )
686
{
687
fileChunkSize = size;
688
}
689
/**
690
* Returns the size of each chunk to read at a time for loading (non-streaming)
691
* files.
692
* @return Size in bytes.
693
*/
694
public static synchronized int getFileChunkSize()
695
{
696
return fileChunkSize;
697
}
698
/**
699
* Returns the name of the MIDI synthesizer to use instead of the default, or
700
* empty string if none was specified.
701
* @return All or part of a MIDI device name, or empty string for not specified.
702
*/
703
public static synchronized String getOverrideMIDISynthesizer()
704
{
705
return overrideMIDISynthesizer;
706
}
707
/**
708
* Sets the name of the MIDI synthesizer to use instead of the default. If
709
* 'name' is an empty string, the default Synthesizer will be used, or one of
710
* the common alternate synthesizers if the default Synthesizer is unavailable.
711
* @param name All or part of the MIDI device name.
712
*/
713
public static synchronized void setOverrideMIDISynthesizer( String name )
714
{
715
overrideMIDISynthesizer = name;
716
}
717
/**
718
* Uses the specified file extension to associate a particular file format
719
* with the codec used to read audio data from it.
720
* @param extension File extension to be associated with the specified codec.
721
* @param iCodecClass Codec type to use for files with the specified extension.
722
*/
723
public static synchronized void setCodec( String extension,
724
Class iCodecClass )
725
throws SoundSystemException
726
{
727
if( extension == null )
728
throw new SoundSystemException( "Parameter 'extension' null in " +
729
"method 'setCodec'.",
730
SoundSystemException.NULL_PARAMETER );
731
if( iCodecClass == null )
732
throw new SoundSystemException( "Parameter 'iCodecClass' null in " +
733
"method 'setCodec'.",
734
SoundSystemException.NULL_PARAMETER );
735
if( !ICodec.class.isAssignableFrom( iCodecClass ) )
736
throw new SoundSystemException( "The specified class does " +
737
"not implement interface 'ICodec' in method 'setCodec'",
738
SoundSystemException.CLASS_TYPE_MISMATCH );
739
740
if( codecs == null )
741
codecs = new LinkedList<Codec>();
742
743
ListIterator<Codec> i = codecs.listIterator();
744
Codec codec;
745
746
while( i.hasNext() )
747
{
748
codec = i.next();
749
if( extension.matches( codec.extensionRegX ) )
750
i.remove();
751
}
752
codecs.add( new Codec( extension, iCodecClass ) );
753
754
// Let SoundSystem know if this is a MIDI codec, so it won't use
755
// javax.sound.midi anymore:
756
if( extension.matches( EXTENSION_MIDI ) )
757
midiCodec = true;
758
}
759
/**
760
* Returns the codec that can be used to read audio data from the specified
761
* file.
762
* @param filename File to get a codec for.
763
* @return Codec to use for reading audio data.
764
*/
765
public static synchronized ICodec getCodec( String filename )
766
{
767
if( codecs == null )
768
return null;
769
770
ListIterator<Codec> i = codecs.listIterator();
771
Codec codec;
772
773
while( i.hasNext() )
774
{
775
codec = i.next();
776
if( filename.matches( codec.extensionRegX ) )
777
return codec.getInstance();
778
}
779
780
return null;
781
}
782
783
/**
784
* Indicates whether or not there is a codec for reading from MIDI files. If
785
* there is no codec for MIDI, then SoundSystem uses javax.sound.midi.
786
* @return True if there the user defined a MIDI codec.
787
*/
788
public static boolean midiCodec()
789
{
790
return midiCodec;
791
}
792
793
/**
794
* Adds an entry to the list of stream listeners. If the instance is already
795
* in the list, the command is ignored.
796
* @param streamListener Implementation of interface 'IStreamListener'.
797
*/
798
public static void addStreamListener( IStreamListener streamListener )
799
{
800
synchronized( streamListenersLock )
801
{
802
if( streamListeners == null )
803
streamListeners = new LinkedList<IStreamListener>();
804
805
if( !streamListeners.contains( streamListener ) )
806
streamListeners.add( streamListener );
807
}
808
}
809
810
/**
811
* Removes an entry from the list of stream listeners.
812
* @param streamListener Implementation of interface 'IStreamListener'.
813
*/
814
public static void removeStreamListener( IStreamListener streamListener )
815
{
816
817
synchronized( streamListenersLock )
818
{
819
if( streamListeners == null )
820
streamListeners = new LinkedList<IStreamListener>();
821
822
if( streamListeners.contains( streamListener ) )
823
streamListeners.remove( streamListener );
824
}
825
}
826
827
/**
828
* Notifies all stream listeners that an End Of Stream was reached. If there
829
* are no listeners, the command is ignored.
830
* @param sourcename String identifier of the source which reached the EOS.
831
* @param queueSize Number of items left the the stream's play queue, or zero if none.
832
*/
833
public static void notifyEOS( String sourcename, int queueSize )
834
{
835
synchronized( streamListenersLock )
836
{
837
if( streamListeners == null )
838
return;
839
}
840
final String srcName = sourcename;
841
final int qSize = queueSize;
842
843
new Thread()
844
{
845
@Override
846
public void run()
847
{
848
synchronized( streamListenersLock )
849
{
850
if( streamListeners == null )
851
return;
852
ListIterator<IStreamListener> i = streamListeners.listIterator();
853
IStreamListener streamListener;
854
while( i.hasNext() )
855
{
856
streamListener = i.next();
857
if( streamListener == null )
858
i.remove();
859
else
860
streamListener.endOfStream( srcName, qSize );
861
}
862
}
863
}
864
}.start();
865
}
866
867
// END STATIC SYNCHRONIZED INTERFACE METHODS
868
869
870
// PRIVATE INTERNAL METHODS
871
872
/**
873
* Display the specified error message using the current logger.
874
* @param message Error message to display.
875
*/
876
private static void errorMessage( String message )
877
{
878
if( logger != null )
879
logger.errorMessage( "SoundSystemConfig", message, 0 );
880
}
881
882
// We don't know what Class parameter 'c' is, so we will ignore the
883
// warning message "unchecked call to getMethod".
884
@SuppressWarnings("unchecked")
885
/**
886
* Returns the results of calling the specified method from the specified
887
* class using the specified parameters.
888
* @param c Class to call the method on.
889
* @param method Name of the method.
890
* @param paramTypes Data types of the parameters being passed to the method.
891
* @param params Actual parameters to pass to the method.
892
* @return Specified method's return value, or null if error or void.
893
*/
894
private static Object runMethod( Class c, String method, Class[] paramTypes,
895
Object[] params )
896
{
897
Method m = null;
898
try
899
{
900
m = c.getMethod( method, paramTypes ); // <--- generates a warning
901
}
902
catch( NoSuchMethodException nsme )
903
{
904
errorMessage( "NoSuchMethodException thrown when attempting " +
905
"to call method '" + method + "' in " +
906
"method 'runMethod'" );
907
return null;
908
}
909
catch( SecurityException se )
910
{
911
errorMessage( "Access denied when attempting to call method '" +
912
method + "' in method 'runMethod'" );
913
return null;
914
}
915
catch( NullPointerException npe )
916
{
917
errorMessage( "NullPointerException thrown when attempting " +
918
"to call method '" + method + "' in " +
919
"method 'runMethod'" );
920
return null;
921
}
922
if( m == null )
923
{
924
errorMessage( "Method '" + method + "' not found for the class " +
925
"specified in method 'runMethod'" );
926
return null;
927
}
928
929
Object o = null;
930
try
931
{
932
o = m.invoke( null, params );
933
}
934
catch( IllegalAccessException iae )
935
{
936
errorMessage( "IllegalAccessException thrown when attempting " +
937
"to invoke method '" + method + "' in " +
938
"method 'runMethod'" );
939
return null;
940
}
941
catch( IllegalArgumentException iae )
942
{
943
errorMessage( "IllegalArgumentException thrown when attempting " +
944
"to invoke method '" + method + "' in " +
945
"method 'runMethod'" );
946
return null;
947
}
948
catch( InvocationTargetException ite )
949
{
950
errorMessage( "InvocationTargetException thrown while attempting " +
951
"to invoke method 'Library.getTitle' in " +
952
"method 'getLibraryTitle'" );
953
return null;
954
}
955
catch( NullPointerException npe )
956
{
957
errorMessage( "NullPointerException thrown when attempting " +
958
"to invoke method '" + method + "' in " +
959
"method 'runMethod'" );
960
return null;
961
}
962
catch( ExceptionInInitializerError eiie )
963
{
964
errorMessage( "ExceptionInInitializerError thrown when " +
965
"attempting to invoke method '" + method + "' in " +
966
"method 'runMethod'" );
967
return null;
968
}
969
970
return( o );
971
}
972
973
// END PRIVATE INTERNAL METHODS
974
975
976
// PRIVATE INTERNAL CLASSES
977
978
/**
979
* The Codec class is used to associate individual file formats with the
980
* codecs used to load audio data from them.
981
*
982
* Author: Paul Lamb
983
*/
984
private static class Codec
985
{
986
/**
987
* A regular expression used to match a file's extension. This is used to
988
* determine the file format.
989
*/
990
public String extensionRegX;
991
/**
992
* Codec used to load audio data from this file format.
993
*/
994
public Class iCodecClass;
995
/**
996
* Constructor: Converts the specified extension string into a regular
997
* expression, and associates that with the specified codec.
998
* @param extension File extension to be associated with the specified codec.
999
* @param iCodec Codec to use for files with the specified extension.
1000
*/
1001
public Codec( String extension, Class iCodecClass )
1002
{
1003
extensionRegX = "";
1004
// Make sure an extension was specified:
1005
if( extension != null && extension.length() > 0 )
1006
{
1007
// We are only interested in the file extension. The filename
1008
// can begin with whatever:
1009
extensionRegX = ".*";
1010
String c;
1011
for( int x = 0; x < extension.length(); x++ )
1012
{
1013
// Each character could be either upper or lower case:
1014
c = extension.substring( x, x + 1 );
1015
extensionRegX += "[" + c.toLowerCase( Locale.ENGLISH )
1016
+ c.toUpperCase( Locale.ENGLISH ) + "]";
1017
}
1018
// The extension will be at the end of the filename:
1019
extensionRegX += "$";
1020
}
1021
// remember the codec to use for this format:
1022
this.iCodecClass = iCodecClass;
1023
}
1024
1025
public ICodec getInstance()
1026
{
1027
if( iCodecClass == null )
1028
return null;
1029
1030
Object o = null;
1031
try
1032
{
1033
o = iCodecClass.newInstance();
1034
}
1035
catch( InstantiationException ie )
1036
{
1037
instantiationErrorMessage();
1038
return null;
1039
}
1040
catch( IllegalAccessException iae )
1041
{
1042
instantiationErrorMessage();
1043
return null;
1044
}
1045
catch( ExceptionInInitializerError eiie )
1046
{
1047
instantiationErrorMessage();
1048
return null;
1049
}
1050
catch( SecurityException se )
1051
{
1052
instantiationErrorMessage();
1053
return null;
1054
}
1055
1056
1057
if( o == null )
1058
{
1059
instantiationErrorMessage();
1060
return null;
1061
}
1062
1063
return (ICodec) o;
1064
}
1065
1066
private void instantiationErrorMessage()
1067
{
1068
errorMessage( "Unrecognized ICodec implementation in method " +
1069
"'getInstance'. Ensure that the implementing " +
1070
"class has one public, parameterless constructor." );
1071
}
1072
}
1073
// END PRIVATE INTERNAL CLASSES
1074
}
1075
1076