Path: blob/main/src/lwjgl/java/paulscode/sound/SoundSystemConfig.java
8644 views
package paulscode.sound;12import java.lang.reflect.InvocationTargetException;3import java.lang.reflect.Method;4import java.util.Locale;5import java.util.ListIterator;6import java.util.LinkedList;78/**9* The SoundSystemConfig class is used to access global sound system settings,10* and to link with external pluggins. All members of this class are static.11* SoundSystemConfig is sort of a "catch all" configuration class, so if you12* are not sure where to find something in the SoundSystem library, this is13* probably a good place to start.14*<br><br>15*<b><i> SoundSystem License:</b></i><br><b><br>16* You are free to use this library for any purpose, commercial or otherwise.17* You may modify this library or source code, and distribute it any way you18* like, provided the following conditions are met:19*<br>20* 1) You may not falsely claim to be the author of this library or any21* unmodified portion of it.22*<br>23* 2) You may not copyright this library or a modified version of it and then24* sue me for copyright infringement.25*<br>26* 3) If you modify the source code, you must clearly document the changes27* made before redistributing the modified source code, so other users know28* it is not the original code.29*<br>30* 4) You are not required to give me credit for this library in any derived31* work, but if you do, you must also mention my website:32* http://www.paulscode.com33*<br>34* 5) I the author will not be responsible for any damages (physical,35* financial, or otherwise) caused by the use if this library or any part36* of it.37*<br>38* 6) I the author do not guarantee, warrant, or make any representations,39* either expressed or implied, regarding the use of this library or any40* part of it.41* <br><br>42* Author: Paul Lamb43* <br>44* http://www.paulscode.com45* </b>46*/47public class SoundSystemConfig48{49// GLOBAL THREAD SYNCHRONIZATION50/**51* Lock object used to synchronize the three threads used by SoundSystem.52* Synchronize on this anytime you manually manipulate a Source's properties.53*/54public static final Object THREAD_SYNC = new Object();55// END GLOBAL THREAD SYNCHRONIZATION5657// GLOBAL IDENTIFIERS5859/**60* A normal (non-streaming) source. Also used to define a Channel type as61* normal.62*/63public static final int TYPE_NORMAL = 0;64/**65* A streaming source. Also used to define a Channel type as streaming.66*/67public static final int TYPE_STREAMING = 1;6869/**70* Global identifier for no attenuation. Attenuation is how a source's volume71* fades with distance. When there is no attenuation, a source's volume72* remains constaint regardles of distance.73*/74public static final int ATTENUATION_NONE = 0; // no attenuation75/**76* Global identifier for rolloff attenuation. Rolloff attenuation is a77* realistic attenuation model, which uses a rolloff factor to determine how78* quickly a source fades with distance. A smaller rolloff factor will fade at79* a further distance, and a rolloff factor of 0 will never fade. NOTE: In80* OpenAL, rolloff attenuation only works for monotone sounds.81*/82public static final int ATTENUATION_ROLLOFF = 1; // logrithmic attenuation83/**84* Global identifier for linear attenuation. Linear attenuation is less85* realistic than rolloff attenuation, but it allows the user to specify a86* maximum "fade distance" where a source's volume becomes zero.87*/88public static final int ATTENUATION_LINEAR = 2; // linear attenuation8990/**91* A Regular expression for determining if a file's extension is MIDI.92*/93public static String EXTENSION_MIDI = ".*[mM][iI][dD][iI]?$";9495/**96* A Regular expression for determining if a path is an online URL.97*/98public static String PREFIX_URL = "^[hH][tT][tT][pP]://.*";99100// END GLOBAL IDENTIFIERS101102103// PRIVATE STATIC VARIABLES104105/**106* Handle to the message logger. The default logger can be changed by107* overridding the {@link paulscode.sound.SoundSystemLogger SoundSystemLogger}108* class and calling the setLogger() method (must be done BEFORE instantiating109* the SoundSystem class!)110*/111private static SoundSystemLogger logger = null;112113/**114* List of library types in their order of priority.115*/116private static LinkedList<Class> libraries;117118/**119* List of codecs and the file formats they are associated with.120*/121private static LinkedList<Codec> codecs = null;122123/**124* List of stream listeners.125*/126private static LinkedList<IStreamListener> streamListeners = null;127/**128* For synchronizing access to the streamListeners list.129*/130private static final Object streamListenersLock = new Object();131132/**133* Maximum number of normal (non-streaming) channels that can be created.134* NOTE: JavaSound may require the total number of channels (non-streaming +135* streaming) to be 32.136*/137private static int numberNormalChannels = 28;138/**139* Maximum number of streaming channels that can be created.140* NOTE: JavaSound may require the total number of channels (non-streaming +141* streaming) to be 32.142*/143private static int numberStreamingChannels = 4;144/**145* Overall volume, affecting all sources. Float value (0.0f - 1.0f).146*/147private static float masterGain = 1.0f;148/**149* Attenuation model to use if not specified. Attenuation is how a source's150* volume fades with distance.151*/152private static int defaultAttenuationModel = ATTENUATION_ROLLOFF;153/**154* Default value to use for the rolloff factor if not specified.155*/156private static float defaultRolloffFactor = 0.03f;157/**158* Value to use for the doppler factor, for determining Doppler scale.159*/160private static float dopplerFactor = 0.0f;161/**162* Value to use for the doppler velocity.163*/164private static float dopplerVelocity = 1.0f;165/**166* Default value to use for fade distance if not specified.167*/168private static float defaultFadeDistance = 1000.0f;169/**170* Package where the sound files are located (must be followed by '/').171*/172private static String soundFilesPackage = "Sounds/";173174/**175* Number of bytes to load at a time when streaming.176*/177private static int streamingBufferSize = 131072;178/**179* Number of buffers used for each streaming sorce. Slow codecs may require180* this number to be greater than 2 to prevent audio skipping during playback.181*/182private static int numberStreamingBuffers = 3;183/**184* Enables a transition-speed optimization by assuming all sounds in each185* streaming source's queue will have exactly the same format once decoded186* (including channels, sample rate, and sample size). This is an advanced187* setting which should only be changed by experienced developers.188*/189private static boolean streamQueueFormatsMatch = false;190/**191* The maximum number of bytes to read in for (non-streaming) files.192* Increase this value if non-streaming sounds are getting cut off.193* Decrease this value if large sound files are causing lag during load time.194*/195private static int maxFileSize = 268435456;196/**197* Size of each chunk to read at a time for loading (non-streaming) files.198* Increase if loading sound files is causing significant lag.199*/200private static int fileChunkSize = 1048576;201202/**203* Indicates whether or not there is a codec for reading from MIDI files. If204* there is no codec for MIDI, then SoundSystem uses javax.sound.midi.205*/206private static boolean midiCodec = false;207208/**209* MIDI device to try using as the Synthesizer. May be the full name or part210* of the name. If this String is empty, the default Synthesizer will be used,211* or one of the common alternate synthesizers if the default Synthesizer is212* unavailable.213*/214private static String overrideMIDISynthesizer = "";215216// END PRIVATE STATIC VARIABLES217218// THESE TWO METHODS PROVIDE INFORMATION ABOUT THE INDIVIDUAL SOUND LIBRARIES219220/**221* Adds an entry to the list of library types. This method has no effect if222* the specified library type is already in the list of libraries.223* NOTE: The parameterless constructor of the SoundSystem class will try to224* load libraries in the order that they were entered into the list.225* @param libraryClass Derivitive of class 'Library'.226*/227public static void addLibrary( Class libraryClass )228throws SoundSystemException229{230if( libraryClass == null )231throw new SoundSystemException(232"Parameter null in method 'addLibrary'",233SoundSystemException.NULL_PARAMETER );234if( !Library.class.isAssignableFrom( libraryClass ) )235throw new SoundSystemException( "The specified class does not " +236"extend class 'Library' in method 'addLibrary'" );237238if( libraries == null )239libraries = new LinkedList<Class>();240241if( !libraries.contains( libraryClass ) )242libraries.add( libraryClass );243}244245/**246* Removes the specified library from the list of library types.247* @param libraryClass Derivitive of class 'Library'.248*/249public static void removeLibrary( Class libraryClass )250throws SoundSystemException251{252if( libraries == null || libraryClass == null )253return;254255libraries.remove( libraryClass );256}257258/**259* Returns the list of library types.260* @return LinkedList of classes derived from 'Library', or null if none were specified.261*/262public static LinkedList<Class> getLibraries()263{264return libraries;265}266267/**268* Checks if the specified library class is compatible on the user's machine.269* @param libraryClass Library type to check.270* @return True or false.271*/272public static boolean libraryCompatible( Class libraryClass )273{274if( libraryClass == null )275{276errorMessage( "Parameter 'libraryClass' null in method" +277"'librayCompatible'" );278return false;279}280if( !Library.class.isAssignableFrom( libraryClass ) )281{282errorMessage( "The specified class does not extend class " +283"'Library' in method 'libraryCompatible'" );284return false;285}286287Object o = runMethod( libraryClass, "libraryCompatible",288new Class[0], new Object[0] );289290if( o == null )291{292errorMessage( "Method 'Library.libraryCompatible' returned " +293"'null' in method 'libraryCompatible'" );294return false;295}296297return( ( (Boolean) o ).booleanValue() );298}299300/**301* Return the short title of the specified library, or null if error.302* @param libraryClass Derivitive of class 'Library'.303* @return String containing the library title.304*/305public static String getLibraryTitle( Class libraryClass )306{307if( libraryClass == null )308{309errorMessage( "Parameter 'libraryClass' null in method" +310"'getLibrayTitle'" );311return null;312}313if( !Library.class.isAssignableFrom( libraryClass ) )314{315errorMessage( "The specified class does not extend class " +316"'Library' in method 'getLibraryTitle'" );317return null;318}319320Object o = runMethod( libraryClass, "getTitle", new Class[0],321new Object[0] );322if( o == null )323{324errorMessage( "Method 'Library.getTitle' returned " +325"'null' in method 'getLibraryTitle'" );326return null;327}328329return( (String) o );330}331332/**333* Return the longer description of the specified library, or null if error.334* @param libraryClass Derivitive of class 'Library'.335* @return String containing the library title.336*/337public static String getLibraryDescription( Class libraryClass )338{339if( libraryClass == null )340{341errorMessage( "Parameter 'libraryClass' null in method" +342"'getLibrayDescription'" );343return null;344}345if( !Library.class.isAssignableFrom( libraryClass ) )346{347errorMessage( "The specified class does not extend class " +348"'Library' in method 'getLibraryDescription'" );349return null;350}351352Object o = runMethod( libraryClass, "getDescription",353new Class[0], new Object[0] );354if( o == null )355{356errorMessage( "Method 'Library.getDescription' returned " +357"'null' in method 'getLibraryDescription'" );358return null;359}360361return( (String) o );362}363364/**365* Return whether or not requires reversal of audio data byte-order.366* @param libraryClass Derivitive of class 'Library'.367* @return True if byte-order reversal is required.368*/369public static boolean reverseByteOrder( Class libraryClass )370{371if( libraryClass == null )372{373errorMessage( "Parameter 'libraryClass' null in method" +374"'reverseByteOrder'" );375return false;376}377if( !Library.class.isAssignableFrom( libraryClass ) )378{379errorMessage( "The specified class does not extend class " +380"'Library' in method 'reverseByteOrder'" );381return false;382}383384Object o = runMethod( libraryClass, "reversByteOrder",385new Class[0], new Object[0] );386if( o == null )387{388errorMessage( "Method 'Library.reverseByteOrder' returned " +389"'null' in method 'getLibraryDescription'" );390return false;391}392393return( ((Boolean) o).booleanValue() );394}395396// END LIBRARY INFORMATION397398// Use the following methods to interface the private variables above:399400// STATIC NONSYNCHRONIZED INTERFACE METHODS401/**402* Changes the message logger to use for handling status messages, warnings,403* and error messages. This method should only be called BEFORE instantiating404* the SoundSystem class! If this method is called after the SoundSystem has405* been created, there will be handles floating around to two different406* loggers, and the results will be undesirable. This method can be used to407* change how messages are handled. First, the408* {@link paulscode.sound.SoundSystemLogger SoundSystemLogger} class should be409* extended and methods overriden to change how messages are handled. Then,410* the overridden class should be instantiated, and a call made to411* SoundSystemConfig.setLogger() before creating the SoundSystem object.412* If an alternate logger is not set by the user before the SoundSystem is413* instantiated, then an instance of the base SoundSystemLogger class will be414* used by default.415* @param l Handle to a message logger.416*/417public static void setLogger( SoundSystemLogger l )418{419logger = l;420}421/**422* Returns a handle to the message logger.423* @return The current message logger.424*/425public static SoundSystemLogger getLogger()426{427return logger;428}429430// STATIC SYNCHRONIZED INTERFACE METHODS431432/**433* Sets the maximum number of normal (non-streaming) channels that can be434* created. Streaming channels are created first, so the higher the maximum435* number of streaming channels is set, the fewer non-streaming channels will436* be available. If unable to create the number of channels specified,437* SoundSystem will create as many as possible.438* NOTE: Some sound library pluggins may require the total number of channels439* (non-streaming + streaming) to be 32.440* @param number How many normal audio channels.441*/442public static synchronized void setNumberNormalChannels( int number )443{444numberNormalChannels = number;445}446447/**448* Returns the maximum number of normal (non-streaming) channels that can be449* created.450* @return Maximum non-streaming channels.451*/452public static synchronized int getNumberNormalChannels()453{454return numberNormalChannels;455}456457/**458* Sets the maximum number of streaming channels that can be created.459* Streaming channels are created first, so the higher the maximum number of460* streaming channels is set, the fewer non-streaming channels will461* be available. If unable to create the number of channels specified,462* SoundSystem will create as many as possible.463* NOTE: Some sound library pluggins may require the total number of channels464* (non-streaming + streaming) to be 32.465* @param number How many streaming audio channels.466*/467public static synchronized void setNumberStreamingChannels( int number )468{469numberStreamingChannels = number;470}471472/**473* Returns the maximum number of streaming channels that can be created.474* @return Maximum streaming channels.475*/476public static synchronized int getNumberStreamingChannels()477{478return numberStreamingChannels;479}480481/**482* Sets the varriable used for overall volume, affecting all sources.483* @param value Float value (0.0f - 1.0f).484*/485public static synchronized void setMasterGain( float value )486{487masterGain = value;488}489490/**491* Returns the value for the overall volume.492* @return A float value (0.0f - 1.0f).493*/494public static synchronized float getMasterGain()495{496return masterGain;497}498499/**500* Sets the default attenuation model to use when one is not specified.501* Attenuation is how a source's volume fades with distance.502* @param model A global attenuation model identifier.503*/504public static synchronized void setDefaultAttenuation( int model )505{506defaultAttenuationModel = model;507}508/**509* Returns the default attenuation model used when one is not specified.510* @return A global attenuation model identifier511*/512public static synchronized int getDefaultAttenuation()513{514return defaultAttenuationModel;515}516/**517* Sets the default rolloff factor to use when one is not specified.518* @param rolloff Rolloff factor.519*/520public static synchronized void setDefaultRolloff( float rolloff )521{522defaultRolloffFactor = rolloff;523}524/**525* Returns the doppler factor, for determining Doppler Effect scale.526* @return Doppler factor527*/528public static synchronized float getDopplerFactor()529{530return dopplerFactor;531}532/**533* Sets the doppler factor, for determining Doppler Effect scale. Use this534* method BEFORE instantiating the SoundSystem. To change the Doppler factor535* after the SoundSystem is instantiated, use the536* SoundSystem.changeDopplerFactor method instead.537* @param factor Doppler factor.538*/539public static synchronized void setDopplerFactor( float factor )540{541dopplerFactor = factor;542}543/**544* Returns the Doppler Velocity, for use in Doppler Effect.545* @return Doppler velocity.546*/547public static synchronized float getDopplerVelocity()548{549return dopplerVelocity;550}551/**552* Sets the Doppler velocity, for use in Doppler Effect. Use this method553* BEFORE instantiating the SoundSystem. To change the Doppler velocity after554* the SoundSystem is instantiated, use the SoundSystem.changeDopplerVelocity555* method instead.556* @param velocity Doppler velocity.557*/558public static synchronized void setDopplerVelocity( float velocity )559{560dopplerVelocity = velocity;561}562/**563* Returns the default rolloff factor used when one is not specified.564* @return Default rolloff factor565*/566public static synchronized float getDefaultRolloff()567{568return defaultRolloffFactor;569}570/**571* Sets the default fade distance to use when one is not specified.572* @param distance Fade Distance.573*/574public static synchronized void setDefaultFadeDistance( float distance )575{576defaultFadeDistance = distance;577}578/**579* Returns the default fade distance used when one is not specified.580* @return Default fade distance581*/582public static synchronized float getDefaultFadeDistance()583{584return defaultFadeDistance;585}586/**587* Sets the package where sound files are located.588* @param location Path to the sound files location (must be followed by '/').589*/590public static synchronized void setSoundFilesPackage( String location )591{592soundFilesPackage = location;593}594/**595* Returns the package where sound files are located.596* @return Path to the sound files location597*/598public static synchronized String getSoundFilesPackage()599{600return soundFilesPackage;601}602/**603* Sets the number of bytes to load at a time when streaming.604* @param size Size in bytes.605*/606public static synchronized void setStreamingBufferSize( int size )607{608streamingBufferSize = size;609}610/**611* Returns the number of bytes to load at a time when streaming.612* @return Size in bytes.613*/614public static synchronized int getStreamingBufferSize()615{616return streamingBufferSize;617}618/**619* Sets the number of buffers used for each streaming sorce.620* Slow codecs may require this number to be greater than 2 to prevent audio621* skipping during playback.622* @param num How many buffers.623*/624public static synchronized void setNumberStreamingBuffers( int num )625{626numberStreamingBuffers = num;627}628/**629* Returns the number of buffers used for each streaming sorce.630* @return How many buffers.631*/632public static synchronized int getNumberStreamingBuffers()633{634return numberStreamingBuffers;635}636637/**638* Enables a transition-speed optimization by assuming all sounds in each639* streaming source's queue will have exactly the same format once decoded640* (including channels, sample rate, and sample size). This is an advanced641* setting which should only be changed by experienced developers.642* @param val False by default.643*/644public static synchronized void setStreamQueueFormatsMatch( boolean val )645{646streamQueueFormatsMatch = val;647}648649/**650* Returns whether or not all sounds in each streaming source's queue will be651* handled as if they have exactly the same format once decoded (including652* channels, sample rate, and sample size). This is an advanced setting which653* should only be changed by experienced developers.654* @return Normally false.655*/656public static synchronized boolean getStreamQueueFormatsMatch()657{658return streamQueueFormatsMatch;659}660661/**662* Sets the maximum number of bytes to read in for (non-streaming) files.663* Increase this value if non-streaming sounds are getting cut off.664* Decrease this value if large sound files are causing lag during load time.665* @param size Size in bytes.666*/667public static synchronized void setMaxFileSize( int size )668{669maxFileSize = size;670}671/**672* Returns the maximum number of bytes to read in for (non-streaming) files.673* @return Size in bytes.674*/675public static synchronized int getMaxFileSize()676{677return maxFileSize;678}679/**680* Sets the size of each chunk to read at a time for loading (non-streaming)681* files. Increase if loading sound files is causing significant lag.682* @param size Size in bytes.683*/684public static synchronized void setFileChunkSize( int size )685{686fileChunkSize = size;687}688/**689* Returns the size of each chunk to read at a time for loading (non-streaming)690* files.691* @return Size in bytes.692*/693public static synchronized int getFileChunkSize()694{695return fileChunkSize;696}697/**698* Returns the name of the MIDI synthesizer to use instead of the default, or699* empty string if none was specified.700* @return All or part of a MIDI device name, or empty string for not specified.701*/702public static synchronized String getOverrideMIDISynthesizer()703{704return overrideMIDISynthesizer;705}706/**707* Sets the name of the MIDI synthesizer to use instead of the default. If708* 'name' is an empty string, the default Synthesizer will be used, or one of709* the common alternate synthesizers if the default Synthesizer is unavailable.710* @param name All or part of the MIDI device name.711*/712public static synchronized void setOverrideMIDISynthesizer( String name )713{714overrideMIDISynthesizer = name;715}716/**717* Uses the specified file extension to associate a particular file format718* with the codec used to read audio data from it.719* @param extension File extension to be associated with the specified codec.720* @param iCodecClass Codec type to use for files with the specified extension.721*/722public static synchronized void setCodec( String extension,723Class iCodecClass )724throws SoundSystemException725{726if( extension == null )727throw new SoundSystemException( "Parameter 'extension' null in " +728"method 'setCodec'.",729SoundSystemException.NULL_PARAMETER );730if( iCodecClass == null )731throw new SoundSystemException( "Parameter 'iCodecClass' null in " +732"method 'setCodec'.",733SoundSystemException.NULL_PARAMETER );734if( !ICodec.class.isAssignableFrom( iCodecClass ) )735throw new SoundSystemException( "The specified class does " +736"not implement interface 'ICodec' in method 'setCodec'",737SoundSystemException.CLASS_TYPE_MISMATCH );738739if( codecs == null )740codecs = new LinkedList<Codec>();741742ListIterator<Codec> i = codecs.listIterator();743Codec codec;744745while( i.hasNext() )746{747codec = i.next();748if( extension.matches( codec.extensionRegX ) )749i.remove();750}751codecs.add( new Codec( extension, iCodecClass ) );752753// Let SoundSystem know if this is a MIDI codec, so it won't use754// javax.sound.midi anymore:755if( extension.matches( EXTENSION_MIDI ) )756midiCodec = true;757}758/**759* Returns the codec that can be used to read audio data from the specified760* file.761* @param filename File to get a codec for.762* @return Codec to use for reading audio data.763*/764public static synchronized ICodec getCodec( String filename )765{766if( codecs == null )767return null;768769ListIterator<Codec> i = codecs.listIterator();770Codec codec;771772while( i.hasNext() )773{774codec = i.next();775if( filename.matches( codec.extensionRegX ) )776return codec.getInstance();777}778779return null;780}781782/**783* Indicates whether or not there is a codec for reading from MIDI files. If784* there is no codec for MIDI, then SoundSystem uses javax.sound.midi.785* @return True if there the user defined a MIDI codec.786*/787public static boolean midiCodec()788{789return midiCodec;790}791792/**793* Adds an entry to the list of stream listeners. If the instance is already794* in the list, the command is ignored.795* @param streamListener Implementation of interface 'IStreamListener'.796*/797public static void addStreamListener( IStreamListener streamListener )798{799synchronized( streamListenersLock )800{801if( streamListeners == null )802streamListeners = new LinkedList<IStreamListener>();803804if( !streamListeners.contains( streamListener ) )805streamListeners.add( streamListener );806}807}808809/**810* Removes an entry from the list of stream listeners.811* @param streamListener Implementation of interface 'IStreamListener'.812*/813public static void removeStreamListener( IStreamListener streamListener )814{815816synchronized( streamListenersLock )817{818if( streamListeners == null )819streamListeners = new LinkedList<IStreamListener>();820821if( streamListeners.contains( streamListener ) )822streamListeners.remove( streamListener );823}824}825826/**827* Notifies all stream listeners that an End Of Stream was reached. If there828* are no listeners, the command is ignored.829* @param sourcename String identifier of the source which reached the EOS.830* @param queueSize Number of items left the the stream's play queue, or zero if none.831*/832public static void notifyEOS( String sourcename, int queueSize )833{834synchronized( streamListenersLock )835{836if( streamListeners == null )837return;838}839final String srcName = sourcename;840final int qSize = queueSize;841842new Thread()843{844@Override845public void run()846{847synchronized( streamListenersLock )848{849if( streamListeners == null )850return;851ListIterator<IStreamListener> i = streamListeners.listIterator();852IStreamListener streamListener;853while( i.hasNext() )854{855streamListener = i.next();856if( streamListener == null )857i.remove();858else859streamListener.endOfStream( srcName, qSize );860}861}862}863}.start();864}865866// END STATIC SYNCHRONIZED INTERFACE METHODS867868869// PRIVATE INTERNAL METHODS870871/**872* Display the specified error message using the current logger.873* @param message Error message to display.874*/875private static void errorMessage( String message )876{877if( logger != null )878logger.errorMessage( "SoundSystemConfig", message, 0 );879}880881// We don't know what Class parameter 'c' is, so we will ignore the882// warning message "unchecked call to getMethod".883@SuppressWarnings("unchecked")884/**885* Returns the results of calling the specified method from the specified886* class using the specified parameters.887* @param c Class to call the method on.888* @param method Name of the method.889* @param paramTypes Data types of the parameters being passed to the method.890* @param params Actual parameters to pass to the method.891* @return Specified method's return value, or null if error or void.892*/893private static Object runMethod( Class c, String method, Class[] paramTypes,894Object[] params )895{896Method m = null;897try898{899m = c.getMethod( method, paramTypes ); // <--- generates a warning900}901catch( NoSuchMethodException nsme )902{903errorMessage( "NoSuchMethodException thrown when attempting " +904"to call method '" + method + "' in " +905"method 'runMethod'" );906return null;907}908catch( SecurityException se )909{910errorMessage( "Access denied when attempting to call method '" +911method + "' in method 'runMethod'" );912return null;913}914catch( NullPointerException npe )915{916errorMessage( "NullPointerException thrown when attempting " +917"to call method '" + method + "' in " +918"method 'runMethod'" );919return null;920}921if( m == null )922{923errorMessage( "Method '" + method + "' not found for the class " +924"specified in method 'runMethod'" );925return null;926}927928Object o = null;929try930{931o = m.invoke( null, params );932}933catch( IllegalAccessException iae )934{935errorMessage( "IllegalAccessException thrown when attempting " +936"to invoke method '" + method + "' in " +937"method 'runMethod'" );938return null;939}940catch( IllegalArgumentException iae )941{942errorMessage( "IllegalArgumentException thrown when attempting " +943"to invoke method '" + method + "' in " +944"method 'runMethod'" );945return null;946}947catch( InvocationTargetException ite )948{949errorMessage( "InvocationTargetException thrown while attempting " +950"to invoke method 'Library.getTitle' in " +951"method 'getLibraryTitle'" );952return null;953}954catch( NullPointerException npe )955{956errorMessage( "NullPointerException thrown when attempting " +957"to invoke method '" + method + "' in " +958"method 'runMethod'" );959return null;960}961catch( ExceptionInInitializerError eiie )962{963errorMessage( "ExceptionInInitializerError thrown when " +964"attempting to invoke method '" + method + "' in " +965"method 'runMethod'" );966return null;967}968969return( o );970}971972// END PRIVATE INTERNAL METHODS973974975// PRIVATE INTERNAL CLASSES976977/**978* The Codec class is used to associate individual file formats with the979* codecs used to load audio data from them.980*981* Author: Paul Lamb982*/983private static class Codec984{985/**986* A regular expression used to match a file's extension. This is used to987* determine the file format.988*/989public String extensionRegX;990/**991* Codec used to load audio data from this file format.992*/993public Class iCodecClass;994/**995* Constructor: Converts the specified extension string into a regular996* expression, and associates that with the specified codec.997* @param extension File extension to be associated with the specified codec.998* @param iCodec Codec to use for files with the specified extension.999*/1000public Codec( String extension, Class iCodecClass )1001{1002extensionRegX = "";1003// Make sure an extension was specified:1004if( extension != null && extension.length() > 0 )1005{1006// We are only interested in the file extension. The filename1007// can begin with whatever:1008extensionRegX = ".*";1009String c;1010for( int x = 0; x < extension.length(); x++ )1011{1012// Each character could be either upper or lower case:1013c = extension.substring( x, x + 1 );1014extensionRegX += "[" + c.toLowerCase( Locale.ENGLISH )1015+ c.toUpperCase( Locale.ENGLISH ) + "]";1016}1017// The extension will be at the end of the filename:1018extensionRegX += "$";1019}1020// remember the codec to use for this format:1021this.iCodecClass = iCodecClass;1022}10231024public ICodec getInstance()1025{1026if( iCodecClass == null )1027return null;10281029Object o = null;1030try1031{1032o = iCodecClass.newInstance();1033}1034catch( InstantiationException ie )1035{1036instantiationErrorMessage();1037return null;1038}1039catch( IllegalAccessException iae )1040{1041instantiationErrorMessage();1042return null;1043}1044catch( ExceptionInInitializerError eiie )1045{1046instantiationErrorMessage();1047return null;1048}1049catch( SecurityException se )1050{1051instantiationErrorMessage();1052return null;1053}105410551056if( o == null )1057{1058instantiationErrorMessage();1059return null;1060}10611062return (ICodec) o;1063}10641065private void instantiationErrorMessage()1066{1067errorMessage( "Unrecognized ICodec implementation in method " +1068"'getInstance'. Ensure that the implementing " +1069"class has one public, parameterless constructor." );1070}1071}1072// END PRIVATE INTERNAL CLASSES1073}107410751076