package paulscode.sound;
import java.lang.Boolean;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Locale;
import java.util.ListIterator;
import java.util.LinkedList;
/**
* The SoundSystemConfig class is used to access global sound system settings,
* and to link with external pluggins. All members of this class are static.
* SoundSystemConfig is sort of a "catch all" configuration class, so if you
* are not sure where to find something in the SoundSystem library, this is
* probably a good place to start.
*<br><br>
*<b><i> SoundSystem License:</b></i><br><b><br>
* You are free to use this library for any purpose, commercial or otherwise.
* You may modify this library or source code, and distribute it any way you
* like, provided the following conditions are met:
*<br>
* 1) You may not falsely claim to be the author of this library or any
* unmodified portion of it.
*<br>
* 2) You may not copyright this library or a modified version of it and then
* sue me for copyright infringement.
*<br>
* 3) If you modify the source code, you must clearly document the changes
* made before redistributing the modified source code, so other users know
* it is not the original code.
*<br>
* 4) You are not required to give me credit for this library in any derived
* work, but if you do, you must also mention my website:
* http://www.paulscode.com
*<br>
* 5) I the author will not be responsible for any damages (physical,
* financial, or otherwise) caused by the use if this library or any part
* of it.
*<br>
* 6) I the author do not guarantee, warrant, or make any representations,
* either expressed or implied, regarding the use of this library or any
* part of it.
* <br><br>
* Author: Paul Lamb
* <br>
* http://www.paulscode.com
* </b>
*/
public class SoundSystemConfig
{
// GLOBAL THREAD SYNCHRONIZATION
/**
* Lock object used to synchronize the three threads used by SoundSystem.
* Synchronize on this anytime you manually manipulate a Source's properties.
*/
public static final Object THREAD_SYNC = new Object();
// END GLOBAL THREAD SYNCHRONIZATION
// GLOBAL IDENTIFIERS
/**
* A normal (non-streaming) source. Also used to define a Channel type as
* normal.
*/
public static final int TYPE_NORMAL = 0;
/**
* A streaming source. Also used to define a Channel type as streaming.
*/
public static final int TYPE_STREAMING = 1;
/**
* Global identifier for no attenuation. Attenuation is how a source's volume
* fades with distance. When there is no attenuation, a source's volume
* remains constaint regardles of distance.
*/
public static final int ATTENUATION_NONE = 0; // no attenuation
/**
* Global identifier for rolloff attenuation. Rolloff attenuation is a
* realistic attenuation model, which uses a rolloff factor to determine how
* quickly a source fades with distance. A smaller rolloff factor will fade at
* a further distance, and a rolloff factor of 0 will never fade. NOTE: In
* OpenAL, rolloff attenuation only works for monotone sounds.
*/
public static final int ATTENUATION_ROLLOFF = 1; // logrithmic attenuation
/**
* Global identifier for linear attenuation. Linear attenuation is less
* realistic than rolloff attenuation, but it allows the user to specify a
* maximum "fade distance" where a source's volume becomes zero.
*/
public static final int ATTENUATION_LINEAR = 2; // linear attenuation
/**
* A Regular expression for determining if a file's extension is MIDI.
*/
public static String EXTENSION_MIDI = ".*[mM][iI][dD][iI]?$";
/**
* A Regular expression for determining if a path is an online URL.
*/
public static String PREFIX_URL = "^[hH][tT][tT][pP]://.*";
// END GLOBAL IDENTIFIERS
// PRIVATE STATIC VARIABLES
/**
* Handle to the message logger. The default logger can be changed by
* overridding the {@link paulscode.sound.SoundSystemLogger SoundSystemLogger}
* class and calling the setLogger() method (must be done BEFORE instantiating
* the SoundSystem class!)
*/
private static SoundSystemLogger logger = null;
/**
* List of library types in their order of priority.
*/
private static LinkedList<Class> libraries;
/**
* List of codecs and the file formats they are associated with.
*/
private static LinkedList<Codec> codecs = null;
/**
* List of stream listeners.
*/
private static LinkedList<IStreamListener> streamListeners = null;
/**
* For synchronizing access to the streamListeners list.
*/
private static final Object streamListenersLock = new Object();
/**
* Maximum number of normal (non-streaming) channels that can be created.
* NOTE: JavaSound may require the total number of channels (non-streaming +
* streaming) to be 32.
*/
private static int numberNormalChannels = 28;
/**
* Maximum number of streaming channels that can be created.
* NOTE: JavaSound may require the total number of channels (non-streaming +
* streaming) to be 32.
*/
private static int numberStreamingChannels = 4;
/**
* Overall volume, affecting all sources. Float value (0.0f - 1.0f).
*/
private static float masterGain = 1.0f;
/**
* Attenuation model to use if not specified. Attenuation is how a source's
* volume fades with distance.
*/
private static int defaultAttenuationModel = ATTENUATION_ROLLOFF;
/**
* Default value to use for the rolloff factor if not specified.
*/
private static float defaultRolloffFactor = 0.03f;
/**
* Value to use for the doppler factor, for determining Doppler scale.
*/
private static float dopplerFactor = 0.0f;
/**
* Value to use for the doppler velocity.
*/
private static float dopplerVelocity = 1.0f;
/**
* Default value to use for fade distance if not specified.
*/
private static float defaultFadeDistance = 1000.0f;
/**
* Package where the sound files are located (must be followed by '/').
*/
private static String soundFilesPackage = "Sounds/";
/**
* Number of bytes to load at a time when streaming.
*/
private static int streamingBufferSize = 131072;
/**
* Number of buffers used for each streaming sorce. Slow codecs may require
* this number to be greater than 2 to prevent audio skipping during playback.
*/
private static int numberStreamingBuffers = 3;
/**
* The maximum number of bytes to read in for (non-streaming) files.
* Increase this value if non-streaming sounds are getting cut off.
* Decrease this value if large sound files are causing lag during load time.
*/
private static int maxFileSize = 268435456;
/**
* Size of each chunk to read at a time for loading (non-streaming) files.
* Increase if loading sound files is causing significant lag.
*/
private static int fileChunkSize = 1048576;
/**
* Indicates whether or not there is a codec for reading from MIDI files. If
* there is no codec for MIDI, then SoundSystem uses javax.sound.midi.
*/
private static boolean midiCodec = false;
/**
* MIDI device to try using as the Synthesizer. May be the full name or part
* of the name. If this String is empty, the default Synthesizer will be used,
* or one of the common alternate synthesizers if the default Synthesizer is
* unavailable.
*/
private static String overrideMIDISynthesizer = "";
// END PRIVATE STATIC VARIABLES
// THESE TWO METHODS PROVIDE INFORMATION ABOUT THE INDIVIDUAL SOUND LIBRARIES
/**
* Adds an entry to the list of library types. This method has no effect if
* the specified library type is already in the list of libraries.
* NOTE: The parameterless constructor of the SoundSystem class will try to
* load libraries in the order that they were entered into the list.
* @param libraryClass Derivitive of class 'Library'.
*/
public static void addLibrary( Class libraryClass )
throws SoundSystemException
{
if( libraryClass == null )
throw new SoundSystemException(
"Parameter null in method 'addLibrary'",
SoundSystemException.NULL_PARAMETER );
if( !Library.class.isAssignableFrom( libraryClass ) )
throw new SoundSystemException( "The specified class does not " +
"extend class 'Library' in method 'addLibrary'" );
if( libraries == null )
libraries = new LinkedList<Class>();
if( !libraries.contains( libraryClass ) )
libraries.add( libraryClass );
}
/**
* Removes the specified library from the list of library types.
* @param libraryClass Derivitive of class 'Library'.
*/
public static void removeLibrary( Class libraryClass )
throws SoundSystemException
{
if( libraries == null || libraryClass == null )
return;
libraries.remove( libraryClass );
}
/**
* Returns the list of library types.
* @return LinkedList of classes derived from 'Library', or null if none were specified.
*/
public static LinkedList<Class> getLibraries()
{
return libraries;
}
/**
* Checks if the specified library class is compatible on the user's machine.
* @param libraryClass Library type to check.
* @return True or false.
*/
public static boolean libraryCompatible( Class libraryClass )
{
if( libraryClass == null )
{
errorMessage( "Parameter 'libraryClass' null in method" +
"'librayCompatible'" );
return false;
}
if( !Library.class.isAssignableFrom( libraryClass ) )
{
errorMessage( "The specified class does not extend class " +
"'Library' in method 'libraryCompatible'" );
return false;
}
Object o = runMethod( libraryClass, "libraryCompatible",
new Class[0], new Object[0] );
if( o == null )
{
errorMessage( "Method 'Library.libraryCompatible' returned " +
"'null' in method 'libraryCompatible'" );
return false;
}
return( ( (Boolean) o ).booleanValue() );
}
/**
* Return the short title of the specified library, or null if error.
* @param libraryClass Derivitive of class 'Library'.
* @return String containing the library title.
*/
public static String getLibraryTitle( Class libraryClass )
{
if( libraryClass == null )
{
errorMessage( "Parameter 'libraryClass' null in method" +
"'getLibrayTitle'" );
return null;
}
if( !Library.class.isAssignableFrom( libraryClass ) )
{
errorMessage( "The specified class does not extend class " +
"'Library' in method 'getLibraryTitle'" );
return null;
}
Object o = runMethod( libraryClass, "getTitle", new Class[0],
new Object[0] );
if( o == null )
{
errorMessage( "Method 'Library.getTitle' returned " +
"'null' in method 'getLibraryTitle'" );
return null;
}
return( (String) o );
}
/**
* Return the longer description of the specified library, or null if error.
* @param libraryClass Derivitive of class 'Library'.
* @return String containing the library title.
*/
public static String getLibraryDescription( Class libraryClass )
{
if( libraryClass == null )
{
errorMessage( "Parameter 'libraryClass' null in method" +
"'getLibrayDescription'" );
return null;
}
if( !Library.class.isAssignableFrom( libraryClass ) )
{
errorMessage( "The specified class does not extend class " +
"'Library' in method 'getLibraryDescription'" );
return null;
}
Object o = runMethod( libraryClass, "getDescription",
new Class[0], new Object[0] );
if( o == null )
{
errorMessage( "Method 'Library.getDescription' returned " +
"'null' in method 'getLibraryDescription'" );
return null;
}
return( (String) o );
}
/**
* Return whether or not requires reversal of audio data byte-order.
* @param libraryClass Derivitive of class 'Library'.
* @return True if byte-order reversal is required.
*/
public static boolean reverseByteOrder( Class libraryClass )
{
if( libraryClass == null )
{
errorMessage( "Parameter 'libraryClass' null in method" +
"'reverseByteOrder'" );
return false;
}
if( !Library.class.isAssignableFrom( libraryClass ) )
{
errorMessage( "The specified class does not extend class " +
"'Library' in method 'reverseByteOrder'" );
return false;
}
Object o = runMethod( libraryClass, "reversByteOrder",
new Class[0], new Object[0] );
if( o == null )
{
errorMessage( "Method 'Library.reverseByteOrder' returned " +
"'null' in method 'getLibraryDescription'" );
return false;
}
return( ((Boolean) o).booleanValue() );
}
// END LIBRARY INFORMATION
// Use the following methods to interface the private variables above:
// STATIC NONSYNCHRONIZED INTERFACE METHODS
/**
* Changes the message logger to use for handling status messages, warnings,
* and error messages. This method should only be called BEFORE instantiating
* the SoundSystem class! If this method is called after the SoundSystem has
* been created, there will be handles floating around to two different
* loggers, and the results will be undesirable. This method can be used to
* change how messages are handled. First, the
* {@link paulscode.sound.SoundSystemLogger SoundSystemLogger} class should be
* extended and methods overriden to change how messages are handled. Then,
* the overridden class should be instantiated, and a call made to
* SoundSystemConfig.setLogger() before creating the SoundSystem object.
* If an alternate logger is not set by the user before the SoundSystem is
* instantiated, then an instance of the base SoundSystemLogger class will be
* used by default.
* @param l Handle to a message logger.
*/
public static void setLogger( SoundSystemLogger l )
{
logger = l;
}
/**
* Returns a handle to the message logger.
* @return The current message logger.
*/
public static SoundSystemLogger getLogger()
{
return logger;
}
// STATIC SYNCHRONIZED INTERFACE METHODS
/**
* Sets the maximum number of normal (non-streaming) channels that can be
* created. Streaming channels are created first, so the higher the maximum
* number of streaming channels is set, the fewer non-streaming channels will
* be available. If unable to create the number of channels specified,
* SoundSystem will create as many as possible.
* NOTE: Some sound library pluggins may require the total number of channels
* (non-streaming + streaming) to be 32.
* @param number How many normal audio channels.
*/
public static synchronized void setNumberNormalChannels( int number )
{
numberNormalChannels = number;
}
/**
* Returns the maximum number of normal (non-streaming) channels that can be
* created.
* @return Maximum non-streaming channels.
*/
public static synchronized int getNumberNormalChannels()
{
return numberNormalChannels;
}
/**
* Sets the maximum number of streaming channels that can be created.
* Streaming channels are created first, so the higher the maximum number of
* streaming channels is set, the fewer non-streaming channels will
* be available. If unable to create the number of channels specified,
* SoundSystem will create as many as possible.
* NOTE: Some sound library pluggins may require the total number of channels
* (non-streaming + streaming) to be 32.
* @param number How many streaming audio channels.
*/
public static synchronized void setNumberStreamingChannels( int number )
{
numberStreamingChannels = number;
}
/**
* Returns the maximum number of streaming channels that can be created.
* @return Maximum streaming channels.
*/
public static synchronized int getNumberStreamingChannels()
{
return numberStreamingChannels;
}
/**
* Sets the varriable used for overall volume, affecting all sources.
* @param value Float value (0.0f - 1.0f).
*/
public static synchronized void setMasterGain( float value )
{
masterGain = value;
}
/**
* Returns the value for the overall volume.
* @return A float value (0.0f - 1.0f).
*/
public static synchronized float getMasterGain()
{
return masterGain;
}
/**
* Sets the default attenuation model to use when one is not specified.
* Attenuation is how a source's volume fades with distance.
* @param model A global attenuation model identifier.
*/
public static synchronized void setDefaultAttenuation( int model )
{
defaultAttenuationModel = model;
}
/**
* Returns the default attenuation model used when one is not specified.
* @return A global attenuation model identifier
*/
public static synchronized int getDefaultAttenuation()
{
return defaultAttenuationModel;
}
/**
* Sets the default rolloff factor to use when one is not specified.
* @param rolloff Rolloff factor.
*/
public static synchronized void setDefaultRolloff( float rolloff )
{
defaultRolloffFactor = rolloff;
}
/**
* Returns the doppler factor, for determining Doppler Effect scale.
* @return Doppler factor
*/
public static synchronized float getDopplerFactor()
{
return dopplerFactor;
}
/**
* Sets the doppler factor, for determining Doppler Effect scale. Use this
* method BEFORE instantiating the SoundSystem. To change the Doppler factor
* after the SoundSystem is instantiated, use the
* SoundSystem.changeDopplerFactor method instead.
* @param factor Doppler factor.
*/
public static synchronized void setDopplerFactor( float factor )
{
dopplerFactor = factor;
}
/**
* Returns the Doppler Velocity, for use in Doppler Effect.
* @return Doppler velocity.
*/
public static synchronized float getDopplerVelocity()
{
return dopplerVelocity;
}
/**
* Sets the Doppler velocity, for use in Doppler Effect. Use this method
* BEFORE instantiating the SoundSystem. To change the Doppler velocity after
* the SoundSystem is instantiated, use the SoundSystem.changeDopplerVelocity
* method instead.
* @param velocity Doppler velocity.
*/
public static synchronized void setDopplerVelocity( float velocity )
{
dopplerVelocity = velocity;
}
/**
* Returns the default rolloff factor used when one is not specified.
* @return Default rolloff factor
*/
public static synchronized float getDefaultRolloff()
{
return defaultRolloffFactor;
}
/**
* Sets the default fade distance to use when one is not specified.
* @param distance Fade Distance.
*/
public static synchronized void setDefaultFadeDistance( float distance )
{
defaultFadeDistance = distance;
}
/**
* Returns the default fade distance used when one is not specified.
* @return Default fade distance
*/
public static synchronized float getDefaultFadeDistance()
{
return defaultFadeDistance;
}
/**
* Sets the package where sound files are located.
* @param location Path to the sound files location (must be followed by '/').
*/
public static synchronized void setSoundFilesPackage( String location )
{
soundFilesPackage = location;
}
/**
* Returns the package where sound files are located.
* @return Path to the sound files location
*/
public static synchronized String getSoundFilesPackage()
{
return soundFilesPackage;
}
/**
* Sets the number of bytes to load at a time when streaming.
* @param size Size in bytes.
*/
public static synchronized void setStreamingBufferSize( int size )
{
streamingBufferSize = size;
}
/**
* Returns the number of bytes to load at a time when streaming.
* @return Size in bytes.
*/
public static synchronized int getStreamingBufferSize()
{
return streamingBufferSize;
}
/**
* Sets the number of buffers used for each streaming sorce.
* Slow codecs may require this number to be greater than 2 to prevent audio
* skipping during playback.
* @param num How many buffers.
*/
public static synchronized void setNumberStreamingBuffers( int num )
{
numberStreamingBuffers = num;
}
/**
* Returns the number of buffers used for each streaming sorce.
* @return How many buffers.
*/
public static synchronized int getNumberStreamingBuffers()
{
return numberStreamingBuffers;
}
/**
* Sets the maximum number of bytes to read in for (non-streaming) files.
* Increase this value if non-streaming sounds are getting cut off.
* Decrease this value if large sound files are causing lag during load time.
* @param size Size in bytes.
*/
public static synchronized void setMaxFileSize( int size )
{
maxFileSize = size;
}
/**
* Returns the maximum number of bytes to read in for (non-streaming) files.
* @return Size in bytes.
*/
public static synchronized int getMaxFileSize()
{
return maxFileSize;
}
/**
* Sets the size of each chunk to read at a time for loading (non-streaming)
* files. Increase if loading sound files is causing significant lag.
* @param size Size in bytes.
*/
public static synchronized void setFileChunkSize( int size )
{
fileChunkSize = size;
}
/**
* Returns the size of each chunk to read at a time for loading (non-streaming)
* files.
* @return Size in bytes.
*/
public static synchronized int getFileChunkSize()
{
return fileChunkSize;
}
/**
* Returns the name of the MIDI synthesizer to use instead of the default, or
* empty string if none was specified.
* @return All or part of a MIDI device name, or empty string for not specified.
*/
public static synchronized String getOverrideMIDISynthesizer()
{
return overrideMIDISynthesizer;
}
/**
* Sets the name of the MIDI synthesizer to use instead of the default. If
* 'name' is an empty string, the default Synthesizer will be used, or one of
* the common alternate synthesizers if the default Synthesizer is unavailable.
* @param name All or part of the MIDI device name.
*/
public static synchronized void setOverrideMIDISynthesizer( String name )
{
overrideMIDISynthesizer = name;
}
/**
* Uses the specified file extension to associate a particular file format
* with the codec used to read audio data from it.
* @param extension File extension to be associated with the specified codec.
* @param iCodecClass Codec type to use for files with the specified extension.
*/
public static synchronized void setCodec( String extension,
Class iCodecClass )
throws SoundSystemException
{
if( extension == null )
throw new SoundSystemException( "Parameter 'extension' null in " +
"method 'setCodec'.",
SoundSystemException.NULL_PARAMETER );
if( iCodecClass == null )
throw new SoundSystemException( "Parameter 'iCodecClass' null in " +
"method 'setCodec'.",
SoundSystemException.NULL_PARAMETER );
if( !ICodec.class.isAssignableFrom( iCodecClass ) )
throw new SoundSystemException( "The specified class does " +
"not implement interface 'ICodec' in method 'setCodec'",
SoundSystemException.CLASS_TYPE_MISMATCH );
if( codecs == null )
codecs = new LinkedList<Codec>();
ListIterator<Codec> i = codecs.listIterator();
Codec codec;
while( i.hasNext() )
{
codec = i.next();
if( extension.matches( codec.extensionRegX ) )
i.remove();
}
codecs.add( new Codec( extension, iCodecClass ) );
// Let SoundSystem know if this is a MIDI codec, so it won't use
// javax.sound.midi anymore:
if( extension.matches( EXTENSION_MIDI ) )
midiCodec = true;
}
/**
* Returns the codec that can be used to read audio data from the specified
* file.
* @param filename File to get a codec for.
* @return Codec to use for reading audio data.
*/
public static synchronized ICodec getCodec( String filename )
{
if( codecs == null )
return null;
ListIterator<Codec> i = codecs.listIterator();
Codec codec;
while( i.hasNext() )
{
codec = i.next();
if( filename.matches( codec.extensionRegX ) )
return codec.getInstance();
}
return null;
}
/**
* Indicates whether or not there is a codec for reading from MIDI files. If
* there is no codec for MIDI, then SoundSystem uses javax.sound.midi.
* @return True if there the user defined a MIDI codec.
*/
public static boolean midiCodec()
{
return midiCodec;
}
/**
* Adds an entry to the list of stream listeners. If the instance is already
* in the list, the command is ignored.
* @param streamListener Implementation of interface 'IStreamListener'.
*/
public static void addStreamListener( IStreamListener streamListener )
{
synchronized( streamListenersLock )
{
if( streamListeners == null )
streamListeners = new LinkedList<IStreamListener>();
if( !streamListeners.contains( streamListener ) )
streamListeners.add( streamListener );
}
}
/**
* Removes an entry from the list of stream listeners.
* @param streamListener Implementation of interface 'IStreamListener'.
*/
public static void removeStreamListener( IStreamListener streamListener )
{
synchronized( streamListenersLock )
{
if( streamListeners == null )
streamListeners = new LinkedList<IStreamListener>();
if( streamListeners.contains( streamListener ) )
streamListeners.remove( streamListener );
}
}
/**
* Notifies all stream listeners that an End Of Stream was reached. If there
* are no listeners, the command is ignored.
* @param sourcename String identifier of the source which reached the EOS.
* @param queueSize Number of items left the the stream's play queue, or zero if none.
*/
public static void notifyEOS( String sourcename, int queueSize )
{
synchronized( streamListenersLock )
{
if( streamListeners == null )
return;
}
final String srcName = sourcename;
final int qSize = queueSize;
new Thread()
{
@Override
public void run()
{
synchronized( streamListenersLock )
{
if( streamListeners == null )
return;
ListIterator<IStreamListener> i = streamListeners.listIterator();
IStreamListener streamListener;
while( i.hasNext() )
{
streamListener = i.next();
if( streamListener == null )
i.remove();
else
streamListener.endOfStream( srcName, qSize );
}
}
}
}.start();
}
// END STATIC SYNCHRONIZED INTERFACE METHODS
// PRIVATE INTERNAL METHODS
/**
* Display the specified error message using the current logger.
* @param message Error message to display.
*/
private static void errorMessage( String message )
{
if( logger != null )
logger.errorMessage( "SoundSystemConfig", message, 0 );
}
// We don't know what Class parameter 'c' is, so we will ignore the
// warning message "unchecked call to getMethod".
@SuppressWarnings("unchecked")
/**
* Returns the results of calling the specified method from the specified
* class using the specified parameters.
* @param c Class to call the method on.
* @param method Name of the method.
* @param paramTypes Data types of the parameters being passed to the method.
* @param params Actual parameters to pass to the method.
* @return Specified method's return value, or null if error or void.
*/
private static Object runMethod( Class c, String method, Class[] paramTypes,
Object[] params )
{
Method m = null;
try
{
m = c.getMethod( method, paramTypes ); // <--- generates a warning
}
catch( NoSuchMethodException nsme )
{
errorMessage( "NoSuchMethodException thrown when attempting " +
"to call method '" + method + "' in " +
"method 'runMethod'" );
return null;
}
catch( SecurityException se )
{
errorMessage( "Access denied when attempting to call method '" +
method + "' in method 'runMethod'" );
return null;
}
catch( NullPointerException npe )
{
errorMessage( "NullPointerException thrown when attempting " +
"to call method '" + method + "' in " +
"method 'runMethod'" );
return null;
}
if( m == null )
{
errorMessage( "Method '" + method + "' not found for the class " +
"specified in method 'runMethod'" );
return null;
}
Object o = null;
try
{
o = m.invoke( null, params );
}
catch( IllegalAccessException iae )
{
errorMessage( "IllegalAccessException thrown when attempting " +
"to invoke method '" + method + "' in " +
"method 'runMethod'" );
return null;
}
catch( IllegalArgumentException iae )
{
errorMessage( "IllegalArgumentException thrown when attempting " +
"to invoke method '" + method + "' in " +
"method 'runMethod'" );
return null;
}
catch( InvocationTargetException ite )
{
errorMessage( "InvocationTargetException thrown while attempting " +
"to invoke method 'Library.getTitle' in " +
"method 'getLibraryTitle'" );
return null;
}
catch( NullPointerException npe )
{
errorMessage( "NullPointerException thrown when attempting " +
"to invoke method '" + method + "' in " +
"method 'runMethod'" );
return null;
}
catch( ExceptionInInitializerError eiie )
{
errorMessage( "ExceptionInInitializerError thrown when " +
"attempting to invoke method '" + method + "' in " +
"method 'runMethod'" );
return null;
}
return( o );
}
// END PRIVATE INTERNAL METHODS
// PRIVATE INTERNAL CLASSES
/**
* The Codec class is used to associate individual file formats with the
* codecs used to load audio data from them.
*
* Author: Paul Lamb
*/
private static class Codec
{
/**
* A regular expression used to match a file's extension. This is used to
* determine the file format.
*/
public String extensionRegX;
/**
* Codec used to load audio data from this file format.
*/
public Class iCodecClass;
/**
* Constructor: Converts the specified extension string into a regular
* expression, and associates that with the specified codec.
* @param extension File extension to be associated with the specified codec.
* @param iCodec Codec to use for files with the specified extension.
*/
public Codec( String extension, Class iCodecClass )
{
extensionRegX = "";
// Make sure an extension was specified:
if( extension != null && extension.length() > 0 )
{
// We are only interested in the file extension. The filename
// can begin with whatever:
extensionRegX = ".*";
String c;
for( int x = 0; x < extension.length(); x++ )
{
// Each character could be either upper or lower case:
c = extension.substring( x, x + 1 );
extensionRegX += "[" + c.toLowerCase( Locale.ENGLISH )
+ c.toUpperCase( Locale.ENGLISH ) + "]";
}
// The extension will be at the end of the filename:
extensionRegX += "$";
}
// remember the codec to use for this format:
this.iCodecClass = iCodecClass;
}
public ICodec getInstance()
{
if( iCodecClass == null )
return null;
Object o = null;
try
{
o = iCodecClass.newInstance();
}
catch( InstantiationException ie )
{
instantiationErrorMessage();
return null;
}
catch( IllegalAccessException iae )
{
instantiationErrorMessage();
return null;
}
catch( ExceptionInInitializerError eiie )
{
instantiationErrorMessage();
return null;
}
catch( SecurityException se )
{
instantiationErrorMessage();
return null;
}
if( o == null )
{
instantiationErrorMessage();
return null;
}
return (ICodec) o;
}
private void instantiationErrorMessage()
{
errorMessage( "Unrecognized ICodec implementation in method " +
"'getInstance'. Ensure that the implementing " +
"class has one public, parameterless constructor." );
}
}
// END PRIVATE INTERNAL CLASSES
}