package sound.paulscode; import java.net.URL; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.Random; import java.util.Set; import javax.sound.sampled.AudioFormat; /** * The SoundSystem class is the core class for the SoundSystem library. It is * capable of interfacing with external sound library and codec library * pluggins. This core class is stripped down to give it a smaller memory * footprint and to make it more customizable. This library was created to * provide a simple, common interface to a variety of 3rd-party sound and codec * libraries, and to simplify switching between them on the fly. If no * external pluggins are loaded, this core class by itself is only capable of * playing MIDI files. Specific implementations (such as SoundSystemJPCT) will * extend this core class. They will automatically link with popular * external pluggins and provide extra methods for ease of use. * There should be only one instance of this class in any program! The * SoundSystem can be constructed by defining which sound library to use, or by * allowing SoundSystem to perform its own library compatibility checking. See * {@link sound.paulscode.SoundSystemConfig SoundSystemConfig} for information * about changing default settings and linking with external pluggins. *<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 SoundSystem { /** * Used to return a current value from one of the synchronized * boolean-interface methods. */ private static final boolean GET = false; /** * Used to set the value in one of the synchronized boolean-interface methods. */ private static final boolean SET = true; /** * Used when a parameter for one of the synchronized boolean-interface methods * is not aplicable. */ private static final boolean XXX = false; /** * Processes status messages, warnings, and error messages. */ protected SoundSystemLogger logger; /** * Handle to the active sound library. */ protected Library soundLibrary; /** * List of queued commands to perform. */ protected List<CommandObject> commandQueue; /** * Used internally by SoundSystem to keep track of play/pause/stop/rewind * commands. This prevents source management (culling and activating) from * being adversely affected by the quickPlay, quickStream, and backgroundMusic * methods. */ private List<CommandObject> sourcePlayList; /** * Processes queued commands in the background. */ protected CommandThread commandThread; /** * Generates random numbers. */ public Random randomNumberGenerator; /** * Name of this class. */ protected String className = "SoundSystem"; /** * Indicates the currently loaded sound-library, or null if none. */ private static Class currentLibrary = null; /** * Becomes true when the sound library has been initialized. */ private static boolean initialized = false; /** * Indicates the last exception that was thrown. */ private static SoundSystemException lastException = null; /** * Constructor: Create the sound system using the default library. If the * default library is not compatible, another library type will be loaded * instead, in the order of library preference. * See {@link sound.paulscode.SoundSystemConfig SoundSystemConfig} for * information about sound library types. */ public SoundSystem() { // create the message logger: logger = SoundSystemConfig.getLogger(); // if the user didn't create one, then do it now: if( logger == null ) { logger = new SoundSystemLogger(); SoundSystemConfig.setLogger( logger ); } linkDefaultLibrariesAndCodecs(); LinkedList<Class> libraries = SoundSystemConfig.getLibraries(); if( libraries != null ) { ListIterator<Class> i = libraries.listIterator(); Class c; while( i.hasNext() ) { c = i.next(); try { init( c ); return; } catch( SoundSystemException sse ) { logger.printExceptionMessage( sse, 1 ); } } } try { init( Library.class ); return; } catch( SoundSystemException sse ) { logger.printExceptionMessage( sse, 1 ); } } /** * Constructor: Create the sound system using the specified library. * @param libraryClass Library to use. * See {@link sound.paulscode.SoundSystemConfig SoundSystemConfig} for * information about chosing a sound library. */ public SoundSystem( Class libraryClass ) throws SoundSystemException { // create the message logger: logger = SoundSystemConfig.getLogger(); // if the user didn't create one, then do it now: if( logger == null ) { logger = new SoundSystemLogger(); SoundSystemConfig.setLogger( logger ); } linkDefaultLibrariesAndCodecs(); init( libraryClass ); } /** * Links with any default libraries or codecs should be made in this method. * This method is empty in the core SoundSystem class, and should be overriden * by classes which extend SoundSystem. * See {@link sound.paulscode.SoundSystemConfig SoundSystemConfig} for * information about linking with sound libraries and codecs. */ protected void linkDefaultLibrariesAndCodecs() { } /** * Loads the message logger, initializes the specified sound library, and * starts the command thread. Also instantiates the random number generator * and the command queue. * @param libraryClass Library to initialize. * See {@link sound.paulscode.SoundSystemConfig SoundSystemConfig} for * information about chosing a sound library. */ protected void init( Class libraryClass ) throws SoundSystemException { message( "", 0 ); message( "Starting up " + className + "...", 0 ); // create the random number generator: randomNumberGenerator = new Random(); // create the command queue: commandQueue = new LinkedList<CommandObject>(); // create the working source playlist: sourcePlayList = new LinkedList<CommandObject>(); // Instantiate and start the Command Processer thread: commandThread = new CommandThread( this ); // Gets a SoundSystem handle commandThread.start(); snooze( 200 ); newLibrary( libraryClass ); message( "", 0 ); } /** * Ends the command thread, shuts down the sound system, and removes references * to all instantiated objects. */ public void cleanup() { boolean killException = false; message( "", 0 ); message( className + " shutting down...", 0 ); // End the command thread: try { commandThread.kill(); // end the command processor loop. commandThread.interrupt(); // wake the thread up so it can end. } catch( Exception e ) { killException = true; } if( !killException ) { // wait up to 5 seconds for command thread to end: for( int i = 0; i < 50; i++ ) { if( !commandThread.alive() ) break; snooze( 100 ); } } // Let user know if there was a problem ending the command thread if( killException || commandThread.alive() ) { errorMessage( "Command thread did not die!", 0 ); message( "Ignoring errors... continuing clean-up.", 0 ); } initialized( SET, false ); currentLibrary( SET, null ); try { // Stop all sources and shut down the sound library: if( soundLibrary != null ) soundLibrary.cleanup(); } catch( Exception e ) { errorMessage( "Problem during Library.cleanup()!", 0 ); message( "Ignoring errors... continuing clean-up.", 0 ); } try { // remove any queued commands: if( commandQueue != null ) commandQueue.clear(); } catch( Exception e ) { errorMessage( "Unable to clear the command queue!", 0 ); message( "Ignoring errors... continuing clean-up.", 0 ); } try { // empty the source management list: if( sourcePlayList != null ) sourcePlayList.clear(); } catch( Exception e ) { errorMessage( "Unable to clear the source management list!", 0 ); message( "Ignoring errors... continuing clean-up.", 0 ); } // Remove references to all instantiated objects: randomNumberGenerator = null; soundLibrary = null; commandQueue = null; sourcePlayList = null; commandThread = null; importantMessage( "Author: Paul Lamb, www.paulscode.com", 1 ); message( "", 0 ); } /** * Wakes up the Command Thread to process commands. This method should be * used if there is a need to call methods 'ManageSources' and 'CommandQueue' * externally. In most cases, this method will not be needed, since * SoundSystem automatically wakes the Command Thread every time commands are * placed into either the ManageSources queue or CommandQueue to be processed. */ public void interruptCommandThread() { if( commandThread == null ) { errorMessage( "Command Thread null in method " + "'interruptCommandThread'", 0 ); return; } // Wake the command thread to process commands: commandThread.interrupt(); } /** * Pre-loads a sound into memory. The file may either be located within the * JAR or at an online location. If the file is online, filename must begin * with "http://", since that is how SoundSystem recognizes URL's. If the file * is located within the compiled JAR, the package in which sound files are * located may be set by calling SoundSystemConfig.setSoundFilesPackage(). * @param filename Filename of the sound file to load. */ public void loadSound( String filename ) { // Queue a command to load the sound file: CommandQueue( new CommandObject( CommandObject.LOAD_SOUND, new FilenameURL( filename ) ) ); // Wake the command thread to process commands: commandThread.interrupt(); } /** * Pre-loads a sound specified by the given URL into memory. The second * parameter 'identifier' should look like a filename, and it must have the * correct extension so SoundSystem knows what codec to use for the file * referenced by the URL instance. * @param url URL handle to the sound file to load. * @param identifier Filename/identifier of the file referenced by the URL. */ public void loadSound( URL url, String identifier ) { // Queue a command to load the sound file from a URL: CommandQueue( new CommandObject( CommandObject.LOAD_SOUND, new FilenameURL( url, identifier ) ) ); // Wake the command thread to process commands: commandThread.interrupt(); } /** * Saves raw PCM audio data in the specified audio format, under the specified * identifier. This identifier can be later used in place of 'filename' * parameters to reference the sample data. * @param data The sample data * @param format Format the sample data is stored in * @param identifier What to call the sample. */ public void loadSound( byte[] data, AudioFormat format, String identifier ) { // Queue a command to load the sound file from a URL: CommandQueue( new CommandObject( CommandObject.LOAD_DATA, identifier, new SoundBuffer( data, format ) ) ); // Wake the command thread to process commands: commandThread.interrupt(); } /** * Removes a pre-loaded sound from memory. This is a good method to use for * freeing up memory after a large sound file is no longer needed. NOTE: the * source will remain in memory after calling this method as long as the * sound is attached to an existing source. When calling this method, calls * should also be made to method removeSource( String ) for all sources which * this sound is bound to. * @param filename Filename/identifier of the sound file to unload. */ public void unloadSound( String filename ) { // Queue a command to unload the sound file: CommandQueue( new CommandObject( CommandObject.UNLOAD_SOUND, filename ) ); // Wake the command thread to process commands: commandThread.interrupt(); } /** * If the specified source is a streaming source or MIDI source, this method * queues up the next sound to play when the previous playback ends. The file * may either be located within the JAR or at an online location. If the file * is online, filename must begin with "http://", since that is how SoundSystem * recognizes URL paths. If the file is located within the compiled JAR, the * package in which sound files are located may be set by calling * SoundSystemConfig.setSoundFilesPackage(). This method has no effect on * non-streaming sources. * @param sourcename Source identifier. * @param filename Name of the sound file to play next. */ public void queueSound( String sourcename, String filename ) { // Queue a command to queue the sound: CommandQueue( new CommandObject( CommandObject.QUEUE_SOUND, sourcename, new FilenameURL( filename ) ) ); // Wake the command thread to process commands: commandThread.interrupt(); } /** * If the specified source is a streaming source or MIDI source, this method * queues up the next sound to play when the previous playback ends. The third * parameter 'identifier' should look like a filename, and it must have the * correct extension so SoundSystem knows what codec to use for the file * referenced by the URL instance. This method has no effect on non-streaming * sources. * @param sourcename Source identifier. * @param url URL handle to the sound file to load. * @param identifier Filename/identifier of the file referenced by the URL. */ public void queueSound( String sourcename, URL url, String identifier ) { // Queue a command to queue the sound: CommandQueue( new CommandObject( CommandObject.QUEUE_SOUND, sourcename, new FilenameURL( url, identifier ) ) ); // Wake the command thread to process commands: commandThread.interrupt(); } /** * Removes the first occurrence of the specified filename/identifier from the * specified source's list of sounds to play when previous playback ends. This * method has no effect on non-streaming sources. * @param sourcename Source identifier. * @param filename Filename/identifier of the sound file to play next. */ public void dequeueSound( String sourcename, String filename ) { // Queue a command to dequeue the sound: CommandQueue( new CommandObject( CommandObject.DEQUEUE_SOUND, sourcename, filename ) ); // Wake the command thread to process commands: commandThread.interrupt(); } /** * Fades out the volume of whatever the specified source is currently playing, * then begins playing the specified file at the source's previously * assigned volume level. The file may either be located within the JAR or at * an online location. If the file is online, filename must begin with * "http://", since that is how SoundSystem recognizes URL paths. If the file * is located within the compiled JAR, the package in which sound files are * located may be set by calling SoundSystemConfig.setSoundFilesPackage(). If * the filename parameter is null or empty, the specified source will simply * fade out and stop. The miliseconds parameter must be non-negative or zero. * This method will remove anything that is currently in the specified source's * list of queued sounds that would have played next when the current sound * finished playing. This method may only be used for streaming and MIDI * sources. * @param sourcename Name of the source to fade out. * @param filename Name of a sound file to play next, or null for none. * @param milis Number of miliseconds the fadeout should take. */ public void fadeOut( String sourcename, String filename, long milis ) { FilenameURL fu = null; if( filename != null ) fu = new FilenameURL( filename ); // Queue a command to fade out: CommandQueue( new CommandObject( CommandObject.FADE_OUT, sourcename, fu, milis ) ); // Wake the command thread to process commands: commandThread.interrupt(); } /** * Fades out the volume of whatever the specified source is currently playing, * then begins playing the specified file at the source's previously * assigned volume level. If the url parameter is null or empty, the * specified source will simply fade out and stop. The third * parameter 'identifier' should look like a filename, and it must have the * correct extension so SoundSystem knows what codec to use for the file * referenced by the URL instance. The miliseconds parameter must be * non-negative or zero. This method will remove anything that is currently in * the specified source's list of queued sounds that would have played next * when the current sound finished playing. This method may only be used for * streaming and MIDI sources. * @param sourcename Name of the source to fade out. * @param url URL handle to the sound file to play next, or null for none. * @param identifier Filename/identifier of the file referenced by the URL. * @param milis Number of miliseconds the fadeout should take. */ public void fadeOut( String sourcename, URL url, String identifier, long milis ) { FilenameURL fu = null; if( url != null && identifier != null ) fu = new FilenameURL( url, identifier ); // Queue a command to fade out: CommandQueue( new CommandObject( CommandObject.FADE_OUT, sourcename, fu, milis ) ); // Wake the command thread to process commands: commandThread.interrupt(); } /** * Fades out the volume of whatever the specified source is currently playing, * then fades the volume back in playing the specified filename. Final volume * after fade-in completes will be equal to the source's previously assigned * volume level. The filename parameter may not be null or empty. The file * may either be located within the JAR or at an online location. If the file * is online, filename must begin with "http://", since that is how * SoundSystem recognizes URL paths. If the file is located within the * compiled JAR, the package in which sound files are located may be set by * calling SoundSystemConfig.setSoundFilesPackage(). The miliseconds * parameters must be non-negative or zero. This method will remove anything * that is currently in the specified source's list of queued sounds that would * have played next when the current sound finished playing. This method may * only be used for streaming and MIDI sources. * @param sourcename Name of the source to fade out/in. * @param filename Name of a sound file to play next, or null for none. * @param milisOut Number of miliseconds the fadeout should take. * @param milisIn Number of miliseconds the fadein should take. */ public void fadeOutIn( String sourcename, String filename, long milisOut, long milisIn ) { // Queue a command to load the sound file: CommandQueue( new CommandObject( CommandObject.FADE_OUT_IN, sourcename, new FilenameURL( filename ), milisOut, milisIn ) ); // Wake the command thread to process commands: commandThread.interrupt(); } /** * Fades out the volume of whatever the specified source is currently playing, * then fades the volume back in playing the specified file. Final volume * after fade-in completes will be equal to the source's previously assigned * volume level. The url parameter may not be null or empty. The third * parameter 'identifier' should look like a filename, and it must have the * correct extension so SoundSystem knows what codec to use for the file * referenced by the URL instance. The miliseconds parameters must be * non-negative or zero. This method will remove anything that is currently * in the specified source's list of queued sounds that would have played next * when the current sound finished playing. This method may only be used for * streaming and MIDI sources. * @param sourcename Name of the source to fade out/in. * @param url URL handle to the sound file to play next. * @param identifier Filename/identifier of the file referenced by the URL. * @param milisOut Number of miliseconds the fadeout should take. * @param milisIn Number of miliseconds the fadein should take. */ public void fadeOutIn( String sourcename, URL url, String identifier, long milisOut, long milisIn ) { // Queue a command to load the sound file: CommandQueue( new CommandObject( CommandObject.FADE_OUT_IN, sourcename, new FilenameURL( url, identifier ), milisOut, milisIn ) ); // Wake the command thread to process commands: commandThread.interrupt(); } /** * Makes sure the current volume levels of streaming sources and MIDI are * correct. This method is designed to help reduce the "jerky" fading behavior * that happens when using some library and codec pluggins (such as * LibraryJavaSound and CodecJOrbis). This method has no effect on normal * "non-streaming" sources. It would normally be called somewhere in the main * "game loop". IMPORTANT: To optimize frame-rates, do not call this method * for every frame. It is better to just call this method at some acceptable * "granularity" (play around with different granularities to find what sounds * acceptable for a particular situation). */ public void checkFadeVolumes() { // Queue a command to load check fading source volumes: CommandQueue( new CommandObject( CommandObject.CHECK_FADE_VOLUMES ) ); // Wake the command thread to process commands: commandThread.interrupt(); } /** * Creates a new permanant, streaming, priority source with zero attenuation. * The file may either be located within the JAR or at an online location. If * the file is online, filename must begin with "http://", since that is how * SoundSystem recognizes URL paths. If the file is located within the * compiled JAR, the package in which sound files are located may be set by * calling SoundSystemConfig.setSoundFilesPackage(). * @param sourcename A unique identifier for this source. Two sources may not use the same sourcename. * @param filename Filename of the sound file to stream at this source. * @param toLoop Should this source loop, or play only once. */ public void backgroundMusic( String sourcename, String filename, boolean toLoop ) { // Queue a command to quick stream a new source: CommandQueue( new CommandObject( CommandObject.QUICK_PLAY, true, true, toLoop, sourcename, new FilenameURL( filename ), 0, 0, 0, SoundSystemConfig.ATTENUATION_NONE, 0, false ) ); CommandQueue( new CommandObject( CommandObject.PLAY, sourcename) ); commandThread.interrupt(); } /** * Creates a new permanant, streaming, priority source with zero attenuation. * The third parameter 'identifier' should look like a filename, and it must * have the correct extension so SoundSystem knows what codec to use for the * file referenced by the URL instance. * @param sourcename A unique identifier for this source. Two sources may not use the same sourcename. * @param url URL handle to the sound file to stream at this source. * @param identifier Filename/identifier of the file referenced by the URL. * @param toLoop Should this source loop, or play only once. */ public void backgroundMusic( String sourcename, URL url, String identifier, boolean toLoop ) { // Queue a command to quick stream a new source: CommandQueue( new CommandObject( CommandObject.QUICK_PLAY, true, true, toLoop, sourcename, new FilenameURL( url, identifier ), 0, 0, 0, SoundSystemConfig.ATTENUATION_NONE, 0, false ) ); CommandQueue( new CommandObject( CommandObject.PLAY, sourcename) ); commandThread.interrupt(); } /** * Creates a new non-streaming source. * See {@link sound.paulscode.SoundSystemConfig SoundSystemConfig} for more * information about Attenuation, fade distance, and rolloff factor. * @param priority Setting this to true will prevent other sounds from overriding this one. * @param sourcename A unique identifier for this source. Two sources may not use the same sourcename. * @param filename Filename/identifier of the sound file to play at this source. * @param toLoop Should this source loop, or play only once. * @param x X position for this source. * @param y Y position for this source. * @param z Z position for this source. * @param attmodel Attenuation model to use. * @param distOrRoll Either the fading distance or rolloff factor, depending on the value of "attmodel". */ public void newSource( boolean priority, String sourcename, String filename, boolean toLoop, float x, float y, float z, int attmodel, float distOrRoll ) { CommandQueue( new CommandObject( CommandObject.NEW_SOURCE, priority, false, toLoop, sourcename, new FilenameURL( filename ), x, y, z, attmodel, distOrRoll ) ); commandThread.interrupt(); } /** * Creates a new non-streaming source. * See {@link sound.paulscode.SoundSystemConfig SoundSystemConfig} for more * information about Attenuation, fade distance, and rolloff factor. * @param priority Setting this to true will prevent other sounds from overriding this one. * @param sourcename A unique identifier for this source. Two sources may not use the same sourcename. * @param url URL handle to the sound file to stream at this source. * @param identifier Filename/identifier of the file referenced by the URL. * @param toLoop Should this source loop, or play only once. * @param x X position for this source. * @param y Y position for this source. * @param z Z position for this source. * @param attmodel Attenuation model to use. * @param distOrRoll Either the fading distance or rolloff factor, depending on the value of "attmodel". */ public void newSource( boolean priority, String sourcename, URL url, String identifier, boolean toLoop, float x, float y, float z, int attmodel, float distOrRoll ) { CommandQueue( new CommandObject( CommandObject.NEW_SOURCE, priority, false, toLoop, sourcename, new FilenameURL( url, identifier ), x, y, z, attmodel, distOrRoll ) ); commandThread.interrupt(); } /** * Creates a new streaming source. The file may either be located within the * JAR or at an online location. If the file is online, filename must begin * with "http://", since that is how SoundSystem recognizes URL paths. If the * file is located within the compiled JAR, the package in which sound files * are located may be set by calling SoundSystemConfig.setSoundFilesPackage(). * @param priority Setting this to true will prevent other sounds from overriding this one. * @param sourcename A unique identifier for this source. Two sources may not use the same sourcename. * @param filename The filename of the sound file to play at this source. * @param toLoop Should this source loop, or play only once. * @param x X position for this source. * @param y Y position for this source. * @param z Z position for this source. * @param attmodel Attenuation model to use. * @param distOrRoll Either the fading distance or rolloff factor, depending on the value of "attmodel". */ public void newStreamingSource( boolean priority, String sourcename, String filename, boolean toLoop, float x, float y, float z, int attmodel, float distOrRoll ) { CommandQueue( new CommandObject( CommandObject.NEW_SOURCE, priority, true, toLoop, sourcename, new FilenameURL( filename ), x, y, z, attmodel, distOrRoll ) ); commandThread.interrupt(); } /** * Creates a new streaming source. The fourth parameter 'identifier' should * look like a filename, and it must have the correct extension so SoundSystem * knows what codec to use for the file referenced by the URL instance. * @param priority Setting this to true will prevent other sounds from overriding this one. * @param sourcename A unique identifier for this source. Two sources may not use the same sourcename. * @param url URL handle to the sound file to stream at this source. * @param identifier Filename/identifier of the file referenced by the URL. * @param toLoop Should this source loop, or play only once. * @param x X position for this source. * @param y Y position for this source. * @param z Z position for this source. * @param attmodel Attenuation model to use. * @param distOrRoll Either the fading distance or rolloff factor, depending on the value of "attmodel". */ public void newStreamingSource( boolean priority, String sourcename, URL url, String identifier, boolean toLoop, float x, float y, float z, int attmodel, float distOrRoll ) { CommandQueue( new CommandObject( CommandObject.NEW_SOURCE, priority, true, toLoop, sourcename, new FilenameURL( url, identifier ), x, y, z, attmodel, distOrRoll ) ); commandThread.interrupt(); } /** * Opens a direct line for streaming audio data. This method creates a new * streaming source to play the data at. The resulting streaming source can be * manipulated the same as any other streaming source. Raw data can be sent to * the new streaming source using the feedRawAudioData() method. * @param audioFormat Format that the data will be in. * @param priority Setting this to true will prevent other sounds from overriding this one. * @param sourcename A unique identifier for this source. Two sources may not use the same sourcename. * @param x X position for this source. * @param y Y position for this source. * @param z Z position for this source. * @param attModel Attenuation model to use. * @param distOrRoll Either the fading distance or rolloff factor, depending on the value of "attmodel". */ public void rawDataStream( AudioFormat audioFormat, boolean priority, String sourcename, float x, float y, float z, int attModel, float distOrRoll ) { CommandQueue( new CommandObject( CommandObject.RAW_DATA_STREAM, audioFormat, priority, sourcename, x, y, z, attModel, distOrRoll ) ); commandThread.interrupt(); } /** * Creates a temporary source and plays it. After the source finishes playing, * it is removed. Returns a randomly generated name for the new source. NOTE: * to make a source created by this method permanant, call the setActive() * method using the return value for sourcename. * @param priority Setting this to true will prevent other sounds from overriding this one. * @param filename Filename/identifier of the sound file to play at this source. * @param toLoop Should this source loop, or play only once. * @param x X position for this source. * @param y Y position for this source. * @param z Z position for this source. * @param attmodel Attenuation model to use. * @param distOrRoll Either the fading distance or rolloff factor, depending on the value of "attmodel". * @return The new sorce's name. */ public String quickPlay( boolean priority, String filename, boolean toLoop, float x, float y, float z, int attmodel, float distOrRoll ) { //generate a random name for this source: String sourcename = "Source_" + randomNumberGenerator.nextInt() + "_" + randomNumberGenerator.nextInt(); // Queue a command to quick play this new source: CommandQueue( new CommandObject( CommandObject.QUICK_PLAY, priority, false, toLoop, sourcename, new FilenameURL( filename ), x, y, z, attmodel, distOrRoll, true ) ); CommandQueue( new CommandObject( CommandObject.PLAY, sourcename) ); // Wake the command thread to process commands: commandThread.interrupt(); // return the new source name. return sourcename; } /** * Creates a temporary source and plays it. After the source finishes playing, * it is removed. Returns a randomly generated name for the new source. NOTE: * to make a source created by this method permanant, call the setActive() * method using the return value for sourcename. * @param priority Setting this to true will prevent other sounds from overriding this one. * @param url URL handle to the sound file to stream at this source. * @param identifier Filename/identifier of the file referenced by the URL. * @param toLoop Should this source loop, or play only once. * @param x X position for this source. * @param y Y position for this source. * @param z Z position for this source. * @param attmodel Attenuation model to use. * @param distOrRoll Either the fading distance or rolloff factor, depending on the value of "attmodel". * @return The new sorce's name. */ public String quickPlay( boolean priority, URL url, String identifier, boolean toLoop, float x, float y, float z, int attmodel, float distOrRoll ) { //generate a random name for this source: String sourcename = "Source_" + randomNumberGenerator.nextInt() + "_" + randomNumberGenerator.nextInt(); // Queue a command to quick play this new source: CommandQueue( new CommandObject( CommandObject.QUICK_PLAY, priority, false, toLoop, sourcename, new FilenameURL( url, identifier ), x, y, z, attmodel, distOrRoll, true ) ); CommandQueue( new CommandObject( CommandObject.PLAY, sourcename) ); // Wake the command thread to process commands: commandThread.interrupt(); // return the new source name. return sourcename; } /** * Creates a temporary source and streams it. After the source finishes * playing, it is removed. The file may either be located within the * JAR or at an online location. If the file is online, filename must begin * with "http://", since that is how SoundSystem recognizes URL paths. If the * file is located within the compiled JAR, the package in which sound files * are located may be set by calling SoundSystemConfig.setSoundFilesPackage(). * Returns a randomly generated name for the new source. NOTE: to make a * source created by this method permanant, call the setActive() method using * the return value for sourcename. * @param priority Setting this to true will prevent other sounds from overriding this one. * @param filename Filename of the sound file to stream at this source. * @param toLoop Should this source loop, or play only once. * @param x X position for this source. * @param y Y position for this source. * @param z Z position for this source. * @param attmodel Attenuation model to use. * @param distOrRoll Either the fading distance or rolloff factor, depending on the value of "attmodel". * @return The new sorce's name. */ public String quickStream( boolean priority, String filename, boolean toLoop, float x, float y, float z, int attmodel, float distOrRoll ) { //generate a random name for this source: String sourcename = "Source_" + randomNumberGenerator.nextInt() + "_" + randomNumberGenerator.nextInt(); // Queue a command to quick stream this new source: CommandQueue( new CommandObject( CommandObject.QUICK_PLAY, priority, true, toLoop, sourcename, new FilenameURL( filename ), x, y, z, attmodel, distOrRoll, true ) ); CommandQueue( new CommandObject( CommandObject.PLAY, sourcename) ); // Wake the command thread to process commands: commandThread.interrupt(); // return the new source name. return sourcename; } /** * Creates a temporary source and streams it. After the source finishes * playing, it is removed. The third parameter 'identifier' should * look like a filename, and it must have the correct extension so SoundSystem * knows what codec to use for the file referenced by the URL instance. * Returns a randomly generated name for the new source. NOTE: to make a * source created by this method permanant, call the setActive() method using * the return value for sourcename. * @param priority Setting this to true will prevent other sounds from overriding this one. * @param url URL handle to the sound file to stream at this source. * @param identifier Filename/identifier of the file referenced by the URL. * @param toLoop Should this source loop, or play only once. * @param x X position for this source. * @param y Y position for this source. * @param z Z position for this source. * @param attmodel Attenuation model to use. * @param distOrRoll Either the fading distance or rolloff factor, depending on the value of "attmodel". * @return The new sorce's name. */ public String quickStream( boolean priority, URL url, String identifier, boolean toLoop, float x, float y, float z, int attmodel, float distOrRoll ) { //generate a random name for this source: String sourcename = "Source_" + randomNumberGenerator.nextInt() + "_" + randomNumberGenerator.nextInt(); // Queue a command to quick stream this new source: CommandQueue( new CommandObject( CommandObject.QUICK_PLAY, priority, true, toLoop, sourcename, new FilenameURL( url, identifier ), x, y, z, attmodel, distOrRoll, true ) ); CommandQueue( new CommandObject( CommandObject.PLAY, sourcename) ); // Wake the command thread to process commands: commandThread.interrupt(); // return the new source name. return sourcename; } /** * Move a source to the specified location. * @param sourcename Identifier for the source. * @param x destination X coordinate. * @param y destination Y coordinate. * @param z destination Z coordinate. */ public void setPosition( String sourcename, float x, float y, float z ) { CommandQueue( new CommandObject( CommandObject.SET_POSITION, sourcename, x, y, z ) ); commandThread.interrupt(); } /** * Manually sets the specified source's volume. * @param sourcename Source to move. * @param value New volume, float value ( 0.0f - 1.0f ). */ public void setVolume( String sourcename, float value ) { CommandQueue( new CommandObject( CommandObject.SET_VOLUME, sourcename, value ) ); commandThread.interrupt(); } /** * Returns the current volume of the specified source, or zero if the specified * source was not found. * @param sourcename Source to read volume from. * @return Float value representing the source volume (0.0f - 1.0f). */ public float getVolume( String sourcename ) { synchronized( SoundSystemConfig.THREAD_SYNC ) { if( soundLibrary != null ) return soundLibrary.getVolume( sourcename ); else return 0.0f; } } /** * Manually sets the specified source's pitch. * @param sourcename The source's name. * @param value A float value ( 0.5f - 2.0f ). */ public void setPitch( String sourcename, float value ) { CommandQueue( new CommandObject( CommandObject.SET_PITCH, sourcename, value ) ); commandThread.interrupt(); } /** * Returns the pitch of the specified source. * @param sourcename The source's name. * @return Float value representing the source pitch (0.5f - 2.0f). */ public float getPitch( String sourcename ) { if( soundLibrary != null ) return soundLibrary.getPitch( sourcename ); else return 1.0f; } /** * Set a source's priority factor. A priority source will not be overriden when * too many sources are playing at once. * @param sourcename Identifier for the source. * @param pri Setting this to true makes this source a priority source. */ public void setPriority( String sourcename, boolean pri ) { CommandQueue( new CommandObject( CommandObject.SET_PRIORITY, sourcename, pri ) ); commandThread.interrupt(); } /** * Changes a source to looping or non-looping. * @param sourcename Identifier for the source. * @param lp This source should loop. */ public void setLooping( String sourcename, boolean lp ) { CommandQueue( new CommandObject( CommandObject.SET_LOOPING, sourcename, lp ) ); commandThread.interrupt(); } /** * Changes a source's attenuation model. * See {@link sound.paulscode.SoundSystemConfig SoundSystemConfig} for more * information about Attenuation. * @param sourcename Identifier for the source. * @param model Attenuation model to use. */ public void setAttenuation( String sourcename, int model ) { CommandQueue( new CommandObject( CommandObject.SET_ATTENUATION, sourcename, model ) ); commandThread.interrupt(); } /** * Changes a source's fade distance or rolloff factor. * See {@link sound.paulscode.SoundSystemConfig SoundSystemConfig} for more * information about fade distance and rolloff. * @param sourcename Identifier for the source. * @param dr Either the fading distance or rolloff factor, depending on the attenuation model used. */ public void setDistOrRoll( String sourcename, float dr) { CommandQueue( new CommandObject( CommandObject.SET_DIST_OR_ROLL, sourcename, dr ) ); commandThread.interrupt(); } /** * Changes the Doppler factor, for determining Doppler effect scale. * See {@link sound.paulscode.SoundSystemConfig SoundSystemConfig} for more * information about Doppler effect. * @param dopplerFactor New value for Doppler factor. */ public void changeDopplerFactor( float dopplerFactor) { CommandQueue( new CommandObject( CommandObject.CHANGE_DOPPLER_FACTOR, dopplerFactor ) ); commandThread.interrupt(); } /** * Changes the Doppler velocity, for use in Doppler effect. * See {@link sound.paulscode.SoundSystemConfig SoundSystemConfig} for more * information about Doppler effect. * @param dopplerVelocity New value for Doppler velocity. */ public void changeDopplerVelocity( float dopplerVelocity ) { CommandQueue( new CommandObject( CommandObject.CHANGE_DOPPLER_VELOCITY, dopplerVelocity ) ); commandThread.interrupt(); } /** * Sets the specified source's velocity, for use in Doppler effect. * See {@link sound.paulscode.SoundSystemConfig SoundSystemConfig} for more * information about Doppler effect. * @param sourcename The source's name. * @param x Velocity along world x-axis. * @param y Velocity along world y-axis. * @param z Velocity along world z-axis. */ public void setVelocity( String sourcename, float x, float y, float z ) { CommandQueue( new CommandObject( CommandObject.SET_VELOCITY, sourcename, x, y, z ) ); commandThread.interrupt(); } /** * Sets the listener's velocity, for use in Doppler effect. * See {@link sound.paulscode.SoundSystemConfig SoundSystemConfig} for more * information about Doppler effect. * @param x Velocity along world x-axis. * @param y Velocity along world y-axis. * @param z Velocity along world z-axis. */ public void setListenerVelocity( float x, float y, float z ) { CommandQueue( new CommandObject( CommandObject.SET_LISTENER_VELOCITY, x, y, z ) ); commandThread.interrupt(); } /** * Returns the number of miliseconds since the specified source began playing. * @return miliseconds, or -1 if not playing or unable to calculate */ public float millisecondsPlayed( String sourcename ) { synchronized( SoundSystemConfig.THREAD_SYNC ) { return soundLibrary.millisecondsPlayed( sourcename ); } } /** * Feeds raw data through the specified source. The source must be a * streaming source and it can not be already associated with a file or URL to * stream from. Only use this for streaming sources created with the * rawDataStream() method. NOTE: Be carefull how much data you send to a * source to stream. The data will be processed at playback speed, so if you * queue up 1 hour worth of data, it will take 1 hour to play (not to mention * hogging a ton of memory). To clear out all queued data from the source, use * the flush() method. Also note: if there is a break in the data stream, * you will hear clicks and studders, so ensure that the data flow is steady. * @param sourcename Name of the streaming source to play from. * @param buffer Byte buffer containing raw audio data to stream. */ public void feedRawAudioData( String sourcename, byte[] buffer ) { CommandQueue( new CommandObject( CommandObject.FEED_RAW_AUDIO_DATA, sourcename, buffer ) ); commandThread.interrupt(); } /** * Plays the specified source. * @param sourcename Identifier for the source. */ public void play( String sourcename ) { CommandQueue( new CommandObject( CommandObject.PLAY, sourcename) ); commandThread.interrupt(); } /** * Pauses the specified source. * @param sourcename Identifier for the source. */ public void pause( String sourcename ) { CommandQueue( new CommandObject( CommandObject.PAUSE, sourcename) ); commandThread.interrupt(); } /** * Stops the specified source. * @param sourcename Identifier for the source. */ public void stop( String sourcename ) { CommandQueue( new CommandObject( CommandObject.STOP, sourcename) ); commandThread.interrupt(); } /** * Rewinds the specified source. * @param sourcename Identifier for the source. */ public void rewind( String sourcename ) { CommandQueue( new CommandObject( CommandObject.REWIND, sourcename) ); commandThread.interrupt(); } /** * Flushes all previously queued audio data from a streaming source. * @param sourcename Identifier for the source. */ public void flush( String sourcename ) { CommandQueue( new CommandObject( CommandObject.FLUSH, sourcename) ); commandThread.interrupt(); } /** * Culls the specified source. A culled source can not be played until it has * been activated again. * @param sourcename Identifier for the source. */ public void cull( String sourcename ) { CommandQueue( new CommandObject( CommandObject.CULL, sourcename) ); commandThread.interrupt(); } /** * Activates the specified source after it was culled, so it can be played * again. * @param sourcename Identifier for the source. */ public void activate( String sourcename ) { CommandQueue( new CommandObject( CommandObject.ACTIVATE, sourcename) ); commandThread.interrupt(); } /** * Sets a flag for a source indicating whether it should be used or if it * should be removed after it finishes playing. One possible use for this * method is to make temporary sources that were created with quickPlay() * permanant. Another use could be to have a source automatically removed * after it finishes playing. NOTE: Setting a source to temporary does not * stop it, and setting a source to permanant does not play it. It is also * important to note that a looping temporary source will not be removed as * long as it keeps playing. * @param sourcename Identifier for the source. * @param temporary True = temporary, False = permanant. */ public void setTemporary( String sourcename, boolean temporary ) { CommandQueue( new CommandObject( CommandObject.SET_TEMPORARY, sourcename, temporary ) ); commandThread.interrupt(); } /** * Removes the specified source and clears up any memory it used. * @param sourcename Identifier for the source. */ public void removeSource( String sourcename ) { CommandQueue( new CommandObject( CommandObject.REMOVE_SOURCE, sourcename ) ); commandThread.interrupt(); } /** * Moves the listener relative to the current location. * @param x X offset. * @param y Y offset. * @param z Z offset. */ public void moveListener( float x, float y, float z ) { CommandQueue( new CommandObject( CommandObject.MOVE_LISTENER, x, y, z ) ); commandThread.interrupt(); } /** * Moves the listener to the specified location. * @param x Destination X coordinate. * @param y Destination Y coordinate. * @param z Destination Z coordinate. */ public void setListenerPosition( float x, float y, float z ) { CommandQueue( new CommandObject( CommandObject.SET_LISTENER_POSITION, x, y, z ) ); commandThread.interrupt(); } /** * Turns the listener counterclockwise by "angle" radians around the y-axis, * relative to the current angle. * @param angle radian offset. */ public void turnListener( float angle ) { CommandQueue( new CommandObject( CommandObject.TURN_LISTENER, angle ) ); commandThread.interrupt(); } /** * Sets the listener's angle in radians around the y-axis. * @param angle radians. */ public void setListenerAngle( float angle ) { CommandQueue( new CommandObject( CommandObject.SET_LISTENER_ANGLE, angle ) ); commandThread.interrupt(); } /** * Sets the listener's orientation. * @param lookX X coordinate of the (normalized) look-at vector. * @param lookY Y coordinate of the (normalized) look-at vector. * @param lookZ Z coordinate of the (normalized) look-at vector. * @param upX X coordinate of the (normalized) up-direction vector. * @param upY Y coordinate of the (normalized) up-direction vector. * @param upZ Z coordinate of the (normalized) up-direction vector. */ public void setListenerOrientation( float lookX, float lookY, float lookZ, float upX, float upY, float upZ ) { CommandQueue( new CommandObject( CommandObject.SET_LISTENER_ORIENTATION, lookX, lookY, lookZ, upX, upY, upZ ) ); commandThread.interrupt(); } /** * Sets the overall volume, affecting all sources. * @param value New volume, float value ( 0.0f - 1.0f ). */ public void setMasterVolume( float value ) { CommandQueue( new CommandObject( CommandObject.SET_MASTER_VOLUME, value ) ); commandThread.interrupt(); } /** * Returns the overall volume, affecting all sources. * @return Float value representing the master volume (0.0f - 1.0f). */ public float getMasterVolume() { return SoundSystemConfig.getMasterGain(); } /** * Method for obtaining information about the listener's position and * orientation. * @return a {@link sound.paulscode.ListenerData ListenerData} object. */ public ListenerData getListenerData() { synchronized( SoundSystemConfig.THREAD_SYNC ) { return soundLibrary.getListenerData(); } } /** * Switches to the specified library, and preserves all sources. * @param libraryClass Library to use. * @return True if switch was successful. * See {@link sound.paulscode.SoundSystemConfig SoundSystemConfig} for * information about chosing a sound library. */ public boolean switchLibrary( Class libraryClass ) throws SoundSystemException { synchronized( SoundSystemConfig.THREAD_SYNC ) { initialized( SET, false ); HashMap<String, Source> sourceMap = null; ListenerData listenerData = null; boolean wasMidiChannel = false; MidiChannel midiChannel = null; FilenameURL midiFilenameURL = null; String midiSourcename = ""; boolean midiToLoop = true; if( soundLibrary != null ) { currentLibrary( SET, null ); sourceMap = copySources( soundLibrary.getSources() ); listenerData = soundLibrary.getListenerData(); midiChannel = soundLibrary.getMidiChannel(); if( midiChannel != null ) { wasMidiChannel = true; midiToLoop = midiChannel.getLooping(); midiSourcename = midiChannel.getSourcename(); midiFilenameURL = midiChannel.getFilenameURL(); } soundLibrary.cleanup(); soundLibrary = null; } message( "", 0 ); message( "Switching to " + SoundSystemConfig.getLibraryTitle( libraryClass ), 0 ); message( "(" + SoundSystemConfig.getLibraryDescription( libraryClass ) + ")", 1 ); try { soundLibrary = (Library) libraryClass.newInstance(); } catch( InstantiationException ie ) { errorMessage( "The specified library did not load properly", 1 ); } catch( IllegalAccessException iae ) { errorMessage( "The specified library did not load properly", 1 ); } catch( ExceptionInInitializerError eiie ) { errorMessage( "The specified library did not load properly", 1 ); } catch( SecurityException se ) { errorMessage( "The specified library did not load properly", 1 ); } if( errorCheck( soundLibrary == null, "Library null after " + "initialization in method 'switchLibrary'", 1 ) ) { SoundSystemException sse = new SoundSystemException( className + " did not load properly. " + "Library was null after initialization.", SoundSystemException.LIBRARY_NULL ); lastException( SET, sse ); initialized( SET, true ); throw sse; } try { soundLibrary.init(); } catch( SoundSystemException sse ) { lastException( SET, sse ); initialized( SET, true ); throw sse; } soundLibrary.setListenerData( listenerData ); if( wasMidiChannel ) { if( midiChannel != null ) midiChannel.cleanup(); midiChannel = new MidiChannel( midiToLoop, midiSourcename, midiFilenameURL ); soundLibrary.setMidiChannel( midiChannel ); } soundLibrary.copySources( sourceMap ); message( "", 0 ); lastException( SET, null ); initialized( SET, true ); return true; } } /** * Switches to the specified library, loosing all sources. * @param libraryClass Library to use. * See {@link sound.paulscode.SoundSystemConfig SoundSystemConfig} for * information about chosing a sound library. */ public boolean newLibrary( Class libraryClass ) throws SoundSystemException { initialized( SET, false ); CommandQueue( new CommandObject( CommandObject.NEW_LIBRARY, libraryClass ) ); commandThread.interrupt(); for( int x = 0; (!initialized( GET, XXX )) && (x < 100); x++ ) { snooze( 400 ); commandThread.interrupt(); } if( !initialized( GET, XXX ) ) { SoundSystemException sse = new SoundSystemException( className + " did not load after 30 seconds.", SoundSystemException.LIBRARY_NULL ); lastException( SET, sse ); throw sse; } else { SoundSystemException sse = lastException( GET, null ); if( sse != null ) throw sse; } return true; } /** * Switches to the specified library, loosing all sources. This method is used * internally by SoundSystem for thread synchronization, and it can not be * called directly - please use the newLibrary() method instead. * @param libraryClass Library to use. * See {@link sound.paulscode.SoundSystemConfig SoundSystemConfig} for * information about chosing a sound library. */ private void CommandNewLibrary( Class libraryClass ) { initialized( SET, false ); String headerMessage = "Initializing "; if( soundLibrary != null ) { currentLibrary( SET, null ); // we are switching libraries headerMessage = "Switching to "; soundLibrary.cleanup(); soundLibrary = null; } message( headerMessage + SoundSystemConfig.getLibraryTitle( libraryClass ), 0 ); message( "(" + SoundSystemConfig.getLibraryDescription( libraryClass ) + ")", 1 ); try { soundLibrary = (Library) libraryClass.newInstance(); } catch( InstantiationException ie ) { errorMessage( "The specified library did not load properly", 1 ); } catch( IllegalAccessException iae ) { errorMessage( "The specified library did not load properly", 1 ); } catch( ExceptionInInitializerError eiie ) { errorMessage( "The specified library did not load properly", 1 ); } catch( SecurityException se ) { errorMessage( "The specified library did not load properly", 1 ); } if( errorCheck( soundLibrary == null, "Library null after " + "initialization in method 'newLibrary'", 1 ) ) { lastException( SET, new SoundSystemException( className + " did not load properly. " + "Library was null after initialization.", SoundSystemException.LIBRARY_NULL ) ); importantMessage( "Switching to silent mode", 1 ); try { soundLibrary = new Library(); } catch( SoundSystemException sse ) { lastException( SET, new SoundSystemException( "Silent mode did not load properly. " + "Library was null after initialization.", SoundSystemException.LIBRARY_NULL ) ); initialized( SET, true ); return; } } try { soundLibrary.init(); } catch( SoundSystemException sse ) { lastException( SET, sse ); initialized( SET, true ); return; } lastException( SET, null ); initialized( SET, true ); return; } /** * Calls the library's initialize() method. This method is used * internally by SoundSystem for thread synchronization, and it can not be * called directly. */ private void CommandInitialize() { try { if( errorCheck( soundLibrary == null, "Library null after " + "initialization in method 'CommandInitialize'", 1 ) ) { SoundSystemException sse = new SoundSystemException( className + " did not load properly. " + "Library was null after initialization.", SoundSystemException.LIBRARY_NULL ); lastException( SET, sse ); throw sse; } soundLibrary.init(); } catch( SoundSystemException sse ) { lastException( SET, sse ); initialized( SET, true ); } } /** * Loads sample data from a sound file or URL into memory. This method is used * internally by SoundSystem for thread synchronization, and it can not be * called directly - please use the loadSound() method instead. * @param filenameURL Filename/URL of the sound file to load. */ private void CommandLoadSound( FilenameURL filenameURL ) { if( soundLibrary != null ) soundLibrary.loadSound( filenameURL ); else errorMessage( "Variable 'soundLibrary' null in method 'CommandLoadSound'", 0 ); } /** * Saves the specified sample data, under the specified identifier. This * identifier can be later used in place of 'filename' parameters to reference * the sample data. This method is used internally by SoundSystem for thread * synchronization, and it can not be called directly - please use the * loadSound() method instead. * @param buffer the sample data and audio format to save. * @param identifier What to call the sample. */ private void CommandLoadSound( SoundBuffer buffer, String identifier ) { if( soundLibrary != null ) soundLibrary.loadSound( buffer, identifier ); else errorMessage( "Variable 'soundLibrary' null in method 'CommandLoadSound'", 0 ); } /** * Removes previously loaded sampled data from memory. This method is used * internally by SoundSystem for thread synchronization, and it can not be * called directly - please use the unloadSound() method instead. * @param filename Filename or string identifyer of sound to unload. */ private void CommandUnloadSound( String filename ) { if( soundLibrary != null ) soundLibrary.unloadSound( filename ); else errorMessage( "Variable 'soundLibrary' null in method 'CommandLoadSound'", 0 ); } /** * If the specified source is a streaming source or MIDI source, this method * queues up the next sound to play when the previous playback ends. This * method has no effect on non-streaming sources. This method is used * internally by SoundSystem for thread synchronization, and it can not be * called directly - please use the queueSound() method instead. * @param sourcename Source identifier. * @param filenameURL Filename/URL of the sound file to play next. */ private void CommandQueueSound( String sourcename, FilenameURL filenameURL ) { if( soundLibrary != null ) soundLibrary.queueSound( sourcename, filenameURL ); else errorMessage( "Variable 'soundLibrary' null in method 'CommandQueueSound'", 0 ); } /** * Removes the first occurrence of the specified filename/identifier from the * specified source's list of sounds to play when previous playback ends. This * method has no effect on non-streaming sources. This method is used * internally by SoundSystem for thread synchronization, and it can not be * called directly - please use the dequeueSound() method instead. * @param sourcename Source identifier. * @param filename Filename/identifier of the sound file to remove from the queue. */ private void CommandDequeueSound( String sourcename, String filename ) { if( soundLibrary != null ) soundLibrary.dequeueSound( sourcename, filename ); else errorMessage( "Variable 'soundLibrary' null in method 'CommandDequeueSound'", 0 ); } /** * Fades out the volume of whatever the specified source is currently playing, * then begins playing the specified file at the source's previously * assigned volume level. If the filenameURL parameter is null or empty, the * specified source will simply fade out and stop. The miliseconds parameter * must be non-negative or zero. This method will remove anything that is * currently in the specified source's list of queued sounds that would have * played next when the current sound finished playing. This method may only * be used for streaming and MIDI sources. This method is used * internally by SoundSystem for thread synchronization, and it can not be * called directly - please use the fadeOut() method instead. * @param sourcename Name of the source to fade out. * @param filenameURL Filename/URL of a sound file to play next, or null for none. * @param milis Number of miliseconds the fadeout should take. */ private void CommandFadeOut( String sourcename, FilenameURL filenameURL, long milis ) { if( soundLibrary != null ) soundLibrary.fadeOut( sourcename, filenameURL, milis ); else errorMessage( "Variable 'soundLibrary' null in method 'CommandFadeOut'", 0 ); } /** * Fades out the volume of whatever the specified source is currently playing, * then fades the volume back in playing the specified file. Final volume * after fade-in completes will be equal to the source's previously assigned * volume level. The filenameURL parameter may not be null or empty. The * miliseconds parameters must be non-negative or zero. This method will * remove anything that is currently in the specified source's list of queued * sounds that would have played next when the current sound finished playing. * This method may only be used for streaming and MIDI sources. This method is * used internally by SoundSystem for thread synchronization, and it can not be * called directly - please use the fadeOutIn() method instead. * @param sourcename Name of the source to fade out/in. * @param filenameURL Filename/URL of a sound file to play next, or null for none. * @param milisOut Number of miliseconds the fadeout should take. * @param milisIn Number of miliseconds the fadein should take. */ private void CommandFadeOutIn( String sourcename, FilenameURL filenameURL, long milisOut, long milisIn ) { if( soundLibrary != null ) soundLibrary.fadeOutIn( sourcename, filenameURL, milisOut, milisIn ); else errorMessage( "Variable 'soundLibrary' null in method 'CommandFadeOutIn'", 0 ); } /** * Makes sure the current volume levels of streaming sources and MIDI are * correct. This method is designed to help reduce the "jerky" fading behavior * that happens when using some library and codec pluggins (such as * LibraryJavaSound and CodecJOrbis). This method has no effect on normal * "non-streaming" sources. It would normally be called somewhere in the main * "game loop". IMPORTANT: To optimize frame-rates, do not call this method * for every frame. It is better to just call this method at some acceptable * "granularity" (play around with different granularities to find what sounds * acceptable for a particular situation). This method is used * internally by SoundSystem for thread synchronization, and it can not be * called directly - please use the checkFadeVolumes() method instead. */ private void CommandCheckFadeVolumes() { if( soundLibrary != null ) soundLibrary.checkFadeVolumes(); else errorMessage( "Variable 'soundLibrary' null in method " + "'CommandCheckFadeVolumes'", 0 ); } /** * Loads a sound file into memory. This method is used internally by * SoundSystem for thread synchronization, and it can not be called directly - * please use the newSource() method instead. * @param priority Setting this to true will prevent other sounds from overriding this one. * @param toStream Whether or not to stream the source. * @param toLoop Whether or not to loop the source. * @param sourcename A unique identifier for the source. * @param filenameURL Filename/URL of the sound file to play at this source. * @param x X position for this source. * @param y Y position for this source. * @param z Z position for this source. * @param attModel Attenuation model to use. * @param distORroll Either the fading distance or rolloff factor, depending on the value of "attmodel". */ private void CommandNewSource( boolean priority, boolean toStream, boolean toLoop, String sourcename, FilenameURL filenameURL, float x, float y, float z, int attModel, float distORroll ) { if( soundLibrary != null ) { if( filenameURL.getFilename().matches( SoundSystemConfig.EXTENSION_MIDI ) && !SoundSystemConfig.midiCodec() ) { soundLibrary.loadMidi( toLoop, sourcename, filenameURL ); } else { soundLibrary.newSource( priority, toStream, toLoop, sourcename, filenameURL, x, y, z, attModel, distORroll ); } } else errorMessage( "Variable 'soundLibrary' null in method 'CommandNewSource'", 0 ); } /** * Opens a direct line for streaming audio data. This method is used * internally by SoundSystem, and it can not be called directly - please use * the rawDataStream() method instead. * @param audioFormat Format that the data will be in. * @param priority Setting this to true will prevent other sounds from overriding this one. * @param sourcename A unique identifier for this source. Two sources may not use the same sourcename. * @param x X position for this source. * @param y Y position for this source. * @param z Z position for this source. * @param attModel Attenuation model to use. * @param distOrRoll Either the fading distance or rolloff factor, depending on the value of "attmodel". */ private void CommandRawDataStream( AudioFormat audioFormat, boolean priority, String sourcename, float x, float y, float z, int attModel, float distOrRoll ) { if( soundLibrary != null ) soundLibrary.rawDataStream( audioFormat, priority, sourcename, x, y, z, attModel, distOrRoll ); else errorMessage( "Variable 'soundLibrary' null in method 'CommandRawDataStream'", 0 ); } /** * Creates a temporary source and either plays or streams it. After the source * finishes playing, it is removed. This method is used internally by * SoundSystem for thread synchronization, and it can not be called directly - * please use the quickPlay() method instead. * @param priority Setting this to true will prevent other sounds from overriding this one. * @param toStream Whether or not to stream the source. * @param toLoop Whether or not to loop the source. * @param sourcename A unique identifier for the source. * @param filenameURL Filename/URL of the sound file to play at this source. * @param x X position for this source. * @param y Y position for this source. * @param z Z position for this source. * @param attModel Attenuation model to use. * @param distORroll Either the fading distance or rolloff factor, depending on the value of "attmodel". * @param temporary Whether or not the source should be removed after it finishes playing. */ private void CommandQuickPlay( boolean priority, boolean toStream, boolean toLoop, String sourcename, FilenameURL filenameURL, float x, float y, float z, int attModel, float distORroll, boolean temporary ) { if( soundLibrary != null ) { if( filenameURL.getFilename().matches( SoundSystemConfig.EXTENSION_MIDI ) && !SoundSystemConfig.midiCodec() ) { soundLibrary.loadMidi( toLoop, sourcename, filenameURL ); } else { soundLibrary.quickPlay( priority, toStream, toLoop, sourcename, filenameURL, x, y, z, attModel, distORroll, temporary ); } } else errorMessage( "Variable 'soundLibrary' null in method 'CommandQuickPlay'", 0 ); } /** * Moves a source to the specified coordinates. This method is used * internally by SoundSystem for thread synchronization, and it can not be * called directly - please use the setPosition() method instead. * @param sourcename Source to move. * @param x Destination X coordinate. * @param y Destination Y coordinate. * @param z Destination Z coordinate. */ private void CommandSetPosition( String sourcename, float x, float y, float z) { if( soundLibrary != null ) soundLibrary.setPosition( sourcename, x, y, z ); else errorMessage( "Variable 'soundLibrary' null in method 'CommandMoveSource'", 0 ); } /** * Manually sets the specified source's volume. This method is used * internally by SoundSystem for thread synchronization, and it can not be * called directly - please use the setVolume() method instead. * @param sourcename Source to change the volume of. * @param value New volume, float value ( 0.0f - 1.0f ). */ private void CommandSetVolume( String sourcename, float value ) { if( soundLibrary != null ) soundLibrary.setVolume( sourcename, value ); else errorMessage( "Variable 'soundLibrary' null in method 'CommandSetVolume'", 0 ); } /** * Manually sets the specified source's pitch. This method is used * internally by SoundSystem for thread synchronization, and it can not be * called directly - please use the setPitch() method instead. * @param sourcename Source to change the pitch of. * @param value New pitch, float value ( 0.5f - 2.0f ). */ private void CommandSetPitch( String sourcename, float value ) { if( soundLibrary != null ) soundLibrary.setPitch( sourcename, value ); else errorMessage( "Variable 'soundLibrary' null in method 'CommandSetPitch'", 0 ); } /** * Set a source's priority factor. A priority source will not be overriden when * too many sources are playing at once. This method is used * internally by SoundSystem for thread synchronization, and it can not be * called directly - please use the setPriority() method instead. * @param sourcename Identifier for the source. * @param pri Setting this to true makes this source a priority source. */ private void CommandSetPriority( String sourcename, boolean pri ) { if( soundLibrary != null ) soundLibrary.setPriority( sourcename, pri ); else errorMessage( "Variable 'soundLibrary' null in method 'CommandSetPriority'", 0 ); } /** * Changes a source to looping or non-looping. This method is used * internally by SoundSystem for thread synchronization, and it can not be * called directly - please use the setLooping() method instead. * @param sourcename Identifier for the source. * @param lp This source should loop. */ private void CommandSetLooping( String sourcename, boolean lp ) { if( soundLibrary != null ) soundLibrary.setLooping( sourcename, lp ); else errorMessage( "Variable 'soundLibrary' null in method 'CommandSetLooping'", 0 ); } /** * Changes a source's attenuation model. This method is used * internally by SoundSystem for thread synchronization, and it can not be * called directly - please use the setAttenuation() method instead. * See {@link sound.paulscode.SoundSystemConfig SoundSystemConfig} for more * information about Attenuation. * @param sourcename Identifier for the source. * @param model Attenuation model to use. */ private void CommandSetAttenuation( String sourcename, int model ) { if( soundLibrary != null ) soundLibrary.setAttenuation( sourcename, model ); else errorMessage( "Variable 'soundLibrary' null in method 'CommandSetAttenuation'", 0 ); } /** * Changes a source's fade distance or rolloff factor. * See {@link sound.paulscode.SoundSystemConfig SoundSystemConfig} for more * information about fade distance and rolloff. * @param sourcename Identifier for the source. * @param dr Either the fading distance or rolloff factor, depending on the attenuation model used. */ private void CommandSetDistOrRoll( String sourcename, float dr ) { if( soundLibrary != null ) soundLibrary.setDistOrRoll( sourcename, dr ); else errorMessage( "Variable 'soundLibrary' null in method 'CommandSetDistOrRoll'", 0 ); } /** * Changes the Doppler factor. This method is used internally by SoundSystem * for thread synchronization, and it can not be called directly - please use * the setDopplerFactor() method instead. * See {@link sound.paulscode.SoundSystemConfig SoundSystemConfig} for more * information about Doppler effect. * @param dopplerFactor New Doppler factor, for determining Doppler effect scale. */ private void CommandChangeDopplerFactor( float dopplerFactor ) { if( soundLibrary != null ) { SoundSystemConfig.setDopplerFactor( dopplerFactor ); soundLibrary.dopplerChanged(); } else errorMessage( "Variable 'soundLibrary' null in method 'CommandSetDopplerFactor'", 0 ); } /** * Changes the Doppler velocity. This method is used internally by SoundSystem * for thread synchronization, and it can not be called directly - please use * the setDopplerVelocity() method instead. * See {@link sound.paulscode.SoundSystemConfig SoundSystemConfig} for more * information about Doppler effect. * @param dopplerVelocity New Doppler velocity, for use in Doppler effect. */ private void CommandChangeDopplerVelocity( float dopplerVelocity ) { if( soundLibrary != null ) { SoundSystemConfig.setDopplerVelocity( dopplerVelocity ); soundLibrary.dopplerChanged(); } else errorMessage( "Variable 'soundLibrary' null in method 'CommandSetDopplerFactor'", 0 ); } /** * Changes a source's velocity, for use in Doppler effect. This method is used * internally by SoundSystem for thread synchronization, and it can not be * called directly - please use the setVelocity() method instead. * See {@link sound.paulscode.SoundSystemConfig SoundSystemConfig} for more * information about Doppler effect. * @param sourcename Identifier for the source. * @param x Source's velocity along the world x-axis. * @param y Source's velocity along the world y-axis. * @param z Source's velocity along the world z-axis. */ private void CommandSetVelocity( String sourcename, float x, float y, float z ) { if( soundLibrary != null ) soundLibrary.setVelocity( sourcename, x, y, z ); else errorMessage( "Variable 'soundLibrary' null in method 'CommandVelocity'", 0 ); } /** * Changes the listener's velocity, for use in Doppler effect. This method is * used internally by SoundSystem for thread synchronization, and it can not be * called directly - please use the setListenerVelocity() method instead. * See {@link sound.paulscode.SoundSystemConfig SoundSystemConfig} for more * information about Doppler effect. * @param x Velocity along the world x-axis. * @param y Velocity along the world y-axis. * @param z Velocity along the world z-axis. */ private void CommandSetListenerVelocity( float x, float y, float z ) { if( soundLibrary != null ) soundLibrary.setListenerVelocity( x, y, z ); else errorMessage( "Variable 'soundLibrary' null in method 'CommandSetListenerVelocity'", 0 ); } /** * Plays the specified source. This method is used * internally by SoundSystem for thread synchronization, and it can not be * called directly - please use the play() method instead. * @param sourcename Identifier for the source. */ private void CommandPlay( String sourcename ) { if( soundLibrary != null ) soundLibrary.play( sourcename ); else errorMessage( "Variable 'soundLibrary' null in method 'CommandPlay'", 0 ); } /** * Feeds raw data through the specified source. The source must be a * streaming source and it can not be already associated with a file or URL to * stream from. This method is used internally by SoundSystem for thread * synchronization, and it can not be called directly - please use the * feedRawAudioData() method instead. * @param sourcename Name of the streaming source to play from. * @param buffer Byte buffer containing raw audio data to stream. */ private void CommandFeedRawAudioData( String sourcename, byte[] buffer ) { if( soundLibrary != null ) soundLibrary.feedRawAudioData( sourcename, buffer ); else errorMessage( "Variable 'soundLibrary' null in method 'CommandFeedRawAudioData'", 0 ); } /** * Pauses the specified source. This method is used * internally by SoundSystem for thread synchronization, and it can not be * called directly - please use the pause() method instead. * @param sourcename Identifier for the source. */ private void CommandPause( String sourcename ) { if( soundLibrary != null ) soundLibrary.pause( sourcename ); else errorMessage( "Variable 'soundLibrary' null in method 'CommandPause'", 0 ); } /** * Stops the specified source. This method is used * internally by SoundSystem for thread synchronization, and it can not be * called directly - please use the stop() method instead. * @param sourcename Identifier for the source. */ private void CommandStop( String sourcename ) { if( soundLibrary != null ) soundLibrary.stop( sourcename ); else errorMessage( "Variable 'soundLibrary' null in method 'CommandStop'", 0 ); } /** * Rewinds the specified source. This method is used * internally by SoundSystem for thread synchronization, and it can not be * called directly - please use the rewind() method instead. * @param sourcename Identifier for the source. */ private void CommandRewind( String sourcename ) { if( soundLibrary != null ) soundLibrary.rewind( sourcename ); else errorMessage( "Variable 'soundLibrary' null in method 'CommandRewind'", 0 ); } /** * Flushes all previously queued audio data from a streaming source. This * method is used internally by SoundSystem for thread synchronization, and it * can not be called directly - please use the flush() method instead. * @param sourcename Identifier for the source. */ private void CommandFlush( String sourcename ) { if( soundLibrary != null ) soundLibrary.flush( sourcename ); else errorMessage( "Variable 'soundLibrary' null in method 'CommandFlush'", 0 ); } /** * Sets a flag for a source indicating whether it should be used or if it * should be removed after it finishes playing. One possible use for this * method is to make temporary sources that were created with quickPlay() * permanant. Another use could be to have a source automatically removed * after it finishes playing. NOTE: Setting a source to inactive does not stop * it, and setting a source to active does not play it. It is also important * to note that a looping inactive source will not be removed as long as * it keeps playing. This method is used * internally by SoundSystem for thread synchronization, and it can not be * called directly - please use the setTemporary() method instead. * @param sourcename Identifier for the source. * @param temporary True or False. */ private void CommandSetTemporary( String sourcename, boolean temporary ) { if( soundLibrary != null ) soundLibrary.setTemporary( sourcename, temporary ); else errorMessage( "Variable 'soundLibrary' null in method 'CommandSetActive'", 0 ); } /** * Removes the specified source and clears up any memory it used. This method * is used internally by SoundSystem for thread synchronization, and it can not * be called directly - please use the removeSource() method instead. * @param sourcename Identifier for the source. */ private void CommandRemoveSource( String sourcename ) { if( soundLibrary != null ) soundLibrary.removeSource( sourcename ); else errorMessage( "Variable 'soundLibrary' null in method 'CommandRemoveSource'", 0 ); } /** * Moves the listener relative to the current location. This method is used * internally by SoundSystem for thread synchronization, and it can not be * called directly - please use the moveListener() method instead. * @param x X offset. * @param y Y offset. * @param z Z offset. */ private void CommandMoveListener( float x, float y, float z ) { if( soundLibrary != null ) soundLibrary.moveListener( x, y, z ); else errorMessage( "Variable 'soundLibrary' null in method 'CommandMoveListener'", 0 ); } /** * Moves the listener to the specified location. This method is used * internally by SoundSystem for thread synchronization, and it can not be * called directly - please use the setListenerPosition() method instead. * @param x Destination X coordinate. * @param y Destination Y coordinate. * @param z Destination Z coordinate. */ private void CommandSetListenerPosition( float x, float y, float z ) { if( soundLibrary != null ) soundLibrary.setListenerPosition( x, y, z ); else errorMessage( "Variable 'soundLibrary' null in method 'CommandSetListenerPosition'", 0 ); } /** * Turns the listener counterclockwise by "angle" radians around the y-axis, * relative to the current angle. This method is used * internally by SoundSystem for thread synchronization, and it can not be * called directly - please use the turnListener() method instead. * @param angle radian offset. */ private void CommandTurnListener( float angle ) { if( soundLibrary != null ) soundLibrary.turnListener( angle ); else errorMessage( "Variable 'soundLibrary' null in method 'CommandTurnListener'", 0 ); } /** * Sets the listener's angle in radians around the y-axis. This method is used * internally by SoundSystem for thread synchronization, and it can not be * called directly - please use the setListenerAngle() method instead. * @param angle radians. */ private void CommandSetListenerAngle( float angle ) { if( soundLibrary != null ) soundLibrary.setListenerAngle( angle ); else errorMessage( "Variable 'soundLibrary' null in method 'CommandSetListenerAngle'", 0 ); } /** * Sets the listener's orientation. This method is used * internally by SoundSystem for thread synchronization, and it can not be * called directly - please use the setListenerOrientation() method instead. * @param lookX X coordinate of the (normalized) look-at vector. * @param lookY Y coordinate of the (normalized) look-at vector. * @param lookZ Z coordinate of the (normalized) look-at vector. * @param upX X coordinate of the (normalized) look-at vector. * @param upY Y coordinate of the (normalized) look-at vector. * @param upZ Z coordinate of the (normalized) look-at vector. */ private void CommandSetListenerOrientation( float lookX, float lookY, float lookZ, float upX, float upY, float upZ ) { if( soundLibrary != null ) soundLibrary.setListenerOrientation( lookX, lookY, lookZ, upX, upY, upZ ); else errorMessage( "Variable 'soundLibrary' null in method 'CommandSetListenerOrientation'", 0 ); } /** * Culls the specified source. A culled source can not be played until it has * been activated again. This method is used * internally by SoundSystem for thread synchronization, and it can not be * called directly - please use the cull() method instead. * @param sourcename Identifier for the source. */ private void CommandCull( String sourcename ) { if( soundLibrary != null ) soundLibrary.cull( sourcename ); else errorMessage( "Variable 'soundLibrary' null in method 'CommandCull'", 0 ); } /** * Activates a previously culled source, so it can be played again. This * method is used internally by SoundSystem for thread synchronization, and it * can not be called directly - please use the activate() method instead. * @param sourcename Identifier for the source. */ private void CommandActivate( String sourcename ) { if( soundLibrary != null ) soundLibrary.activate( sourcename ); else errorMessage( "Variable 'soundLibrary' null in method 'CommandActivate'", 0 ); } /** * Sets the overall volume, affecting all sources. This method is used * internally by SoundSystem for thread synchronization, and it can not be * called directly - please use the setMasterVolume() method instead. * @param value New volume, float value ( 0.0f - 1.0f ). */ private void CommandSetMasterVolume( float value ) { if( soundLibrary != null ) soundLibrary.setMasterVolume( value ); else errorMessage( "Variable 'soundLibrary' null in method 'CommandSetMasterVolume'", 0 ); } /** * This method can be overridden by extended classes to be used for source * management (culling and activating sources based on established rules). One * possible use for this method is sorting sources by distance, culling the * furthest, and activating the closest. * This method is automatically called on the CommandThread before processing * queued commands, so you do not need to call it anywhere else. Note: use * methods cull() and activate() here, NOT CommandCull() and CommandActivate() * (thread safety issue). * IMPORTANT: Always use synchronized( SoundSystemConfig.THREAD_SYNC ) when * manually manipulating sources from outside the Command Thread! */ protected void ManageSources() { // OVERRIDDEN METHODS MUST USE THIS: /******* synchronized( SoundSystemConfig.THREAD_SYNC ) { // TODO: Sort the sources, cull and activate, etc. } ********/ } /** * Queues a command. * If newCommand is null, all commands are dequeued and executed. * This is automatically used by the sound system, so it is not * likely that a user would ever need to use this method. * See {@link sound.paulscode.CommandObject CommandObject} for more information * about commands. * @param newCommand Command to queue, or null to execute commands. * @return True if more commands exist, false if queue is empty. */ public boolean CommandQueue( CommandObject newCommand ) { synchronized( SoundSystemConfig.THREAD_SYNC ) { if( newCommand == null ) { // New command is null - that means execute all queued commands. boolean activations = false; CommandObject commandObject; // Loop through the command queue: while( commandQueue != null && commandQueue.size() > 0 ) { // Grab the oldest command in the queue: commandObject = commandQueue.remove( 0 ); // See what it is, and execute the proper Command method: if( commandObject != null ) { switch( commandObject.Command ) { case CommandObject.INITIALIZE: CommandInitialize(); break; case CommandObject.LOAD_SOUND: CommandLoadSound( (FilenameURL) commandObject.objectArgs[0] ); break; case CommandObject.LOAD_DATA: CommandLoadSound( (SoundBuffer) commandObject.objectArgs[0], commandObject.stringArgs[0] ); break; case CommandObject.UNLOAD_SOUND: CommandUnloadSound( commandObject.stringArgs[0] ); break; case CommandObject.QUEUE_SOUND: CommandQueueSound( commandObject.stringArgs[0], (FilenameURL) commandObject.objectArgs[0] ); break; case CommandObject.DEQUEUE_SOUND: CommandDequeueSound( commandObject.stringArgs[0], commandObject.stringArgs[1] ); break; case CommandObject.FADE_OUT: CommandFadeOut( commandObject.stringArgs[0], (FilenameURL) commandObject.objectArgs[0], commandObject.longArgs[0] ); break; case CommandObject.FADE_OUT_IN: CommandFadeOutIn( commandObject.stringArgs[0], (FilenameURL) commandObject.objectArgs[0], commandObject.longArgs[0], commandObject.longArgs[1] ); break; case CommandObject.CHECK_FADE_VOLUMES: CommandCheckFadeVolumes(); break; case CommandObject.NEW_SOURCE: CommandNewSource( commandObject.boolArgs[0], commandObject.boolArgs[1], commandObject.boolArgs[2], commandObject.stringArgs[0], (FilenameURL) commandObject.objectArgs[0], commandObject.floatArgs[0], commandObject.floatArgs[1], commandObject.floatArgs[2], commandObject.intArgs[0], commandObject.floatArgs[3] ); break; case CommandObject.RAW_DATA_STREAM: CommandRawDataStream( (AudioFormat) commandObject.objectArgs[0], commandObject.boolArgs[0], commandObject.stringArgs[0], commandObject.floatArgs[0], commandObject.floatArgs[1], commandObject.floatArgs[2], commandObject.intArgs[0], commandObject.floatArgs[3] ); break; case CommandObject.QUICK_PLAY: CommandQuickPlay( commandObject.boolArgs[0], commandObject.boolArgs[1], commandObject.boolArgs[2], commandObject.stringArgs[0], (FilenameURL) commandObject.objectArgs[0], commandObject.floatArgs[0], commandObject.floatArgs[1], commandObject.floatArgs[2], commandObject.intArgs[0], commandObject.floatArgs[3], commandObject.boolArgs[3] ); break; case CommandObject.SET_POSITION: CommandSetPosition( commandObject.stringArgs[0], commandObject.floatArgs[0], commandObject.floatArgs[1], commandObject.floatArgs[2] ); break; case CommandObject.SET_VOLUME: CommandSetVolume( commandObject.stringArgs[0], commandObject.floatArgs[0] ); break; case CommandObject.SET_PITCH: CommandSetPitch( commandObject.stringArgs[0], commandObject.floatArgs[0] ); break; case CommandObject.SET_PRIORITY: CommandSetPriority( commandObject.stringArgs[0], commandObject.boolArgs[0] ); break; case CommandObject.SET_LOOPING: CommandSetLooping( commandObject.stringArgs[0], commandObject.boolArgs[0] ); break; case CommandObject.SET_ATTENUATION: CommandSetAttenuation( commandObject.stringArgs[0], commandObject.intArgs[0] ); break; case CommandObject.SET_DIST_OR_ROLL: CommandSetDistOrRoll( commandObject.stringArgs[0], commandObject.floatArgs[0] ); break; case CommandObject.CHANGE_DOPPLER_FACTOR: CommandChangeDopplerFactor( commandObject.floatArgs[0] ); break; case CommandObject.CHANGE_DOPPLER_VELOCITY: CommandChangeDopplerVelocity( commandObject.floatArgs[0] ); break; case CommandObject.SET_VELOCITY: CommandSetVelocity( commandObject.stringArgs[0], commandObject.floatArgs[0], commandObject.floatArgs[1], commandObject.floatArgs[2] ); break; case CommandObject.SET_LISTENER_VELOCITY: CommandSetListenerVelocity( commandObject.floatArgs[0], commandObject.floatArgs[1], commandObject.floatArgs[2] ); break; // Methods related to playing sources must be processed // after cull/activate commands in order for source // management to work properly, so save them for // later: //------------------------------------------------------ case CommandObject.PLAY: sourcePlayList.add( commandObject ); break; case CommandObject.FEED_RAW_AUDIO_DATA: sourcePlayList.add( commandObject ); break; //------------------------------------------------------ case CommandObject.PAUSE: CommandPause( commandObject.stringArgs[0] ); break; case CommandObject.STOP: CommandStop( commandObject.stringArgs[0] ); break; case CommandObject.REWIND: CommandRewind( commandObject.stringArgs[0] ); break; case CommandObject.FLUSH: CommandFlush( commandObject.stringArgs[0] ); break; case CommandObject.CULL: CommandCull( commandObject.stringArgs[0] ); break; case CommandObject.ACTIVATE: activations = true; CommandActivate( commandObject.stringArgs[0] ); break; case CommandObject.SET_TEMPORARY: CommandSetTemporary( commandObject.stringArgs[0], commandObject.boolArgs[0] ); break; case CommandObject.REMOVE_SOURCE: CommandRemoveSource( commandObject.stringArgs[0] ); break; case CommandObject.MOVE_LISTENER: CommandMoveListener( commandObject.floatArgs[0], commandObject.floatArgs[1], commandObject.floatArgs[2]); break; case CommandObject.SET_LISTENER_POSITION: CommandSetListenerPosition( commandObject.floatArgs[0], commandObject.floatArgs[1], commandObject.floatArgs[2]); break; case CommandObject.TURN_LISTENER: CommandTurnListener( commandObject.floatArgs[0] ); break; case CommandObject.SET_LISTENER_ANGLE: CommandSetListenerAngle( commandObject.floatArgs[0]); break; case CommandObject.SET_LISTENER_ORIENTATION: CommandSetListenerOrientation( commandObject.floatArgs[0], commandObject.floatArgs[1], commandObject.floatArgs[2], commandObject.floatArgs[3], commandObject.floatArgs[4], commandObject.floatArgs[5]); break; case CommandObject.SET_MASTER_VOLUME: CommandSetMasterVolume( commandObject.floatArgs[0] ); break; case CommandObject.NEW_LIBRARY: CommandNewLibrary( commandObject.classArgs[0] ); break; // If we don't recognize the command, just skip it: default: break; } } } // If any sources were reactivated, check if they need to be // replayed: if( activations ) soundLibrary.replaySources(); // Now that we have the correct sources culled and activated, we // can start playing sources. Loop through the playlist and // execute the commands: while( sourcePlayList != null && sourcePlayList.size() > 0 ) { // Grab the oldest command in the queue: commandObject = sourcePlayList.remove( 0 ); if( commandObject != null ) { // See what it is, and execute the proper Command method: switch( commandObject.Command ) { case CommandObject.PLAY: CommandPlay( commandObject.stringArgs[0] ); break; case CommandObject.FEED_RAW_AUDIO_DATA: CommandFeedRawAudioData( commandObject.stringArgs[0], commandObject.buffer ); break; } } } return( commandQueue != null && commandQueue.size() > 0 ); } else { // make sure the commandQueue exists: if( commandQueue == null ) return false; // queue a new command commandQueue.add( newCommand ); // Of course there is something in the list now, since we just // added it: return true; } } } /** * Searches for and removes any temporary sources that have finished * playing. This method is used internally by SoundSystem, and it is * unlikely that the user will ever need to use it. */ public void removeTemporarySources() { synchronized( SoundSystemConfig.THREAD_SYNC ) { if( soundLibrary != null ) soundLibrary.removeTemporarySources(); } } /** * Returns true if the specified source is playing. * @param sourcename Unique identifier of the source to check. * @return True or false. */ public boolean playing( String sourcename ) { synchronized( SoundSystemConfig.THREAD_SYNC ) { if( soundLibrary == null ) return false; Source src = soundLibrary.getSources().get( sourcename ); if( src == null ) return false; return src.playing(); } } /** * Returns true if anything is currently playing. * @return True or false. */ public boolean playing() { synchronized( SoundSystemConfig.THREAD_SYNC ) { if( soundLibrary == null ) return false; HashMap<String, Source> sourceMap = soundLibrary.getSources(); if( sourceMap == null ) return false; Set<String> keys = sourceMap.keySet(); Iterator<String> iter = keys.iterator(); String sourcename; Source source; while( iter.hasNext() ) { sourcename = iter.next(); source = sourceMap.get( sourcename ); if( source != null ) if( source.playing() ) return true; } return false; } } /** * Copies and returns the peripheral information from a map of sources. This * method is used internally by SoundSystem, and it is unlikely that the user * will ever need to use it. * @param sourceMap Sources to copy. * @return New map of sources. */ private HashMap<String, Source> copySources( HashMap<String, Source> sourceMap ) { Set<String> keys = sourceMap.keySet(); Iterator<String> iter = keys.iterator(); String sourcename; Source source; // New map of generic source data: HashMap<String, Source> returnMap = new HashMap<String, Source>(); // loop through and store information from all the sources: while( iter.hasNext() ) { sourcename = iter.next(); source = sourceMap.get( sourcename ); if( source != null ) returnMap.put( sourcename, new Source( source, null ) ); } return returnMap; } /** * Checks if the specified library type is compatible. * @param libraryClass Libary type to check. * @return True or false. */ public static boolean libraryCompatible( Class libraryClass ) { // create the message logger: SoundSystemLogger logger = SoundSystemConfig.getLogger(); // if the user didn't create one, then do it now: if( logger == null ) { logger = new SoundSystemLogger(); SoundSystemConfig.setLogger( logger ); } logger.message( "", 0 ); logger.message( "Checking if " + SoundSystemConfig.getLibraryTitle( libraryClass ) + " is compatible...", 0 ); boolean comp = SoundSystemConfig.libraryCompatible( libraryClass ); if( comp ) logger.message( "...yes", 1 ); else logger.message( "...no", 1 ); return comp; } /** * Returns the currently loaded library, or -1 if none. * @return Global library identifier */ public static Class currentLibrary() { return( currentLibrary( GET, null ) ); } /** * Returns false if a sound library is busy initializing. * @return True or false. */ public static boolean initialized() { return( initialized( GET, XXX ) ); } /** * Returns the last SoundSystemException thrown, or null if none. * @return The last exception. */ public static SoundSystemException getLastException() { return( lastException( GET, null ) ); } /** * Stores a SoundSystemException which can be retreived later with the * 'getLastException' method. * @param e Exception to store. */ public static void setException( SoundSystemException e ) { lastException( SET, e ); } /** * Sets or returns the value of boolean 'initialized'. * @param action Action to perform (GET or SET). * @param value New value if action is SET, otherwise XXX. * @return value of boolean 'initialized'. */ private static boolean initialized( boolean action, boolean value ) { synchronized( SoundSystemConfig.THREAD_SYNC ) { if( action == SET ) initialized = value; return initialized; } } /** * Sets or returns the value of boolean 'initialized'. * @param action Action to perform (GET or SET). * @param value New value if action is SET, otherwise XXX. * @return value of boolean 'initialized'. */ private static Class currentLibrary( boolean action, Class value ) { synchronized( SoundSystemConfig.THREAD_SYNC ) { if( action == SET ) currentLibrary = value; return currentLibrary; } } /** * Sets or returns the error code for the last error that occurred. If no * errors have occurred, returns SoundSystem.ERROR_NONE * @param action Action to perform (GET or SET). * @param e New exception if action is SET, otherwise XXX. * @return Last SoundSystemException thrown. */ private static SoundSystemException lastException( boolean action, SoundSystemException e ) { synchronized( SoundSystemConfig.THREAD_SYNC ) { if( action == SET ) lastException = e; return lastException; } } /** * Sleeps for the specified number of milliseconds. */ protected static void snooze( long milliseconds ) { try { Thread.sleep( milliseconds ); } catch( InterruptedException e ){} } /** * Prints a message. * @param message Message to print. * @param indent Number of tabs to indent the message. */ protected void message( String message, int indent ) { logger.message( message, indent ); } /** * Prints an important message. * @param message Message to print. * @param indent Number of tabs to indent the message. */ protected void importantMessage( String message, int indent ) { logger.importantMessage( message, indent ); } /** * Prints the specified message if error is true. * @param error True or False. * @param message Message to print if error is true. * @param indent Number of tabs to indent the message. * @return True if error is true. */ protected boolean errorCheck( boolean error, String message, int indent ) { return logger.errorCheck( error, className, message, indent ); } /** * Prints an error message. * @param message Message to print. * @param indent Number of tabs to indent the message. */ protected void errorMessage( String message, int indent ) { logger.errorMessage( className, message, indent ); } }