package paulscode.sound;
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 paulscode.sound.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 paulscode.sound.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 paulscode.sound.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 paulscode.sound.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 paulscode.sound.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 paulscode.sound.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 paulscode.sound.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 paulscode.sound.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 paulscode.sound.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 paulscode.sound.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 paulscode.sound.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 paulscode.sound.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 paulscode.sound.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 paulscode.sound.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 paulscode.sound.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 paulscode.sound.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 paulscode.sound.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 paulscode.sound.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 paulscode.sound.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 paulscode.sound.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 paulscode.sound.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 paulscode.sound.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 paulscode.sound.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 paulscode.sound.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 );
}
}